classpath-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cp-patches] Patch: dssi control handling


From: Anthony Green
Subject: [cp-patches] Patch: dssi control handling
Date: Tue, 04 Oct 2005 08:21:10 -0700

DSSI soft-synths can have any number of control inputs (with default
values and MIDI controller mappings).  For instance, Xsynth has the
following controls, each of which should be adjustable from your MIDI
keyboard...

[OSC1 Pitch] = 4
[OSC1 Waveform] = 0
[OSC1 Pulse Width] = 0.5
[OSC2 Pitch] = 4
[OSC2 Waveform] = 0
[OSC2 Pulse Width] = 0.5
[Oscillator Sync] = 0
[Oscillator Balance] = 0
[LFO Frequency] = 5.05
[LFO Waveform] = 0
[LFO Osc Pitch Mod] = 0
[LFO VCF Cutoff Mod] = 0
[EG1 Attack Rate] = 0.0750025
[EG1 Decay Rate] = 0.0750025
[EG1 Sustain Level] = 0.5
[EG1 Release Rate] = 0.0750025
[EG1 Velocity Sens] = 0
[EG1 Osc Pitch Mod] = 0
[EG1 VCF Cutoff Mod] = 0
[EG2 Attack Rate] = 0.0750025
[EG2 Decay Rate] = 0.0750025
[EG2 Sustain Level] = 0.5
[EG2 Release Rate] = 0.0750025
[EG2 Velocity Sens] = 0
[EG2 Osc Pitch Mod] = 0
[EG2 VCF Cutoff Mod] = 0
[VCF Cutoff] = 50
[VCF Resonance] = 0.49875
[VCF Mode] = 0
[Glide Rate] = 0.002
[Volume] = 0.25
[Tuning] = 440

The following patch sets up the controller mapping and processes MIDI
controller events.

OK?

AG




2005-10-04  Anthony Green  <address@hidden>

        * native/jni/midi-dssi/dssi_data.h (dssi_data): Add control_count,
        control_port_map, control_value_map, sample_rate, and
        control_values fields.
        * native/jni/midi-dssi/gnu_javax_sound_midi_dssi_DSSISynthesizer.c
        (DEBUG_DSSI_PROVIDER): New macro.
        (get_port_default): New function.
        (set_control): New function.
        (Java_gnu_javax_sound_midi_dssi_DSSISynthesizer_open_1): Remove
        debug output.  Reformat.  Allocate the control ports and assign
        proper default values.
        (Java_gnu_javax_sound_midi_dssi_DSSISynthesizer_noteOn_1): Use
        JLONG_TO_PTR.
        (process): Handle MIDI control messages.
        (Java_gnu_javax_sound_midi_dssi_DSSISynthesizer_noteOff_1): Ditto.
        * gnu/javax/sound/midi/dssi/DSSISynthesizer.java 
(Channel.controlChange):
        Implement.
        (controlChange_): New native method.


Index: native/jni/midi-dssi/dssi_data.h
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/midi-dssi/dssi_data.h,v
retrieving revision 1.2
diff -u -r1.2 dssi_data.h
--- native/jni/midi-dssi/dssi_data.h    4 Oct 2005 12:24:08 -0000       1.2
+++ native/jni/midi-dssi/dssi_data.h    4 Oct 2005 15:07:06 -0000
@@ -100,5 +100,22 @@
   float *left_buffer;
   float *right_buffer;
 
+  /* The number of input controls for this synth.  */
+  unsigned control_count;
+  
+  /* An array of control values, control_count in length.  */
+  LADSPA_Data *control_values;
+
+  /* A mapping of MIDI controllers to control values.  There are a
+     maximum of 128 MIDI controllers.  */
+  unsigned control_value_map[128];
+
+  /* A mapping of MIDI controllers to LADSPA ports.  There are a
+     maximum of 128 MIDI controllers.  */
+  unsigned control_port_map[128];
+
+  /* The sample rate.  */
+  jack_nframes_t sample_rate;
+
 } dssi_data;
 
Index: native/jni/midi-dssi/gnu_javax_sound_midi_dssi_DSSISynthesizer.c
===================================================================
RCS file: 
/cvsroot/classpath/classpath/native/jni/midi-dssi/gnu_javax_sound_midi_dssi_DSSISynthesizer.c,v
retrieving revision 1.2
diff -u -r1.2 gnu_javax_sound_midi_dssi_DSSISynthesizer.c
--- native/jni/midi-dssi/gnu_javax_sound_midi_dssi_DSSISynthesizer.c    4 Oct 
2005 12:24:08 -0000       1.2
+++ native/jni/midi-dssi/gnu_javax_sound_midi_dssi_DSSISynthesizer.c    4 Oct 
2005 15:07:07 -0000
@@ -35,11 +35,30 @@
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
+/* The original get_port_default() and set_control() routines were
+ * copied from the DSSI source distribution and are covered by the
+ * following copyright and license...
+ *
+ * Copyright 2004 Chris Cannam, Steve Harris and Sean Bolton.
+ * 
+ * Permission to use, copy, modify, distribute, and sell this software
+ * for any purpose is hereby granted without fee, provided that the
+ * above copyright notice and this permission notice are included in
+ * all copies or substantial portions of the software.
+ */
+
 #include <config.h>
 #include <gnu_javax_sound_midi_dssi_DSSISynthesizer.h> 
+#include <math.h>
 
 #include "dssi_data.h"
 
+/* Define this for debug output.  */
+#undef DEBUG_DSSI_PROVIDER
+
+static void set_control (dssi_data *data, snd_seq_event_t *event);
+
+
 /**
  * The jack callback routine.
  *
@@ -50,15 +69,19 @@
 static int
 process (jack_nframes_t nframes, void *arg)
 {    
-  struct timeval tv;
   dssi_data *data = (dssi_data *) arg;
   int index;
   jack_default_audio_sample_t *buffer;
 
-  /* Look through the event buffer to see if anything needs doing.  */
+  /* Look through the event buffer to see if any control values
+     need changing.  */
   for ( index = data->midiEventReadIndex; 
        index != data->midiEventWriteIndex;
-       index = (index + 1) % EVENT_BUFFER_SIZE);
+       index = (index + 1) % EVENT_BUFFER_SIZE)
+    {
+      if (data->midiEventBuffer[index].type == SND_SEQ_EVENT_CONTROLLER)
+       set_control (data, & data->midiEventBuffer[index]);
+    }
 
   /* Call the synth audio processing routine.  */
   data->desc->run_synth(data->plugin_handle,
@@ -84,8 +107,139 @@
   return 0;   
 }
 
