diff --git a/Channel.pde b/Channel.pde index 8e87c40..e829a04 100644 --- a/Channel.pde +++ b/Channel.pde @@ -3,12 +3,18 @@ import java.util.Stack; public class ChannelOsc { + int id; HashMap current_notes; // Pairs float curr_global_amp = 1.0; // channel volume (0.0 to 1.0) float amp_multiplier = 1.0; // basically expression float curr_global_bend = 0.0; // channel pitch bend (-curr_bend_range to curr_bend_range semitones) float curr_global_pan = 0.0; // channel stereo panning (-1.0 to 1.0) float curr_bend_range = 2.0; // channel pitch bend range +/- semitones... uh, sure. + boolean hold_pedal = false; + boolean sostenuto_pedal = false; + boolean soft_pedal = false; + ArrayList curr_holding; + ArrayList curr_sustaining; float curr_freqDetune = 0.0; float curr_noteDetune = 0.0; String please_how_many_midi_params_are_there = "dw, around 100+"; // darn. @@ -29,11 +35,15 @@ public class ChannelOsc { ChannelOsc() { current_notes = new HashMap(); + curr_holding = new ArrayList(); + curr_sustaining = new ArrayList(); } ChannelOsc(int osc_type) { current_notes = new HashMap(); + curr_holding = new ArrayList(); + curr_sustaining = new ArrayList(); set_osc_type(osc_type); } @@ -41,6 +51,7 @@ public class ChannelOsc { void create_display(int x, int y, int id) { ChannelDisplay d = new ChannelDisplay(x, y, id, this); this.disp = d; + this.id = id; } @@ -59,7 +70,7 @@ public class ChannelOsc { void play_note(int note_code, int velocity) { - if (curr_global_amp <= 0 || silenced) return; + if (curr_global_amp <= 0 || silenced || osc_type == -1) return; if (osc_type == 4) { play_drum(note_code, velocity); return; @@ -77,7 +88,7 @@ public class ChannelOsc { current_notes.put(note_code, s); } s.pan(curr_global_pan); - s.amp(amp * (osc_type == 1 || osc_type == 2 ? 0.12 : 0.05) * curr_global_amp * amp_multiplier); // give a volume boost to TRI and SIN + s.amp(amp * (osc_type == 1 || osc_type == 2 ? 0.12 : 0.05) * curr_global_amp * amp_multiplier * (soft_pedal ? 0.5 : 1)); // give a volume boost to TRI and SIN if (osc_type == 0) ((Pulse) s).width(pulse_width); s.play(); @@ -95,7 +106,7 @@ public class ChannelOsc { SoundFile s = (SoundFile) samples[sample_code-1]; if (s == null || s.isPlaying()) return; - s.amp(amp * 0.32 * curr_global_amp * amp_multiplier); + s.amp(amp * 0.32 * curr_global_amp * amp_multiplier * (soft_pedal ? 0.5 : 1)); s.play(); last_amp = amp; @@ -105,6 +116,14 @@ public class ChannelOsc { void stop_note(int note_code) { + if (hold_pedal) { + curr_holding.add(note_code); + return; + } + if (sostenuto_pedal && curr_sustaining.contains(note_code)) { + return; + } + if (osc_type != 4) { SoundObject s = current_notes.get(note_code); if (s == null) return; @@ -117,6 +136,43 @@ public class ChannelOsc { } + void set_hold(int value) { + if (osc_type == 4) return; + hold_pedal = value < 63 ? false : true; + if (!hold_pedal) { + for (int note_code : curr_holding) { + stop_note(note_code); + } + curr_holding.clear(); + } + } + + + void set_sostenuto(int value) { + if (osc_type == 4) return; + sostenuto_pedal = value < 63 ? false : true; + if (sostenuto_pedal) { + for (Entry s : this.current_notes.entrySet()) { + if (((Oscillator) s.getValue()).isPlaying()) { + curr_sustaining.add(s.getKey()); + } + } + } + else { + for (int note_code : curr_sustaining) { + stop_note(note_code); + } + curr_sustaining.clear(); + } + } + + + void set_soft(int value) { + soft_pedal = value < 63 ? false : true; + set_all_oscs_amp(); + } + + void set_osc_type(float osc_type) { // if 0.0 < value < 1.0, then pulse osc shut_up(); current_notes.clear(); @@ -156,13 +212,13 @@ public class ChannelOsc { void set_all_oscs_amp() { for (SoundObject s : current_notes.values()) { - ((Oscillator) s).amp((osc_type == 1 || osc_type == 2 ? 0.12 : 0.05) * curr_global_amp * amp_multiplier); + ((Oscillator) s).amp((osc_type == 1 || osc_type == 2 ? 0.12 : 0.05) * curr_global_amp * amp_multiplier * (soft_pedal ? 0.5 : 1)); } } void set_bend(int bits_lsb, int bits_msb) { - if (osc_type == -1) return; // no bend for drums... + if (osc_type == 4) return; // no bend for drums... int value = (bits_msb << 7) + bits_lsb; curr_global_bend = map(value, 0, 16383, -1.0, 1.0) * curr_bend_range; @@ -193,16 +249,22 @@ public class ChannelOsc { void shut_up() { + set_hold(0); + set_sostenuto(0); + set_soft(0); for (int note_code : current_notes.keySet()) stop_note(note_code); } void reset_params() { current_notes.clear(); + curr_holding.clear(); + curr_sustaining.clear(); last_amp = 0.0; last_freq = 0; last_notecode = -1; - osc_type = -1; + if (id == 9) osc_type = 4; + else osc_type = -1; pulse_width = 0.5; curr_global_amp = 1.0; amp_multiplier = 1.0; diff --git a/Player.pde b/Player.pde index 9f84931..311fb86 100644 --- a/Player.pde +++ b/Player.pde @@ -67,6 +67,10 @@ class Player { float last_freqDetune = 0.0; float last_noteDetune = 0.0; String custom_info_msg = ""; + boolean file_is_GM = false; + boolean file_is_GM2 = false; + boolean file_is_XG = false; + boolean file_is_GS = false; Synthesizer syn; @@ -118,6 +122,27 @@ class Player { } + protected void set_params_from_sysex(byte[] arr) { + int man_id = arr[0]; + switch(man_id) { + case 67: // Yamh, XG + file_is_XG = true; + break; + + case 65: // Rold, GS + file_is_GS = true; + break; + + default: // check if GM or GM2 + if (arr[2] == 9) { + file_is_GM = arr[3] == 1 ? true : false; + file_is_GM2 = arr[3] == 3 ? true : false; + } + break; + } + } + + protected void set_rpn_param_val(int chan, float value) { switch (curr_rpn) { case 0: @@ -330,6 +355,11 @@ class Player { seq.setLoopEndPoint(-1); seq.setLoopStartPoint(0); + + file_is_GM = false; + file_is_GM2 = false; + file_is_XG = false; + file_is_GS = false; } @@ -415,6 +445,18 @@ class Player { set_rpn_param_val(chan, data2); // data entry break; + case 64: // hold pedal + channels[chan].set_hold(data2); + break; + + case 66: // sostenuto pedal + channels[chan].set_sostenuto(data2); + break; + + case 67: // soft pedal + channels[chan].set_soft(data2); + break; + case 96: add_rpn_param_val(chan, data2, false); // data increment break; @@ -428,6 +470,11 @@ class Player { } } } + + else if (msg instanceof SysexMessage) { + SysexMessage event = (SysexMessage) msg; + set_params_from_sysex(event.getData()); + } } diff --git a/UIServer.pde b/UIServer.pde index 8f40fdf..7fba78a 100644 --- a/UIServer.pde +++ b/UIServer.pde @@ -28,6 +28,9 @@ class ChannelDisplay { float label_pulse_width = 0.5; float meter_bend = 0.0; float meter_pan = 0.0; + boolean label_hold_pedal = false; + boolean label_sostenuto_pedal = false; + boolean label_soft_pedal = false; final float METER_LERP_QUICKNESS = 0.5; final int METER_VU_LENGTH = 30; @@ -58,6 +61,9 @@ class ChannelDisplay { meter_ch_volume = parent.curr_global_amp * parent.amp_multiplier; meter_velocity = parent.last_amp; label_osc_type = parent.osc_type; + label_hold_pedal = parent.hold_pedal; + label_sostenuto_pedal = parent.sostenuto_pedal; + label_soft_pedal = parent.soft_pedal; int notecode = parent.last_notecode - 21; if (label_osc_type == 4) { if (notecode <= -1) label_note = "| |"; else label_note = "/ \\"; } @@ -184,6 +190,13 @@ class ChannelDisplay { if (meter_pan != 0) triangle(x+144 + 9 * meter_pan, y+48 - 9 * abs(meter_pan), x+144 + 9 * meter_pan, y+48 + 9 * abs(meter_pan), x+144, y+48); //text(meter_pan, x+145, y+48); + // Pedals + textFont(fonts[0]); + fill(t.theme[4]); + if (label_hold_pedal) text("H", x+64, y+8); + if (label_sostenuto_pedal) text("S", x+72, y+8); + if (label_soft_pedal) text("s", x+80, y+8); + button_mute.redraw(); } } @@ -210,6 +223,12 @@ class PlayerDisplay { float meter_loop_end_X = 0.0; int meter_loop_begin_Y = 0; int meter_loop_end_Y = 0; + String label_timestamp = "-:--"; + String label_timelength = "-:--"; + boolean label_GM = false; + boolean label_GM2 = false; + boolean label_XG = false; + boolean label_GS = false; PlayerDisplay(int x, int y, Player parent) { @@ -240,13 +259,26 @@ class PlayerDisplay { meter_loop_begin = map(parent.seq.getLoopStartPoint(), 0, player.seq.getTickLength(), 0.0, 1.0); if (parent.seq.getLoopEndPoint() == -1) meter_loop_end = 1.0; else meter_loop_end = map(parent.seq.getLoopEndPoint(), 0, player.seq.getTickLength(), 0.0, 1.0); + + long secPos = player.seq.getMicrosecondPosition() / 1000000; + long secLen = player.seq.getMicrosecondLength() / 1000000; + this.label_timestamp = secPos / 60 + ":" + String.format("%02d", secPos % 60); + this.label_timelength = secLen / 60 + ":" + String.format("%02d", secLen % 60); } else { meter_loop_begin = 0.0; meter_loop_end = 1.0; + + this.label_timestamp = "-:--"; + this.label_timelength = "-:--"; } meter_loop_begin_X = x + POS_X_POSBAR + (WIDTH_POSBAR * meter_loop_begin); meter_loop_end_X = x + POS_X_POSBAR + (WIDTH_POSBAR * meter_loop_end); + + label_GM = parent.file_is_GM; + label_GM2 = parent.file_is_GM2; + label_XG = parent.file_is_XG; + label_GS = parent.file_is_GS; } @@ -255,7 +287,7 @@ class PlayerDisplay { if (collided_posbar()) { if (parent.playing_state == -1) return; - int new_pos = int( map(mouseX, x + POS_X_POSBAR, x + POS_X_POSBAR + WIDTH_POSBAR, 0, parent.seq.getTickLength()) ); + int new_pos = int( map(_mouseX, x + POS_X_POSBAR, x + POS_X_POSBAR + WIDTH_POSBAR, 0, parent.seq.getTickLength()) ); parent.setTicks(new_pos); return; } @@ -263,12 +295,12 @@ class PlayerDisplay { int handle_no = collided_loopset_bar(); try { if (handle_no == -1) { - int new_pos = int( map(mouseX, x + POS_X_POSBAR, x + POS_X_POSBAR + WIDTH_POSBAR, 0, parent.seq.getTickLength()) ); + int new_pos = int( map(_mouseX, x + POS_X_POSBAR, x + POS_X_POSBAR + WIDTH_POSBAR, 0, parent.seq.getTickLength()) ); parent.seq.setLoopStartPoint(new_pos); } else if (handle_no == 1) { - int new_pos = int( map(mouseX, x + POS_X_POSBAR, x + POS_X_POSBAR + WIDTH_POSBAR, 0, parent.seq.getTickLength()) ); + int new_pos = int( map(_mouseX, x + POS_X_POSBAR, x + POS_X_POSBAR + WIDTH_POSBAR, 0, parent.seq.getTickLength()) ); parent.seq.setLoopEndPoint(new_pos); } } @@ -291,6 +323,14 @@ class PlayerDisplay { fill(t.theme[3]); rect(x+1 + POS_X_POSBAR, y+1 + POS_Y_POSBAR, (WIDTH_POSBAR-1) * meter_midi_pos, HEIGHT_POSBAR-1, 4); + // Song pos and length labels + textAlign(CENTER, CENTER); + fill(t.theme[4]); + textFont(fonts[1]); + int auxX = x + POS_X_POSBAR + WIDTH_POSBAR/2; + int auxY = y + POS_Y_POSBAR + 9; + outlinedText(label_timestamp + " / " + label_timelength, auxX, auxY, t.theme[4], t.theme[0] - color(0x40000000)); + // Loop set meter fill(parent.seq.getLoopCount() == 0 ? t.theme[1] : t.theme[3]); stroke(t.theme[0]); @@ -298,10 +338,9 @@ class PlayerDisplay { triangle(meter_loop_end_X, meter_loop_end_Y, meter_loop_end_X - 4, meter_loop_end_Y + 16, meter_loop_end_X + 4, meter_loop_end_Y + 16); // File name label - textAlign(CENTER, CENTER); fill(t.theme[0]); textFont(fonts[3]); - text(label_filename, width / 2, y + POS_Y_POSBAR - 12); + text(label_filename, 362, y + POS_Y_POSBAR - 12); // Messages label stroke(t.theme[0]); @@ -311,6 +350,14 @@ class PlayerDisplay { textFont(fonts[1]); text(label_message, x + POS_X_MESSAGEBAR + WIDTH_MESSAGEBAR/2, y + POS_Y_POSBAR + 9); + // Manufacturers / MIDI formats + fill(t.theme[0]); + textFont(fonts[0]); + if (label_GM) text("GM", x + 660, y - 300); + if (label_GM2) text("GM2", x + 660, y - 290); + if (label_XG) text("XG", x + 660, y - 280); + if (label_GS) text("GS", x + 660, y - 270); + /* fill(t.theme[2]); noStroke(); @@ -323,7 +370,7 @@ class PlayerDisplay { boolean collided_posbar() { - return (mouseX > x + POS_X_POSBAR && mouseX < WIDTH_POSBAR + x + POS_X_POSBAR) && (mouseY > y + POS_Y_POSBAR && mouseY < HEIGHT_POSBAR + y + POS_Y_POSBAR); + return (_mouseX > x + POS_X_POSBAR && _mouseX < WIDTH_POSBAR + x + POS_X_POSBAR) && (_mouseY > y + POS_Y_POSBAR && _mouseY < HEIGHT_POSBAR + y + POS_Y_POSBAR); } @@ -331,10 +378,10 @@ class PlayerDisplay { int which = 0; // 0 is not pressed if (parent.seq.getLoopCount() == 0) return 0; - if ((mouseX > x + POS_X_POSBAR && mouseX < WIDTH_POSBAR + x + POS_X_POSBAR + 4) && (mouseY > y + POS_Y_POSBAR - 16 && mouseY < y + POS_Y_POSBAR)) { + if ((_mouseX > x + POS_X_POSBAR && _mouseX < WIDTH_POSBAR + x + POS_X_POSBAR + 4) && (_mouseY > y + POS_Y_POSBAR - 16 && _mouseY < y + POS_Y_POSBAR)) { which = -1; // -1 is begin } - else if ((mouseX > x + POS_X_POSBAR && mouseX < WIDTH_POSBAR + x + POS_X_POSBAR + 4) && (mouseY > y + POS_Y_POSBAR + HEIGHT_POSBAR && mouseY < y + POS_Y_POSBAR + HEIGHT_POSBAR + 16)) { + else if ((_mouseX > x + POS_X_POSBAR && _mouseX < WIDTH_POSBAR + x + POS_X_POSBAR + 4) && (_mouseY > y + POS_Y_POSBAR + HEIGHT_POSBAR && _mouseY < y + POS_Y_POSBAR + HEIGHT_POSBAR + 16)) { which = 1; // 1 is end } @@ -402,7 +449,7 @@ class Button { boolean collided() { - return (mouseX > this.x && mouseX < this.width + this.x) && (mouseY > this.y && mouseY < this.height + this.y); + return (_mouseX > this.x && _mouseX < this.width + this.x) && (_mouseY > this.y && _mouseY < this.height + this.y); } boolean collided(PApplet win) { @@ -461,3 +508,20 @@ class ButtonToolbar { return b.collided(win); } } + + + +void outlinedText(String text, int x, int y, color cFill, color cStroke) { + push(); + + fill(cStroke); + for (int i = -1; i < 2; i++) { + text(text, x+i, y); + text(text, x, y+i); + } + + fill(cFill); + text(text, x, y); + + pop(); +}