summaryrefslogtreecommitdiff
path: root/jnyqide/InstrumentCharacteristics.java
blob: 91fac840c625512a719ee044479ee6f3deeca485 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
package jnyqide;

import java.util.ArrayList;
import java.io.BufferedReader;

/***
** Class: InstrumentCharacteristics
** Author: Priyanka Raghavan and Roger B. Dannenberg
** Description: The instrument characteristics class reads from 
** the instruments.txt file and stores the
** instrument characteristics such as the subcategory,name,library.
** The characteristics include the implementation and parameter
** description. The different parameters are added as an arraylist.
*
* Syntax for the instruments.txt file:
*  (see instruments.txt for examples, it should be clear)
*  Each sound is described by a function declaration preceded by
*  a category like this:
*    category:subcategory[function](parameters)
*  where category and subcategory determine what appears in the 
*  pull-down lists in the browser, and function is the lisp
*  function to call (if omitted, then subcategory is also the
*  function name). parameters is a comma-separated list of 
*  parameter declarations. Each parameter is of the form
*    type name = default (low:high)
*  where type is "int" or "float", name is the parameter name
*  (if the parameter is a keyword parameter, it is prefixed with
*  a colon), and low:high gives the range for a slider.
* After the function declaration, there can be specifications for
* the implementation of the function. There should be either
* a pair of implementations for LISP and SAL, or a single
* REQUIRE. The LISP and SAL implementation is specified as follows:
*  LISP-SOURCE
*    <any number of lines of LISP source code>
*  SAL-SOURCE
*    <any number of lines of SAL source code>
* and the REQUIRE is specified as follows:
*  REQUIRE "path to implementation file to be loaded"
* After the implementation specifications (if any), the 
* sound description is terminated by the following line:
*  END-SOUND
* There may be any number of these sound specifications (instruments)
* in the file.
* When a user selects an instrument in the browser, the appropriate
* implementation is constructed (using SAL-SOURCE or LISP-SOURCE if
* present, otherwise using REQUIRE to load a file) and the function
* is called with parameters chosen by sliders.
*
**/

public class InstrumentCharacteristics {
    
    private String categoryName;
    private String subcategoryName;
    private String functionName;
    private ArrayList parameterList;
    private String lispImplementation;
    private String salImplementation;
    private String require;

    private static char buffer;
    private static boolean bufferp;
    private static String line;

    InstrumentCharacteristics() {
        bufferp = false;
	parameterList = new ArrayList();
    }

    public String getCategoryName() { return categoryName; }

    public void setCategoryName(String name) { categoryName = name; }

    public void setSubcategoryName(String subname) {
	subcategoryName = subname;
    }

    public String getSubcategoryName() { return subcategoryName; }

    public String getFunctionName() { return functionName; }


    public void addParameter(String name, String minValue, String maxValue, 
                             String defaultValue, String type) {
	Parameter parameter = new Parameter(name, minValue, maxValue, 
					    defaultValue, type);
	parameterList.add(parameter);
    }
    
    public ArrayList getParameters(){ return parameterList; }

    public String getLispImplementation() { return lispImplementation; }

    public String getSalImplementation() { return salImplementation; }

    public String getRequire() { return require; }

    public String readImplementation(BufferedReader br) throws Exception {
        String implementation = "";
        while ((line = br.readLine()) != null && 
               line.indexOf("REQUIRE") != 0 &&
               line.indexOf("LISP-SOURCE") != 0 &&
               line.indexOf("SAL-SOURCE") != 0 &&
               line.indexOf("END-SOUND") != 0) {
            implementation = implementation + line + "\n";
        }
        return implementation;
    }

    public boolean readData(BufferedReader br) {
	categoryName = getToken(br); // category
	if (categoryName == null) return false;
	if (getNonSpace(br) != ':') {
	    System.out.println("expected : after " + categoryName);
	    return false;
	}
	subcategoryName = getToken(br);
	int c = getNonSpace(br);
	functionName = subcategoryName;
	if (c == '[') {
	    functionName = getToken(br);
	    if (getNonSpace(br) != ']') {
		System.out.println("expected ] after " + functionName);
		return false;
	    }
	} else ungetChar(c);
	if (getNonSpace(br) != '(') {
	    System.out.println("no ( after " + functionName);
	    return false;
	}
	while ((c = getNonSpace(br)) != ')') {
	    ungetChar(c);
	    Parameter p = readParameter(br);
	    if (p == null) {
		System.out.println("syntax error for parameter in " + 
				   subcategoryName);
		return false;
	    }
	    parameterList.add(p);
	}
        // get a file to load or an implementation to execute
        require = null;
        lispImplementation = null;
        salImplementation = null;
        try {
            // read eol after function spec
            line = br.readLine();
            line = ""; // force a readline on first entry to loop
            while (true) {
                while (line != null && line.length() < 3) {
                    // skip blank lines -- we're not checking too carefully
                    // but a char count of 3 allows only CRLF and maybe a
                    // space or tab
                    line = br.readLine();
                }
                if (line == null) {
                    System.out.println(
                        "expected LISP-SOURCE or SAL-SOURCE, REQUIRE or " +
                        "END-SOUND, not " + line);
                    return false;
                }

                int reqPos = line.indexOf("REQUIRE");
                if (reqPos >= 0) {
                    reqPos += 8; // length of "REQUIRE "
                    require = line.substring(reqPos, line.length());
                    line = br.readLine();
                } else if (line.indexOf("LISP-SOURCE") == 0) {
                    // read until LISP-END
                    lispImplementation = readImplementation(br);
                } else if (line.indexOf("SAL-SOURCE") == 0) {
                    // read until SAL-END
                    salImplementation = readImplementation(br);
                } else if (line.indexOf("END-SOUND") == 0) {
                    return true;
                } else {
                    System.out.println(
                        "expected REQUIRE, LISP-SOURCE, SAL-SOURCE, or " +
                        "END-SOUND, not " + line);
                    return false;
                }
            }
	} catch (Exception e) {
	    return false;
	}	    
    }