-/* FIXME: Temporary hack.  */
-float mctrl = 0.9f;
+
+/**
+ * Calculate a reasonable default value for a specific control port.
+ * This is mostly copied from the DSSI example code.  Copyright info
+ * is found at the top of this file.
+ *
+ */
+static LADSPA_Data 
+get_port_default (const LADSPA_Descriptor *plugin, 
+                 int port, jack_nframes_t sample_rate)
+{
+  LADSPA_PortRangeHint hint = plugin->PortRangeHints[port];
+  float lower = hint.LowerBound *
+    (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sample_rate : 1.0f);
+  float upper = hint.UpperBound *
+    (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sample_rate : 1.0f);
+  
+  if (!LADSPA_IS_HINT_HAS_DEFAULT(hint.HintDescriptor)) 
+    {
+      if (!LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) ||
+         !LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) 
+       {
+         /* No hint, its not bounded, wild guess */
+         return 0.0f;
+       }
+    
+      if (lower <= 0.0f && upper >= 0.0f) 
+       {
+         /* It spans 0.0, 0.0 is often a good guess */
+         return 0.0f;
+       }
+    
+      /* No clues, return minimum */
+      return lower;
+    }
+  
+  /* Try all the easy ones */
+  
+  if (LADSPA_IS_HINT_DEFAULT_0(hint.HintDescriptor))
+    return 0.0f;
+  else if (LADSPA_IS_HINT_DEFAULT_1(hint.HintDescriptor)) 
+    return 1.0f;
+  else if (LADSPA_IS_HINT_DEFAULT_100(hint.HintDescriptor)) 
+    return 100.0f;
+  else if (LADSPA_IS_HINT_DEFAULT_440(hint.HintDescriptor)) 
+    return 440.0f;
+   
+  /* All the others require some bounds */
+  
+  if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)
+      && (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint.HintDescriptor)))
+    return lower;
+
+  if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor))
+    {
+      if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint.HintDescriptor))
+       return upper;
+
+      if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) 
+       {
+         if (LADSPA_IS_HINT_DEFAULT_LOW(hint.HintDescriptor)) 
+           return lower * 0.75f + upper * 0.25f;
+         else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint.HintDescriptor)) 
+           return lower * 0.5f + upper * 0.5f;
+         else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint.HintDescriptor)) 
+           return lower * 0.25f + upper * 0.75f;
+       }
+    }
+  
+  /* fallback */
+  return 0.0f;
+}
+
+/**
+ * Set a control value by mapping the MIDI event to a suitable value
+ * for this control.
+ * This is mostly copied from the DSSI example code.  Copyright info
+ * is found at the top of this file.
+ *
+ */
+static void
+set_control(dssi_data *data, snd_seq_event_t *event)
+{
+  unsigned control = event->data.control.param;
+  unsigned port = data->control_port_map[control];
+
+  const LADSPA_Descriptor *p = data->desc->LADSPA_Plugin;
+
+  LADSPA_PortRangeHintDescriptor d = p->PortRangeHints[port].HintDescriptor;
+
+  LADSPA_Data lb = p->PortRangeHints[port].LowerBound *
+    (LADSPA_IS_HINT_SAMPLE_RATE(p->PortRangeHints[port].HintDescriptor) ?
+     data->sample_rate : 1.0f);
+  
+  LADSPA_Data ub = p->PortRangeHints[port].UpperBound *
+    (LADSPA_IS_HINT_SAMPLE_RATE(p->PortRangeHints[port].HintDescriptor) ?
+     data->sample_rate : 1.0f);
+  
+  float value = (float)event->data.control.value;
+  
+  if (!LADSPA_IS_HINT_BOUNDED_BELOW(d)) {
+    if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+      /* unbounded: might as well leave the value alone. */
+    } else {
+      /* bounded above only. just shift the range. */
+      value = ub - 127.0f + value;
+    }
+  } else {
+    if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+      /* bounded below only. just shift the range. */
+      value = lb + value;
+    } else {
+      /* bounded both ends.  more interesting. */
+      if (LADSPA_IS_HINT_LOGARITHMIC(d)) {
+       const float llb = logf(lb);
+       const float lub = logf(ub);
+       
+       value = expf(llb + ((lub - llb) * value / 127.0f));
+      } else {
+       value = lb + ((ub - lb) * value / 127.0f);
+      }
+    }
+  }
+  
+#ifdef DEBUG_DSSI_PROVIDER
+  printf("MIDI controller %d=%d -> control in %u=%f\n", 
+        event->data.control.param,
+        event->data.control.value, 
+        data->control_value_map[control], value);
+#endif
+
+  data->control_values[data->control_value_map[control]] = value;
+}
 
 /**
  * Open a new synthesizer.  This currently involves instantiating a
@@ -97,7 +251,7 @@
 Java_gnu_javax_sound_midi_dssi_DSSISynthesizer_open_1 
   (JNIEnv *env, jclass clazz __attribute__((unused)), jlong handle)
 {
-  unsigned int port_count, j;
+  unsigned int port_count, j, cindex, controller = 0;
   dssi_data *data = (dssi_data *) (long) handle;
   if ((data->jack_client = jack_client_new (data->desc->LADSPA_Plugin->Label)) 
== 0)
     {
@@ -106,9 +260,14 @@
                          "can't create jack client");
       return;
     } 
+
+  /* Get the jack sample rate, which may be used in default control port
+     value calculations.  */
+  data->sample_rate = jack_get_sample_rate (data->jack_client);
   
