Java Synthesizer, Part 12 – GUI, Part 2


When I first started writing part 1 for the GUI interface section, I was focusing on user input from the laptop screen and keyboard. The number of sliders and text fields on the screen limited the number of sound settings available to the user on any given display screen. For the most part this was ok, because none of the “circuits” have more than 6 settable parameters. The entry screen (screen “0”) is limited to 12 parameters because that’s how many sliders Netbeans allows me given its flaky “what you see is not what you get” canvas. Anyway, with a simple synth circuit configuration, 12 is enough.

The result of the above was that I could afford to cast each individual circuit within the display settings and set parameter methods, because the location and type of each circuit parameter could be obtained from the “o” object via for-loops.

But when I began implementing the A-300 Pro keyboard support, things got unwieldy fast. The A-300 has 9 sliders, 9 dials, 8 drum pads and 4 user buttons. This is well over the 12 controls accessible from display screen “0”, and I want to use the drum pads as gate triggers, for toggling the filter, echo and oscillator gates on and off. There’s no reason to not extend the number of controllable parameters through the top 6 dials to be independent of whatever screen I’m on, and the drum pads also need to be independent. I can hardcode the pads to specific circuit gate “pins”, but that makes it harder for me to create a new synth configuration later. What I needed was to extend the “o” object to abstract the setStr() and strVal() methods to make “o” truly class blind.

The first step was to correct my class name. I changed “o” to “circuit”.

The second step was to create an abstract class that could extend the individual emulation classes. In keeping with the hardware synth naming conventions I’ve been using so far, I called the new class “module”.

abstract class module {
abstract void setStr(int idx, String str);
abstract String strVal(int idx);
abstract void toggle();
}

As seems to be the case with everything I try to do in Java, none of the examples on the Oracle site work out of the box within Netbeans. There’s error checking in Netbeans that JavaC doesn’t do, apparently. One such example is that Netbeans forced me to include the parameters for each abstracted method. Took me a while to figure out why the Oracle examples were generating errors.

After this, I just needed to add the “extends module” line in each hardware emulation class. I also took the opportunity to add the new toggle() method. For modules that use gate (ADSR, osc, oneShot, noise) I toggle the gate parameter. For echo, it’s echoOn, and for the VCF its filterOn. A couple classes, like the VCA, don’t have anything to toggle, so I just gave them dummy methods.

class osc extends module {

void toggle() {
gate = (! gate);
}

}

Which brings me to the code for handling data coming from the A-300 Pro. I want to get that data in a couple different ways. For Note_On and Note_Off I just store the new value in the associated variable in the a300 object, and reset it to -1 when I’ve read it. The pitch wheel is normally non-zero, so I read the a300.pitchWheel variable all the time rather than trying to figure out the right if-statement for it. Then, for the sliders, dials and buttons, I use a set of 30-element arrays. The first one, controlChanged[] is true if the A300 class parsed something from a specific control number. The MIDI status number is in controlValue1[] and the MIDI value is in controlValue2[]. Error and status messages, and MIDI strings from unparsed MIDI controls come back in .msgBack. Note_On, Note_Off and pitchWheel go into the keyBoard object; everything else gets processed as if it came from a jSlider or jTextField.

The drum pads are presently assigned as follows. Eventually, I’ll softcode them further to allow for user assignments.
18 – The A-300 B1 button, same as PLAY on the GUI.
22 – Drum pad A1, same as Previous
23 – A2, same as Next
24 – A3, toggle the VCF module filterOn status
25 – A4, toggle the echo module echoOn status

private void updateFromKeyboard(){ // Move user-entered values into program.
// jTextArea1.append(“In updateFromKeyboard\n”);

for(int i = 0; i<a300.control.length; i++) {
if(a300.controlChanged[i]) {
if(i < ((objectNamePair) circuits.get(displayCircuitValuesPtr)).ranges.size()) {
jSList.get(i+1).setValue((int) map(a300.control[i], 0, 127, 0, ((objectNamePair) circuits.get(displayCircuitValuesPtr)).ranges.get(i).stepMax));
processScreenInputs(i+2, a300.controlValue1[i], 0, false);
} else if(i >= 11 && i <= 15) {
processScreenInputs(i+2, a300.controlValue2[i], 0, false);
} else if(i == 18) { // Run Play button
jButton1.doClick();
} else if(i == 22) { // Run Previous button
jButton2.doClick();
} else if(i == 23) { // Run Next button
jButton3.doClick();
} else if (i == 24) { // Try to toggle Filter gate
((module) circuits.get(8).obj).toggle();
} else if (i == 25) { // Try to toggle echo gate
((module) circuits.get(12).obj).toggle();
} else {
jTextArea1.append(“i: ” + i + ” ” + a300.msgBack + “\n”);
}
} a300.controlChanged[i] = false;
}
if(a300.noteOn > -1) {
osc1.resetCnt();
osc2.resetCnt();
tmr = 0;
tmrMax = 10;
keyBoard.NOTE_ON(a300.noteOn);
osc1.setFreq(keyBoard.noteOut());
a300.noteOn = -1;
}
if(a300.noteOff > -1) {
keyBoard.NOTE_OFF(a300.noteOff);
a300.noteOff = -1;
}
keyBoard.pitchWheel = a300.pitchWheel;
a300.gotEvent = false;
}

Raw formatted textfile here.
Raw formatted code for the A-300 class file here.

Advertisements
Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: