zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

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:
Msrc/Nio/AlsaEngine.cpp | 52+++++++++++++++++++++++++++++++++-------------------
Msrc/Nio/InMgr.cpp | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/Nio/InMgr.h | 34+++++++++++++++++++++++++++++-----
Msrc/Nio/JackEngine.cpp | 29+++++++++++++++++++++--------
Msrc/Nio/OssEngine.cpp | 38+++++++++++++++++++++++++++++---------
Asrc/Nio/SafeQueue.cpp | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/SafeQueue.h | 37+++++++++++++++++++++++++++++++++++++
Msrc/main.cpp | 1+
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;