-  data->plugin_handle = 
(data->desc->LADSPA_Plugin->instantiate)(data->desc->LADSPA_Plugin, 
-                                                                
jack_get_sample_rate (data->jack_client));
+  data->plugin_handle = 
+    (data->desc->LADSPA_Plugin->instantiate)(data->desc->LADSPA_Plugin, 
+                                            data->sample_rate);
   
   if (jack_set_process_callback (data->jack_client, process, data) != 0)
     {
@@ -123,9 +282,9 @@
   data->jack_right_output_port =
     jack_port_register (data->jack_client, "output_right",
                         JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
-  
-  /* Count the number of output audio ports.  */
-  port_count = 0;
+
+  /* Count the number of controls and audio ouput ports.  */
+  port_count = data->control_count = 0;
   for (j = 0; j < data->desc->LADSPA_Plugin->PortCount; j++) 
     {
       LADSPA_PortDescriptor pod =
@@ -133,11 +292,20 @@
       
       if (LADSPA_IS_PORT_AUDIO(pod) && LADSPA_IS_PORT_OUTPUT(pod))
        port_count++;
+      else if (LADSPA_IS_PORT_CONTROL(pod) && LADSPA_IS_PORT_INPUT(pod))
+       data->control_count++;
     }
-  printf ("LADSPA output ports = %d\n", port_count);
+
+  /* Allocate the array of control values.  */
+  data->control_values = 
+    (LADSPA_Data *) JCL_malloc (env, 
+                               data->control_count * sizeof (LADSPA_Data));
+
+  /* Initialize the MIDI control map.  */
+  memset (data->control_value_map, 0, data->control_count * sizeof(unsigned));
   
   /* Create buffers for each port.  */
-  for (j = 0; j < data->desc->LADSPA_Plugin->PortCount; j++) 
+  for (cindex = 0, j = 0; j < data->desc->LADSPA_Plugin->PortCount; j++) 
     {  
       LADSPA_PortDescriptor pod =
        data->desc->LADSPA_Plugin->PortDescriptors[j];
@@ -152,11 +320,39 @@
       else 
        if (LADSPA_IS_PORT_CONTROL(pod) && LADSPA_IS_PORT_INPUT(pod))
          {
+           /* This is an input control port.  Connect it to a properly
+              initialized value in our controller value array.  */
            (data->desc->LADSPA_Plugin->connect_port)
-             (data->plugin_handle, j, &mctrl);
+             (data->plugin_handle, j, &(data->control_values[cindex]));
+           data->control_values[cindex] = 
+             get_port_default (data->desc->LADSPA_Plugin,
+                               j, data->sample_rate);
+
+           /* Set up the mapping between MIDI controllers and this
+              contoller value.  */
+           if (data->desc->get_midi_controller_for_port)
+             {
+               controller = data->desc->
+                 get_midi_controller_for_port(data->plugin_handle, j);
+
+               if (DSSI_IS_CC(controller))
+                 {
+                   data->control_value_map[DSSI_CC_NUMBER(controller)] = 
cindex;
+                   data->control_port_map[DSSI_CC_NUMBER(controller)] = j;
+                 }
+             }
+
+#ifdef DEBUG_DSSI_PROVIDER
+           printf ("MIDI Controller 0x%x [%s] = %g\n", 
+                   DSSI_CC_NUMBER(controller),
+                   data->desc->LADSPA_Plugin->PortNames[j],
+                   data->control_values[cindex]);
+#endif
+
+           cindex++;
          }
     }
-
+  
   (data->desc->LADSPA_Plugin->activate)(data->plugin_handle);
 
   if (jack_activate (data->jack_client))
@@ -165,6 +361,28 @@
 }
 
 /**
+ * This is called when we receive a new MIDI CONTROL CHANGE message.
+ * Simply stick an appropriate event in the event buffer.  This will
+ * get processed in the jack callback function.
+ */
+JNIEXPORT void JNICALL 
+Java_gnu_javax_sound_midi_dssi_DSSISynthesizer_controlChange_1 
+  (JNIEnv *env __attribute__((unused)), jclass clazz __attribute__((unused)), 
+   jlong handle, jint channel, jint control, jint value)
+{
+  dssi_data *data = JLONG_TO_PTR(dssi_data,handle);
+
+  /* Insert this event in the event buffer.  */
+  snd_seq_event_t *ev = & data->midiEventBuffer[data->midiEventWriteIndex];
+
+  /* Set the event value.  */
+  snd_seq_ev_set_controller (ev, channel, control, value);
+
+  data->midiEventWriteIndex = 
+    (data->midiEventWriteIndex + 1) % EVENT_BUFFER_SIZE;
+}
+
+/**
  * This is called when we receive a new MIDI NOTE ON message.  Simply
  * stick an appropriate event in the event buffer.  This will get
  * processed in the jack callback function.
@@ -174,7 +392,7 @@
   (JNIEnv *env __attribute__((unused)), jclass clazz __attribute__((unused)), 
    jlong handle, jint channel, jint note, jint velocity)
 {
-  dssi_data *data = (dssi_data *) (long) handle;
+  dssi_data *data = JLONG_TO_PTR(dssi_data,handle);
 
   /* Insert this event in the event buffer.  */
   snd_seq_event_t *ev = & data->midiEventBuffer[data->midiEventWriteIndex];
@@ -199,7 +417,7 @@
    jclass clazz __attribute__((unused)), 
    jlong handle, jint channel, jint note, jint velocity)
 {
-  dssi_data *data = (dssi_data *) (long) handle;
+  dssi_data *data = JLONG_TO_PTR(dssi_data,handle);
 
   /* Insert this event in the event buffer.  */
   snd_seq_event_t *ev = & data->midiEventBuffer[data->midiEventWriteIndex];
Index: gnu/javax/sound//midi/dssi/DSSISynthesizer.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/gnu/javax/sound/midi/dssi/DSSISynthesizer.java,v
retrieving revision 1.1
diff -u -r1.1 DSSISynthesizer.java
--- gnu/javax/sound//midi/dssi/DSSISynthesizer.java     3 Oct 2005 01:53:12 
-0000       1.1
+++ gnu/javax/sound//midi/dssi/DSSISynthesizer.java     4 Oct 2005 15:07:07 
-0000
@@ -85,6 +85,10 @@
           else
             channels[smessage.getChannel()].noteOff(smessage.getData1());
           break;
+        case ShortMessage.CONTROL_CHANGE:
+          channels[smessage.getChannel()].controlChange(smessage.getData1(),
+                                                        smessage.getData2());
+          break;
         default:
           System.out.println ("Unhandled message: " + message.getStatus());
           break;
@@ -106,6 +110,7 @@
   static native void noteOff_(long handle, int channel, int noteNumber, int 
velocity);  
   static native void setPolyPressure_(long handle, int channel, int 
noteNumber, int pressure);
   static native int getPolyPressure_(long handle, int channel, int noteNumber);
+  static native void controlChange_(long handle, int channel, int control, int 
value);
   static native void open_(long handle);
   static native void close_(long handle);
       
@@ -184,13 +189,10 @@
       return 0;
     }
 
-    /* (non-Javadoc)
-     * @see javax.sound.midi.MidiChannel#controlChange(int, int)
-     */
+    /* @see javax.sound.midi.MidiChannel#controlChange(int, int)  */
     public void controlChange(int controller, int value)
     {
-      // TODO Auto-generated method stub
-
+      controlChange_(sohandle, channel, controller, value);
     }
 
     /* (non-Javadoc)






reply via email to

[Prev in Thread] Current Thread [Next in Thread]