    private Parameter readParameter(BufferedReader br) {
	Parameter p = new Parameter();
	String tok = getToken(br);
	if (tok == null) {
	    System.out.println("expected parameter type: " + tok);
	    return null;
	}
	p.setType(tok);
	int param1 = getNonSpace(br);
	if (param1 != ':') {
	    ungetChar(param1);
	}
	tok = getToken(br);
	if (tok == null) {
	    System.out.println("expected parameter name: " + tok);
	    return null;
	}
	if (param1 == ':') tok = ":" + tok;
	p.setName(tok);

	if (getNonSpace(br) != '=') {
	    System.out.println("expected = after parameter: " + tok);
	    return null;
	}

	tok = getToken(br);
	if (tok == null) {
	    System.out.println("expected default value: " + tok);
	    return null;
	}
	p.setDefaultValue(tok);

	if (getNonSpace(br) != '(') {
	    System.out.println("expected ( after default value: " + tok);
	    return null;
	}
	
	tok = getToken(br);
	if (tok == null) {
	    System.out.println("expected min value: " + tok);
	    return null;
	}
	p.setMinValue(tok);

	if (getNonSpace(br) != ':') {
	    System.out.println("expected : after min value: " + tok);
	    return null;
	}
	
	tok = getToken(br);
	if (tok == null) {
	    System.out.println("expected max value: " + tok);
	    return null;
	}
	p.setMaxValue(tok);

	if (getNonSpace(br) != ')') {
	    System.out.println("expected ) after max value: " + tok);
	    return null;
	}
	
	int c = getNonSpace(br);
	if (c != ',') ungetChar(c);

	return p;
    }

    private int getNonSpace(BufferedReader br) {
        int c;
        while ((c = getChar(br)) != -1 && Character.isWhitespace(c));
	return c;
    }

    private int getChar(BufferedReader br) {
        if (bufferp) {
	    bufferp = false;
	    return buffer;
	}
	try {
	    return br.read();
	} catch (Exception e) {
	    return -1;
	}
    }

    private void ungetChar(int c) {
	if (c == -1) return; // ignore EOF
	buffer = (char) c;
	bufferp = true;
    }

    private String getToken(BufferedReader br) {
	int c = getNonSpace(br);
	StringBuffer token = new StringBuffer();
        while (c != -1 && (Character.isLetterOrDigit(c) || 
			   c == '-' || c == '.')) {
	    token.append((char) c);
	    c = getChar(br);
	}
	ungetChar(c);
	String s = new String(token);
	if (s.length() == 0) return null;
	// System.out.println("gettoken: " + token);
        return s;
    }
}


/**
 ** Class: Parameter
 ** Author: Priyanka Raghavan
 ** Description: This class is used to store parameter values like 
 **    name, minvalue, maxvalue, default value, and type (integer,string, etc.)
 **
 **/

class Parameter{
    String name;
    String minValue;
    String maxValue;
    String defaultValue;
    String type;
    float value;
    
    Parameter() { }

    Parameter(String name, String minValue, String maxValue,
	      String defaultValue, String type) {
	this.name = name;
	this.minValue = minValue;
	this.maxValue = maxValue;
	this.defaultValue = defaultValue;
	this.type=type;
	value = 0.0f;
    }

    public void setName(String name) {
	this.name=name;
    }

    public void setMinValue(String value) {
	minValue = value;
    }

    public void setMaxValue(String value) {
	maxValue = value;
    }

    public void setDefaultValue(String defaultvalue) {
	defaultValue = defaultvalue;
    }

    public String getName() {
	return name;
    }

    public String getMinValue() {
	return minValue;
    }

    public String getMaxValue() {
	return maxValue;
    }
    
    public String getType() { return type; }
    
    public void setType(String type) { this.type = type; }
    
    public String getDefaultValue() { return defaultValue; }

    public float getValue() { return value; }

    public void setValue(float f) { value = f; }

}