Hello again list,
Today I hacked a little in the fluxus-midi module. I "needed" to get the
MIDI controller change 'as it happened' so to speak.. As usual, various ways
to accomplish this... I looked down upon maintaining a local vector of
controller values in the (fluxus) program, and checking out which have
changed (every channel, all controllers = most probably very slow to do
every-frame). I also looked at (midi-peek), but that returns a string value,
and as the function description includes "for debugging purposes", I'm not
too sure if that would be most optimal, as it's a single string returning
only the last of a sequence of events, so I could loose values there.
The quickest way to get me results, I thought would be to solve my problem
in the fluxus-midi module itself. My version will also 'queue' the event
like the MIDINotes are queued. I kept this list only 16 items large, as
there could be a lot of controller values being sent and for my purpose of
animation only the most recent are interesting...
So I went on to implement (midi-cc-event) which returns #f is there was no
event and otherwise controller information: number and value, similar to
(midi-note).
Now my question is, was implementing that a wise thing to do? Or did I
re-invent the wheel there because I am unaware of something? Or there is a
slight possibility that change may be useful upstream?
Also, when going through that code (specifically, that enum in
MIDIListener), I get the impression that "Program Change" and "Pitch Bend"
are planned features?
If so, perhaps interesting to know that I have already done "Program
Change"; implementing (midi-program ch), to get the current program value
for the requested channel.
I have attached the patch that implements the (midi-program) and
(midi-cc-event), might anyone be interested...
Cheers,
Hugo
diff --git a/modules/fluxus-midi/src/FluxusMIDI.cpp b/modules/fluxus-midi/src/FluxusMIDI.cpp
index efe6af9..c9654d9 100644
--- a/modules/fluxus-midi/src/FluxusMIDI.cpp
+++ b/modules/fluxus-midi/src/FluxusMIDI.cpp
@@ -306,6 +306,74 @@ Scheme_Object *midi_note(int argc, Scheme_Object **argv)
}
// StartFunctionDoc-en
+// midi-program channel-number
+// Returns: program-value-number
+// Description:
+// Returns the program value.
+// Example:
+// (midi-program 0)
+// EndFunctionDoc
+
+Scheme_Object *midi_program(int argc, Scheme_Object **argv)
+{
+ Scheme_Object *ret = NULL;
+ MZ_GC_DECL_REG(2);
+ MZ_GC_VAR_IN_REG(0, argv);
+ MZ_GC_VAR_IN_REG(1, ret);
+ MZ_GC_REG();
+
+ if (!SCHEME_NUMBERP(argv[0]))
+ scheme_wrong_type("midi-program", "number", 0, argc, argv);
+
+ int channel = (int)scheme_real_to_double(argv[0]);
+
+ if (midilistener != NULL)
+ {
+ int val = midilistener->get_program(channel);
+ ret = scheme_make_integer(val);
+ }
+ else
+ {
+ ret = scheme_void;
+ }
+
+ MZ_GC_UNREG();
+ return ret;
+} // midi_program(..)
+
+// midi-cc-event
+// Returns: #(channel controller value) or #f
+// Description:
+// Returns the next event from the MIDI note event queue or #f if the queue is empty.
+// Example:
+// (midi-cc-event)
+// EndFunctionDoc
+Scheme_Object *midi_cc_event(int argc, Scheme_Object **argv)
+{
+ Scheme_Object *ret = NULL;
+ MZ_GC_DECL_REG(2);
+ MZ_GC_VAR_IN_REG(2, ret);
+ MZ_GC_REG();
+
+ ret = scheme_false;
+ if (midilistener != NULL)
+ {
+ MIDIEvent *evt = midilistener->get_cc_event();
+ if (evt)
+ {
+ ret = scheme_make_vector(3, scheme_void);
+ SCHEME_VEC_ELS(ret)[0] = scheme_make_integer(evt->channel);
+ SCHEME_VEC_ELS(ret)[1] = scheme_make_integer(evt->controller);
+ SCHEME_VEC_ELS(ret)[2] = scheme_make_integer(evt->value);
+ }
+ }
+
+ MZ_GC_UNREG();
+ return ret;
+
+} // midi_cc_event(..)
+
+// StartFunctionDoc-en
// midi-peek
// Returns: msg-string
// Description:
@@ -364,6 +432,10 @@ Scheme_Object *scheme_reload(Scheme_Env *env)
scheme_make_prim_w_arity(midi_note, "midi-note", 0, 0), menv);
scheme_add_global("midi-peek",
scheme_make_prim_w_arity(midi_peek, "midi-peek", 0, 0), menv);
+ scheme_add_global("midi-program",
+ scheme_make_prim_w_arity(midi_program, "midi-program", 1, 1), menv);
+ scheme_add_global("midi-cc-event",
+ scheme_make_prim_w_arity(midi_cc_event, "midi-cc-event", 0, 0), menv);
scheme_finish_primitive_module(menv);
MZ_GC_UNREG();
diff --git a/modules/fluxus-midi/src/MIDIListener.cpp b/modules/fluxus-midi/src/MIDIListener.cpp
index ee759d1..7117b23 100644
--- a/modules/fluxus-midi/src/MIDIListener.cpp
+++ b/modules/fluxus-midi/src/MIDIListener.cpp
@@ -24,6 +24,8 @@ using namespace std;
/** maximum number of midi notes stored */
static const unsigned MAX_MIDI_NOTE_COUNT = 256;
+/** maximum number of midi events stored.. there could be *a lot* of events, so this size is intensionally left small. **/
+static const unsigned MAX_MIDI_EVENT_COUNT = 16;
MIDINote::MIDINote(int _on_off, int _channel, int _note, int _velocity) :
on_off(_on_off),
@@ -33,6 +35,13 @@ MIDINote::MIDINote(int _on_off, int _channel, int _note, int _velocity) :
{
}
+MIDIEvent::MIDIEvent(int _channel, int _controller, int _value) :
+ channel(_channel),
+ controller(_controller),
+ value(_value)
+{
+}
+
void midi_callback(double deltatime, vector<unsigned char> *message,
void *user_data)
{
@@ -52,6 +61,10 @@ MIDIListener::MIDIListener(int port /*= -1*/) :
cntrl_values = new unsigned char[MAX_CNTRL];
fill(cntrl_values, cntrl_values + MAX_CNTRL, 0);
+ /* likewise for the per channel "program" values */
+ pgm_values = new unsigned char[MAX_CHAN];
+ fill(pgm_values, pgm_values + MAX_CHAN, 0);
+
pthread_mutex_init(&mutex, NULL);
}
@@ -193,6 +206,26 @@ int MIDIListener::get_cc(int channel, int cntrl_number)
}
/**
+ * Returns program value.
+ * \param channel MIDI channel
+ * \retval int program value
+ **/
+int MIDIListener::get_program(int channel)
+{
+ if (midiin == NULL)
+ {
+ init_midi();
+ if (midiin == NULL)
+ return 0;
+ }
+
+ pthread_mutex_lock(&mutex);
+ int v = pgm_values[ channel ];
+ pthread_mutex_unlock(&mutex);
+ return v;
+}
+
+/**
* Returns normalised controller values.
* \param channel MIDI channel
* \param cntrl_number controller number
@@ -257,6 +290,46 @@ MIDINote *MIDIListener::get_note(void)
}
/**
+ * Returns next MIDI controller event from event queue.
+ * \retval MIDIEvent* pointer to MIDI Event or NULL if the queue is empty
+ **/
+MIDIEvent *MIDIListener::get_cc_event(void)
+{
+ static MIDIEvent evt;
+
+ pthread_mutex_lock(&mutex);
+ if (midi_events.empty())
+ {
+ pthread_mutex_unlock(&mutex);
+ return NULL;
+ }
+
+ MIDIEvent *n = midi_events.front();
+ midi_events.pop_front();
+ pthread_mutex_unlock(&mutex);
+
+ evt.channel = n->channel;
+ evt.value = n->value;
+ evt.controller = n->controller;
+ delete n;
+ return &evt;
+}
+
+/**
+ * Adds a new event to the event queue.
+ **/
+void MIDIListener::add_event(int channel, int controller, int value)
+{
+ MIDIEvent *n = new MIDIEvent(channel,controller,value);
+ midi_events.push_back(n);
+ while (midi_events.size() > MAX_MIDI_EVENT_COUNT)
+ {
+ delete midi_events.front();
+ midi_events.pop_front();
+ }
+}
+
+/**
* Adds a new note to the event queue.
**/
void MIDIListener::add_note(int on_off, int ch, int note, int velocity)
@@ -281,6 +354,16 @@ void MIDIListener::callback(double deltatime, vector<unsigned char> *message)
switch (status)
{
+ case MIDIListener::MIDI_PROGRAM_CHANGE:
+ if (count == 2)
+ {
+ int program_number = (*message)[1];
+ pthread_mutex_lock(&mutex);
+ pgm_values[ch] = program_number;
+ pthread_mutex_unlock(&mutex);
+ }
+ break;
+
case MIDIListener::MIDI_CONTROLLER:
if (count == 3)
{
@@ -290,6 +373,7 @@ void MIDIListener::callback(double deltatime, vector<unsigned char> *message)
int value = (*message)[2]; /* controller value */
pthread_mutex_lock(&mutex);
cntrl_values[i] = value;
+ add_event(ch, cntrl_number, value);
pthread_mutex_unlock(&mutex);
}
break;
diff --git a/modules/fluxus-midi/src/MIDIListener.h b/modules/fluxus-midi/src/MIDIListener.h
index 145dbc9..dfe65f2 100644
--- a/modules/fluxus-midi/src/MIDIListener.h
+++ b/modules/fluxus-midi/src/MIDIListener.h
@@ -26,7 +26,8 @@
using namespace std;
-#define MAX_CNTRL (16*128)
+#define MAX_CHAN 16
+#define MAX_CNTRL (MAX_CHAN*128)
class MIDINote
{
@@ -40,6 +41,16 @@ class MIDINote
int velocity; /**< velocity of MIDI note */
};
+class MIDIEvent
+{
+ public:
+ MIDIEvent() { channel = controller = value; }
+ MIDIEvent(int channel, int controller, int value);
+ int channel; /**< MIDI channel (>=0) **/
+ int controller; /*<< MIDI controller number **/
+ int value; /**< the actual controller value **/
+};
+
class MIDIListener
{
public:
@@ -53,10 +64,12 @@ class MIDIListener
int get_cc(int channel, int cntrl_number);
float get_ccn(int channel, int cntrl_number);
+ int get_program(int channel);
string get_last_event(void);
- MIDINote *get_note(void);
+ MIDINote *get_note(void);
+ MIDIEvent *get_cc_event(void);
enum {
MIDI_NOTE_OFF = 0x08,
@@ -68,6 +81,7 @@ class MIDIListener
private:
void init_midi(void);
void add_note(int on_off, int ch, int note, int velocity);
+ void add_event(int channel, int controller, int value);
pthread_mutex_t mutex;
@@ -78,8 +92,12 @@ class MIDIListener
/** array holding the current state of all, 16*128 controllers */
unsigned char *cntrl_values;
-
+
+ /** array holding program number of 16 channels **/
+ unsigned char *pgm_values;
+
deque<MIDINote *> midi_notes;
+ deque<MIDIEvent *> midi_events;
};
#endif