commit 9388a4723e2bf96d7ad2abfb0966ce35467d5227
parent 5a1eaf5f25e0f559ab7b401945389721cb59b7b9
Author: fundamental <[email protected]>
Date: Sat, 2 Jan 2010 22:34:17 -0500
Nio: reworked InMgr to use a lockless ringbuffer
- The InMgr now should be using a lockless ringbuffer, which removes the need
for any of the realtime threads sending midi events to lock
Diffstat:
8 files changed, 299 insertions(+), 65 deletions(-)
diff --git a/src/Nio/AlsaEngine.cpp b/src/Nio/AlsaEngine.cpp
@@ -255,50 +255,64 @@ void *AlsaEngine::MidiThread(void)
int ctrltype;
int par;
int chk;
+ MidiEvent ev;
set_realtime();
while (enabled())
{
while ((chk = snd_seq_event_input(midi.handle, &event)) > 0)
{
+ //ensure ev is empty
+ ev.channel = 0;
+ ev.num = 0;
+ ev.value = 0;
+ ev.type = 0;
+
if (!event)
continue;
par = event->data.control.param;
switch (event->type)
{
case SND_SEQ_EVENT_NOTEON:
- if (event->data.note.note)
+ if (event->data.note.note)
{
- channel = event->data.note.channel;
- note = event->data.note.note;
- velocity = event->data.note.velocity;
- sysIn->putEvent(MidiNote(note, channel, velocity));
+ ev.type = M_NOTE;
+ ev.channel = event->data.note.channel;
+ ev.num = event->data.note.note;
+ ev.value = event->data.note.velocity;
+ sysIn->putEvent(ev);
}
break;
case SND_SEQ_EVENT_NOTEOFF:
- channel = event->data.note.channel;
- note = event->data.note.note;
- sysIn->putEvent(MidiNote(note, channel));
+ ev.type = M_NOTE;
+ ev.channel = event->data.note.channel;
+ ev.num = event->data.note.note;
+ ev.value = 0;
+ sysIn->putEvent(ev);
break;
case SND_SEQ_EVENT_PITCHBEND:
- channel = event->data.control.channel;
- ctrltype = C_pitchwheel;
- par = event->data.control.value;
- sysIn->putEvent(MidiCtl(ctrltype, channel, par));
+ ev.type = M_CONTROLLER;
+ ev.channel = event->data.control.channel;
+ ev.num = C_pitchwheel;
+ ev.value = event->data.control.value;
+ sysIn->putEvent(ev);
break;
case SND_SEQ_EVENT_CONTROLLER:
- channel = event->data.control.channel;
- ctrltype = event->data.control.param;
- par = event->data.control.value;
- sysIn->putEvent(MidiCtl(ctrltype, channel, par));
+ ev.type = M_CONTROLLER;
+ ev.channel = event->data.control.channel;
+ ev.num = event->data.control.param;
+ ev.value = event->data.control.value;
+ sysIn->putEvent(ev);
break;
case SND_SEQ_EVENT_RESET: // reset to power-on state
- channel = event->data.control.channel;
- ctrltype = C_resetallcontrollers;
- sysIn->putEvent(MidiCtl(ctrltype, channel, 0));
+ ev.type = M_CONTROLLER;
+ ev.channel = event->data.control.channel;
+ ev.num = C_resetallcontrollers;
+ ev.value = 0;
+ sysIn->putEvent(ev);
break;
case SND_SEQ_EVENT_PORT_SUBSCRIBED: // ports connected
diff --git a/src/Nio/InMgr.cpp b/src/Nio/InMgr.cpp
@@ -1,4 +1,3 @@
-#include <pthread.h>
#include "InMgr.h"
#include <iostream>
@@ -6,36 +5,84 @@ using namespace std;
InMgr *sysIn;
-InMgr::InMgr(Master *_master)
- :master(_master)
+ostream &operator<<(ostream &out, const MidiEvent& ev)
+{
+ if(ev.type == M_NOTE)
+ out << "MidiNote: note(" << ev.num << ")\n"
+ << " channel(" << ev.channel << ")\n"
+ << " velocity(" << ev.value << ")";
+ else
+ out << "MidiCtl: controller(" << ev.num << ")\n"
+ << " channel(" << ev.channel << ")\n"
+ << " value(" << ev.value << ")";
+ return out;
+}
+
+MidiEvent::MidiEvent()
+ :channel(0),type(0),num(0),value(0)
{}
-void InMgr::putEvent(MidiNote note)
+InMgr::InMgr(Master *_master)
+ :queue(100), enabled(false), master(_master)
{
- cout << note << endl;
- pthread_mutex_lock(&master->mutex);
- {
- dump.dumpnote(note.channel, note.note, note.velocity);
-
- if(note.velocity)
- master->noteOn(note.channel, note.note, note.velocity);
- else
- master->noteOff(note.channel, note.note);
- }
- pthread_mutex_unlock(&master->mutex);
+ sem_init(&work, PTHREAD_PROCESS_PRIVATE, 0);
}
-void InMgr::putEvent(MidiCtl control)
+InMgr::~InMgr()
{
- cout << control << endl;
- pthread_mutex_lock(&master->mutex);
- {
- dump.dumpcontroller(control.channel, control.controller,
- control.value);
+ //lets stop the consumer thread
+ enabled = false;
+ sem_post(&work);
+ pthread_join(inThread, NULL);
- master->setController(control.channel, control.controller, control.value);
- }
- pthread_mutex_unlock(&master->mutex);
+ sem_destroy(&work);
}
+void InMgr::putEvent(MidiEvent ev)
+{
+ if(queue.push(ev)) //check for error
+ cout << "Error, Midi Ringbuffer is FULL" << endl;
+ else
+ sem_post(&work);
+}
+
+void *_inputThread(void *arg)
+{
+ return static_cast<InMgr *>(arg)->inputThread();
+}
+
+void InMgr::run()
+{
+ enabled = true;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ pthread_create(&inThread, &attr, _inputThread, this);
+}
+
+void *InMgr::inputThread()
+{
+ MidiEvent ev;
+ while(enabled()) {
+ sem_wait(&work);
+ queue.pop(ev);
+ cout << ev << endl;
+
+ pthread_mutex_lock(&master->mutex);
+ if(M_NOTE == ev.type) {
+ dump.dumpnote(ev.channel, ev.num, ev.value);
+
+ if(ev.value)
+ master->noteOn(ev.channel, ev.num, ev.value);
+ else
+ master->noteOff(ev.channel, ev.num);
+ }
+ else {
+ dump.dumpcontroller(ev.channel, ev.num, ev.value);
+ master->setController(ev.channel, ev.num, ev.value);
+ }
+ pthread_mutex_unlock(&master->mutex);
+ }
+ return NULL;
+}
diff --git a/src/Nio/InMgr.h b/src/Nio/InMgr.h
@@ -2,9 +2,25 @@
#define INMGR_H
#include <string>
-#include "MidiEvent.h"
+#include <pthread.h>
+#include <semaphore.h>
#include "../Misc/Master.h"
+#include "../Misc/Atomic.h"
+#include "SafeQueue.h"
+enum midi_type{
+ M_NOTE = 1,
+ M_CONTROLLER = 2
+}; //type=1 for note, type=2 for controller
+
+struct MidiEvent
+{
+ MidiEvent();
+ int channel; //the midi channel for the event
+ int type; //type=1 for note, type=2 for controller
+ int num; //note or contoller number
+ int value; //velocity or controller value
+};
class Master;
//super simple class to manage the inputs
@@ -12,13 +28,21 @@ class InMgr
{
public:
InMgr(Master *nmaster);
- ~InMgr(){};
+ ~InMgr();
- //only interested in Notes and Controllers for now
- void putEvent(MidiNote note);
- void putEvent(MidiCtl control);
+ void putEvent(MidiEvent ev);
+
+ //run the InMgr
+ void run();
+
+ void *inputThread();
private:
+ SafeQueue<MidiEvent> queue;
+ sem_t work;
+ Atomic<bool> enabled;
+ pthread_t inThread;
+
/**the link to the rest of zyn*/
Master *master;
};
diff --git a/src/Nio/JackEngine.cpp b/src/Nio/JackEngine.cpp
@@ -76,6 +76,7 @@ bool JackEngine::Start()
{
if(enabled())
return true;
+ cout << "Runn" << endl;
enabled = true;
if(!connectServer(""))
return false;
@@ -248,26 +249,38 @@ void JackEngine::handleMidi(unsigned long frames)
while(jack_midi_event_get(&jack_midi_event, midi_buf,
event_index++) == 0) {
- midi_data = jack_midi_event.buffer;
- type = midi_data[0] & 0xF0;
- chan = midi_data[0] & 0x0F;
+ MidiEvent ev;
+ midi_data = jack_midi_event.buffer;
+ type = midi_data[0] & 0xF0;
+ ev.channel = midi_data[0] & 0x0F;
switch(type) {
case 0x80: /* note-off */
- sysIn->putEvent(MidiNote(midi_data[1], chan));
+ ev.type = M_NOTE;
+ ev.num = midi_data[1];
+ ev.value = 0;
+ sysIn->putEvent(ev);
break;
case 0x90: /* note-on */
- sysIn->putEvent(MidiNote(midi_data[1], chan, midi_data[2]));
+ ev.type = M_NOTE;
+ ev.num = midi_data[1];
+ ev.value = midi_data[2];
+ sysIn->putEvent(ev);
break;
case 0xB0: /* controller */
- sysIn->putEvent(MidiCtl(midi_data[1], chan, midi_data[2]));
+ ev.type = M_CONTROLLER;
+ ev.num = midi_data[1];
+ ev.value = midi_data[2];
+ sysIn->putEvent(ev);
break;
case 0xE0: /* pitch bend */
- sysIn->putEvent(MidiCtl(C_pitchwheel, chan,
- ((midi_data[2] << 7) | midi_data[1])));
+ ev.type = M_CONTROLLER;
+ ev.num = C_pitchwheel;
+ ev.value = ((midi_data[2] << 7) | midi_data[1]);
+ sysIn->putEvent(ev);
break;
/* XXX TODO: handle MSB/LSB controllers and RPNs and NRPNs */
diff --git a/src/Nio/OssEngine.cpp b/src/Nio/OssEngine.cpp
@@ -34,7 +34,6 @@
#include <unistd.h>
#include <iostream>
-#include "MidiEvent.h"
#include "InMgr.h"
using namespace std;
@@ -152,6 +151,7 @@ void *OssEngine::_MidiThread(void *arg)
void *OssEngine::MidiThread()
{
set_realtime();
+ MidiEvent ev;
while(1) {
lastmidicmd = 0;
unsigned char tmp, i;
@@ -170,14 +170,34 @@ void *OssEngine::MidiThread()
tmp = getmidibyte();
}
- if((lastmidicmd >= 0x80) && (lastmidicmd <= 0x8f)) //Note OFF
- sysIn->putEvent(MidiNote(tmp, lastmidicmd%16));
- else if((lastmidicmd >= 0x90) && (lastmidicmd <= 0x9f)) //Note ON
- sysIn->putEvent(MidiNote(tmp, lastmidicmd%16, getmidibyte()));
- else if((lastmidicmd >= 0xB0) && (lastmidicmd <= 0xBF)) //Controllers
- sysIn->putEvent(MidiCtl(tmp, lastmidicmd%16, getmidibyte()));
- else if((lastmidicmd >= 0xE0) && (lastmidicmd <= 0xEF)) //Pitch Wheel
- sysIn->putEvent(MidiCtl(C_pitchwheel, lastmidicmd%16, (tmp + getmidibyte() * (int) 128) - 8192));
+ if((lastmidicmd >= 0x80) && (lastmidicmd <= 0x8f)) { //Note OFF
+ ev.type = M_NOTE;
+ ev.channel = lastmidicmd%16;
+ ev.num = tmp;
+ ev.value = 0;
+ sysIn->putEvent(ev);
+ }
+ else if((lastmidicmd >= 0x90) && (lastmidicmd <= 0x9f)) {//Note ON
+ ev.type = M_NOTE;
+ ev.channel = lastmidicmd%16;
+ ev.num = tmp;
+ ev.value = getmidibyte();
+ sysIn->putEvent(ev);
+ }
+ else if((lastmidicmd >= 0xB0) && (lastmidicmd <= 0xBF)) {//Controllers
+ ev.type = M_CONTROLLER;
+ ev.channel = lastmidicmd%16;
+ ev.num = tmp;
+ ev.value = getmidibyte();
+ sysIn->putEvent(ev);
+ }
+ else if((lastmidicmd >= 0xE0) && (lastmidicmd <= 0xEF)) {//Pitch Wheel
+ ev.type = M_CONTROLLER;
+ ev.channel = lastmidicmd%16;
+ ev.num = C_pitchwheel;
+ ev.value = (tmp + getmidibyte() * (int) 128) - 8192;
+ sysIn->putEvent(ev);
+ }
}
}
diff --git a/src/Nio/SafeQueue.cpp b/src/Nio/SafeQueue.cpp
@@ -0,0 +1,78 @@
+
+template<class T>
+SafeQueue<T>::SafeQueue(size_t maxlen)
+ :writePtr(0),readPtr(0),bufSize(maxlen)
+{
+ buffer = new T[maxlen];
+}
+
+template<class T>
+SafeQueue<T>::~SafeQueue()
+{
+ delete buffer;
+}
+
+template<class T>
+unsigned int SafeQueue<T>::size() const
+{
+ return rSpace();
+}
+
+template<class T>
+unsigned int SafeQueue<T>::rSpace() const
+{
+ size_t w, r;
+
+ w = writePtr;
+ r = readPtr;
+
+ if (w > r) {
+ return w - r;
+ }
+ else {
+ return (w - r + bufSize) % bufSize;
+ }
+}
+
+template<class T>
+unsigned int SafeQueue<T>::wSpace() const
+{
+ size_t w, r;
+
+ w = writePtr;
+ r = readPtr - 1;
+
+ if (r > w) {
+ return r - w;
+ }
+ else {
+ return (r - w + bufSize) % bufSize;
+ }
+}
+
+template<class T>
+int SafeQueue<T>::push(const T &in)
+{
+ if(!wSpace())
+ return -1;
+
+ //ok, there is space to write
+ size_t w = (writePtr + 1) % bufSize;
+ buffer[w] = in;
+ writePtr = w;
+ return 0;
+}
+
+template<class T>
+int SafeQueue<T>::pop(T &out)
+{
+ if(!rSpace())
+ return -1;
+
+ //ok, there is space to read
+ size_t r = (readPtr + 1) % bufSize;
+ out = buffer[r];
+ readPtr = r;
+ return 0;
+}
+
diff --git a/src/Nio/SafeQueue.h b/src/Nio/SafeQueue.h
@@ -0,0 +1,37 @@
+
+#ifndef SAFEQUEUE_H
+#define SAFEQUEUE_H
+
+
+/**
+ * C++ thread safe lockless queue
+ * Based off of jack's ringbuffer*/
+template <class T>
+class SafeQueue
+{
+ public:
+ SafeQueue(size_t maxlen);
+ ~SafeQueue();
+
+ /**Return read size*/
+ unsigned int size() const;
+
+ /**Returns 0 for normal
+ * Returns -1 on error*/
+ int push(const T &in);
+ int pop(T &out);
+
+ private:
+ unsigned int wSpace() const;
+ unsigned int rSpace() const;
+
+ //next writting spot
+ volatile size_t writePtr;
+ //next reading spot
+ volatile size_t readPtr;
+ const size_t bufSize;
+ T *buffer;
+};
+
+#include "SafeQueue.cpp"
+#endif
diff --git a/src/main.cpp b/src/main.cpp
@@ -205,6 +205,7 @@ void initprogram()
//Run the system
sysOut->run();
+ sysIn->run();
#warning remove welcome message when system is out of beta
cout << "\nThanks for using the Nio system :)" << endl;