zynaddsubfx

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

commit 6685c8cfe0df5e2daf6d893abc8357e1389131ba
parent 6063bcaa3de6faa77d4eee5d0853693d145bb62f
Author: fundamental <[email protected]>
Date:   Fri, 12 Feb 2010 16:24:04 -0500

Nio: Restructuring

Warning this is a messy commit because the local copy was not updating the
remote often enough

Diffstat:
Msrc/Misc/Util.h | 6++++++
Msrc/Nio/AlsaEngine.cpp | 214+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/Nio/AlsaEngine.h | 3---
Msrc/Nio/AudioOut.cpp | 178+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/Nio/AudioOut.h | 32+++++---------------------------
Msrc/Nio/CMakeLists.txt | 1+
Msrc/Nio/Engine.cpp | 5-----
Msrc/Nio/Engine.h | 10++--------
Msrc/Nio/EngineMgr.cpp | 8++++++++
Msrc/Nio/EngineMgr.h | 12++++--------
Msrc/Nio/InMgr.cpp | 35+++++++++++++++++++++++++++++++++++
Msrc/Nio/InMgr.h | 5++++-
Msrc/Nio/JackEngine.cpp | 143+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/Nio/JackEngine.h | 9++++-----
Msrc/Nio/MidiIn.cpp | 103+++++++++++++++++++++++++++----------------------------------------------------
Msrc/Nio/MidiIn.h | 12++++++------
Msrc/Nio/NulEngine.cpp | 94++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/Nio/NulEngine.h | 4++--
Msrc/Nio/OssEngine.cpp | 195+++++++++++++++++++++++++++++++------------------------------------------------
Msrc/Nio/OssEngine.h | 13++++++-------
Msrc/Nio/OutMgr.cpp | 134++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/Nio/OutMgr.h | 14+++++++++++++-
Msrc/Nio/SafeQueue.cpp | 7++++++-
Msrc/Nio/SafeQueue.h | 5++++-
Msrc/Nio/WavEngine.cpp | 41+++++------------------------------------
Msrc/Nio/WavEngine.h | 6++++--
Msrc/UI/NioUI.cpp | 53+++++++++++++++++++++++++++++++++++++++++++----------
Msrc/UI/NioUI.h | 12+++++++++++-
Msrc/main.cpp | 1+
29 files changed, 693 insertions(+), 662 deletions(-)

diff --git a/src/Misc/Util.h b/src/Misc/Util.h @@ -69,5 +69,11 @@ T stringTo(const char *x) return ans; } +template <class T> +T limit(T val, T min, T max) +{ + return (val < min ? min : (val > max ? max : val)); +} + #endif diff --git a/src/Nio/AlsaEngine.cpp b/src/Nio/AlsaEngine.cpp @@ -33,9 +33,7 @@ AlsaEngine::AlsaEngine(OutMgr *out) { name = "ALSA"; audio.handle = NULL; - audio.en = true; - midi.en = true; midi.handle = NULL; midi.alsaId = -1; midi.pThread = 0; @@ -61,62 +59,41 @@ void *AlsaEngine::AudioThread() bool AlsaEngine::Start() { - if(enabled()) - return true; - enabled = true; - if(audio.en) - openAudio(); - if(midi.en) - openMidi(); - - return true; + return openAudio() && openMidi(); } void AlsaEngine::Stop() { - if(!enabled()) - return; - enabled = false; - - + if(getMidiEn()) + setMidiEn(false); + if(getAudioEn()) + setAudioEn(false); } void AlsaEngine::setMidiEn(bool nval) { - midi.en = nval; - if(enabled()) { - if(nval) - openMidi(); - else - stopMidi(); - } + if(nval) + openMidi(); + else + stopMidi(); } bool AlsaEngine::getMidiEn() const { - if(enabled()) - return midi.handle; - else - return midi.en; + return midi.handle; } void AlsaEngine::setAudioEn(bool nval) { - audio.en = nval; - if(enabled()) { - if(nval) - openAudio(); - else - stopAudio(); - } + if(nval) + openAudio(); + else + stopAudio(); } bool AlsaEngine::getAudioEn() const { - if(enabled()) - return audio.handle; - else - return audio.en; + return audio.handle; } void *AlsaEngine::_MidiThread(void *arg) @@ -130,91 +107,91 @@ void *AlsaEngine::MidiThread(void) snd_seq_event_t *event; MidiEvent ev; set_realtime(); - while (enabled()) + while (snd_seq_event_input(midi.handle, &event) > 0) { - while (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; + switch (event->type) { - //ensure ev is empty - ev.channel = 0; - ev.num = 0; - ev.value = 0; - ev.type = 0; - - if (!event) - continue; - switch (event->type) - { - case SND_SEQ_EVENT_NOTEON: - if (event->data.note.note) - { - 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: + case SND_SEQ_EVENT_NOTEON: + if (event->data.note.note) + { 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: - 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: - 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 - ev.type = M_CONTROLLER; - ev.channel = event->data.control.channel; - ev.num = C_resetallcontrollers; - ev.value = 0; + ev.value = event->data.note.velocity; sysIn->putEvent(ev); - break; - - case SND_SEQ_EVENT_PORT_SUBSCRIBED: // ports connected - if (true) - cout << "Info, alsa midi port connected" << endl; - break; - - case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // ports disconnected - if (true) - cout << "Info, alsa midi port disconnected" << endl; - break; - - case SND_SEQ_EVENT_SYSEX: // system exclusive - case SND_SEQ_EVENT_SENSING: // midi device still there - break; - - default: - if (true) - cout << "Info, other non-handled midi event, type: " - << (int)event->type << endl; - break; - } - snd_seq_free_event(event); + } + break; + + case SND_SEQ_EVENT_NOTEOFF: + 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: + 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: + 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 + 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 + if (true) + cout << "Info, alsa midi port connected" << endl; + break; + + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // ports disconnected + if (true) + cout << "Info, alsa midi port disconnected" << endl; + break; + + case SND_SEQ_EVENT_SYSEX: // system exclusive + case SND_SEQ_EVENT_SENSING: // midi device still there + break; + + default: + if (true) + cout << "Info, other non-handled midi event, type: " + << (int)event->type << endl; + break; } + snd_seq_free_event(event); } return NULL; } bool AlsaEngine::openMidi() { + if(getMidiEn()) + return true; + int alsaport; midi.handle = NULL; @@ -242,11 +219,15 @@ bool AlsaEngine::openMidi() void AlsaEngine::stopMidi() { + if(!getMidiEn()) + return; + + snd_seq_t *handle = midi.handle; if (NULL != midi.handle && midi.pThread) pthread_cancel(midi.pThread); midi.handle = NULL; - if(midi.handle) - snd_seq_close(midi.handle); + if(handle) + snd_seq_close(handle); } const short *AlsaEngine::interleave(const Stereo<Sample> smps)const @@ -268,6 +249,9 @@ const short *AlsaEngine::interleave(const Stereo<Sample> smps)const bool AlsaEngine::openAudio() { + if(getAudioEn()) + return true; + int rc = 0; /* Open PCM device for playback. */ audio.handle=NULL; @@ -288,10 +272,9 @@ bool AlsaEngine::openAudio() /* Set the desired hardware parameters. */ -#warning TODO Make Access noninterleaved /* Interleaved mode */ snd_pcm_hw_params_set_access(audio.handle, audio.params, - SND_PCM_ACCESS_RW_INTERLEAVED); + SND_PCM_ACCESS_RW_NONINTERLEAVED); /* Signed 16-bit little-endian format */ snd_pcm_hw_params_set_format(audio.handle, audio.params, @@ -333,6 +316,9 @@ bool AlsaEngine::openAudio() void AlsaEngine::stopAudio() { + if(!getAudioEn()) + return; + snd_pcm_t *handle = audio.handle; audio.handle = NULL; pthread_join(audio.pThread, NULL); diff --git a/src/Nio/AlsaEngine.h b/src/Nio/AlsaEngine.h @@ -64,12 +64,9 @@ class AlsaEngine : public AudioOut, MidiIn snd_seq_t *handle; int alsaId; pthread_t pThread; - bool en; } midi; struct { - bool en; - bool run; snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned int sampleRate; diff --git a/src/Nio/AudioOut.cpp b/src/Nio/AudioOut.cpp @@ -22,134 +22,168 @@ #include <iostream> #include <cstring> +#include "SafeQueue.h" using namespace std; +#include "OutMgr.h" #include "../Misc/Master.h" #include "AudioOut.h" -AudioOut::AudioOut(OutMgr *out) +struct AudioOut::Data +{ + Data(OutMgr *out); + + int samplerate; + int bufferSize; + + SafeQueue<Stereo<Sample> > outBuf; + pthread_mutex_t outBuf_mutex; + pthread_cond_t outBuf_cv; + + /**used for taking in samples with a different + * samplerate/buffersize*/ + Stereo<Sample> partialIn; + bool usePartial; + Stereo<Sample> current;/**<used for xrun defence*/ + + //The number of Samples that are used to buffer + //Note: there is some undetermined behavior when: + //sampleRate!=SAMPLE_RATE || bufferSize!=SOUND_BUFFER_SIZE + unsigned int buffering; + + OutMgr *manager; +}; + +AudioOut::Data::Data(OutMgr *out) :samplerate(SAMPLE_RATE),bufferSize(SOUND_BUFFER_SIZE), - usePartial(false),current(Sample(SOUND_BUFFER_SIZE,0.0)), - buffering(6),manager(out) + outBuf(100),usePartial(false), + current(Sample(SOUND_BUFFER_SIZE,0.0)),buffering(6), + manager(out) +{} + +AudioOut::AudioOut(OutMgr *out) + :dat(new Data(out)) { - pthread_mutex_init(&outBuf_mutex, NULL); - pthread_cond_init(&outBuf_cv, NULL); + pthread_mutex_init(&dat->outBuf_mutex, NULL); + pthread_cond_init(&dat->outBuf_cv, NULL); } AudioOut::~AudioOut() { - pthread_mutex_destroy(&outBuf_mutex); - pthread_cond_destroy(&outBuf_cv); + pthread_mutex_destroy(&dat->outBuf_mutex); + pthread_cond_destroy(&dat->outBuf_cv); + delete dat; } void AudioOut::out(Stereo<Sample> smps) { - pthread_mutex_lock(&outBuf_mutex); - if(samplerate != SAMPLE_RATE) { //we need to resample - smps.l().resample(SAMPLE_RATE,samplerate); - smps.r().resample(SAMPLE_RATE,samplerate); + pthread_mutex_lock(&dat->outBuf_mutex); + if(dat->samplerate != SAMPLE_RATE) { //we need to resample + smps.l().resample(SAMPLE_RATE,dat->samplerate); + smps.r().resample(SAMPLE_RATE,dat->samplerate); } - if(usePartial) { //we have a partial to use - smps.l() = partialIn.l().append(smps.l()); - smps.r() = partialIn.r().append(smps.r()); + if(dat->usePartial) { //we have a partial to use + smps.l() = dat->partialIn.l().append(smps.l()); + smps.r() = dat->partialIn.r().append(smps.r()); } - if(smps.l().size() == bufferSize) { //sized just right - outBuf.push(smps); - usePartial = false; - pthread_cond_signal(&outBuf_cv); + if(smps.l().size() == dat->bufferSize) { //sized just right + dat->outBuf.push(smps); + dat->usePartial = false; + pthread_cond_signal(&dat->outBuf_cv); } - else if(smps.l().size() > bufferSize) { //store overflow + else if(smps.l().size() > dat->bufferSize) { //store overflow - while(smps.l().size() > bufferSize) { - outBuf.push(Stereo<Sample>(smps.l().subSample(0,bufferSize), - smps.r().subSample(0,bufferSize))); - smps = Stereo<Sample>(smps.l().subSample(bufferSize,smps.l().size()), - smps.r().subSample(bufferSize,smps.r().size())); + while(smps.l().size() > dat->bufferSize) { + dat->outBuf.push(Stereo<Sample>(smps.l().subSample(0,dat->bufferSize), + smps.r().subSample(0,dat->bufferSize))); + smps = Stereo<Sample>(smps.l().subSample(dat->bufferSize,smps.l().size()), + smps.r().subSample(dat->bufferSize,smps.r().size())); } - if(smps.l().size() == bufferSize) { //no partial - outBuf.push(smps); - usePartial = false; + if(smps.l().size() == dat->bufferSize) { //no partial + dat->outBuf.push(smps); + dat->usePartial = false; } else { //partial - partialIn = smps; - usePartial = true; + dat->partialIn = smps; + dat->usePartial = true; } - pthread_cond_signal(&outBuf_cv); + pthread_cond_signal(&dat->outBuf_cv); } else { //underflow - partialIn = smps; - usePartial = true; + dat->partialIn = smps; + dat->usePartial = true; } - pthread_mutex_unlock(&outBuf_mutex); + pthread_mutex_unlock(&dat->outBuf_mutex); } void AudioOut::setSamplerate(int _samplerate) { - pthread_mutex_lock(&outBuf_mutex); - usePartial = false; - samplerate = _samplerate; - //hm, the queue does not have a clear - while(!outBuf.empty()) - outBuf.pop(); - pthread_mutex_unlock(&outBuf_mutex); + pthread_mutex_lock(&dat->outBuf_mutex); + dat->usePartial = false; + dat->samplerate = _samplerate; + dat->outBuf.clear(); + pthread_mutex_unlock(&dat->outBuf_mutex); } void AudioOut::setBufferSize(int _bufferSize) { - pthread_mutex_lock(&outBuf_mutex); - usePartial = false; - bufferSize = _bufferSize; - //hm, the queue does not have a clear - while(!outBuf.empty()) - outBuf.pop(); - pthread_mutex_unlock(&outBuf_mutex); -}; + pthread_mutex_lock(&dat->outBuf_mutex); + dat->usePartial = false; + dat->bufferSize = _bufferSize; + dat->outBuf.clear(); + pthread_mutex_unlock(&dat->outBuf_mutex); +} void AudioOut::bufferingSize(int nBuffering) { - buffering = nBuffering; + dat->buffering = nBuffering; } int AudioOut::bufferingSize() { - return buffering; + return dat->buffering; } - -const Stereo<Sample> AudioOut::getNext() +const Stereo<Sample> AudioOut::getNext(bool wait) { - const unsigned int BUFF_SIZE = buffering; + const unsigned int BUFF_SIZE = dat->buffering; Stereo<Sample> ans; - pthread_mutex_lock(&outBuf_mutex); - bool isEmpty = outBuf.empty(); - pthread_mutex_unlock(&outBuf_mutex); + pthread_mutex_lock(&dat->outBuf_mutex); + bool isEmpty = !dat->outBuf.size(); + pthread_mutex_unlock(&dat->outBuf_mutex); if(isEmpty)//fetch samples if possible { - if((unsigned int)manager->getRunning() < BUFF_SIZE) - manager->requestSamples(BUFF_SIZE-manager->getRunning()); + if((unsigned int)dat->manager->getRunning() < BUFF_SIZE) + dat->manager->requestSamples(BUFF_SIZE-dat->manager->getRunning()); if(true) cout << "-----------------Starvation------------------"<< endl; - return current; - } - else - { - pthread_mutex_lock(&outBuf_mutex); - ans = outBuf.front(); - outBuf.pop(); - if(outBuf.size()+manager->getRunning()<BUFF_SIZE) - manager->requestSamples(BUFF_SIZE - (outBuf.size() - + manager->getRunning())); - if(false) - cout << "AudioOut "<< outBuf.size()<< '+' << manager->getRunning() << endl; - pthread_mutex_unlock(&outBuf_mutex); + if(wait) + { + pthread_mutex_lock(&dat->outBuf_mutex); + pthread_cond_wait(&dat->outBuf_cv,&dat->outBuf_mutex); + pthread_mutex_unlock(&dat->outBuf_mutex); + //now the code has a sample to fetch + } + else + return dat->current; } - current=ans; + + pthread_mutex_lock(&dat->outBuf_mutex); + dat->outBuf.pop(ans); + if(dat->outBuf.size()+dat->manager->getRunning()<BUFF_SIZE) + dat->manager->requestSamples(BUFF_SIZE - (dat->outBuf.size() + + dat->manager->getRunning())); + if(false) + cout << "AudioOut "<< dat->outBuf.size()<< '+' << dat->manager->getRunning() << endl; + pthread_mutex_unlock(&dat->outBuf_mutex); + + dat->current = ans; return ans; } diff --git a/src/Nio/AudioOut.h b/src/Nio/AudioOut.h @@ -25,16 +25,12 @@ #include "../Misc/Stereo.h" #include "../Samples/Sample.h" -#include <queue> -#include <pthread.h> -#include "OutMgr.h" -#include "../Misc/Atomic.h" #include "Engine.h" class AudioOut : public virtual Engine { public: - AudioOut(OutMgr *out); + AudioOut(class OutMgr *out); virtual ~AudioOut(); /**Give the Driver Samples to process*/ @@ -60,29 +56,11 @@ class AudioOut : public virtual Engine void putBack(const Stereo<Sample> smp); /**Get the next sample for output. * (has nsamples sampled at a rate of samplerate)*/ - virtual const Stereo<Sample> getNext(); + const Stereo<Sample> getNext(bool wait = false); - int samplerate; - int bufferSize; - - std::queue<Stereo<Sample> > outBuf; - pthread_mutex_t outBuf_mutex; - pthread_cond_t outBuf_cv; - - /**used for taking in samples with a different - * samplerate/buffersize*/ - Stereo<Sample> partialIn; - bool usePartial; - Stereo<Sample> current;/**<used for xrun defence*/ - - //The number of Samples that are used to buffer - //Note: there is some undetermined behavior when: - //sampleRate!=SAMPLE_RATE || bufferSize!=SOUND_BUFFER_SIZE - unsigned int buffering; - - OutMgr *manager; - //thread resources - pthread_t pThread; + //using opaque pointer to reduce compile times + struct Data; + Data *dat; }; #endif diff --git a/src/Nio/CMakeLists.txt b/src/Nio/CMakeLists.txt @@ -7,6 +7,7 @@ set(zynaddsubfx_nio_SRCS WavEngine.cpp NulEngine.cpp AudioOut.cpp + MidiIn.cpp OutMgr.cpp InMgr.cpp Engine.cpp diff --git a/src/Nio/Engine.cpp b/src/Nio/Engine.cpp @@ -22,13 +22,8 @@ #include "Engine.h" Engine::Engine() - :enabled(false) {}; Engine::~Engine() {}; -bool Engine::isRunning() const -{ - return enabled(); -} diff --git a/src/Nio/Engine.h b/src/Nio/Engine.h @@ -23,7 +23,6 @@ #ifndef ENGINE_H #define ENGINE_H #include <string> -#include "../Misc/Atomic.h" /**Marker for input/output driver*/ class Engine { @@ -31,17 +30,12 @@ class Engine Engine(); virtual ~Engine(); - /**Start the Driver + /**Start the Driver with all capabilities * @return true on success*/ virtual bool Start()=0; - /**Stop the Driver*/ + /**Completely stop the Driver*/ virtual void Stop()=0; - /**Retruns if engine is on*/ - bool isRunning() const; - std::string name; - protected: - Atomic<bool> enabled; }; #endif diff --git a/src/Nio/EngineMgr.cpp b/src/Nio/EngineMgr.cpp @@ -1,6 +1,7 @@ #include "EngineMgr.h" #include <algorithm> #include <iostream> +#include "OutMgr.h" #include "AudioOut.h" #include "NulEngine.h" #if OSS @@ -76,3 +77,10 @@ Engine *EngineMgr::getEng(string name) return NULL; } +void EngineMgr::stop() +{ + for(list<Engine*>::iterator itr = engines.begin(); + itr != engines.end(); ++itr) + (*itr)->Stop(); +} + diff --git a/src/Nio/EngineMgr.h b/src/Nio/EngineMgr.h @@ -10,9 +10,8 @@ class MidiIn; class AudioOut; class OutMgr; /**Container/Owner of the long lived Engines*/ -class EngineMgr +struct EngineMgr { - public: EngineMgr(); ~EngineMgr(); @@ -22,15 +21,12 @@ class EngineMgr */ Engine *getEng(std::string name); - private: + /**Stop all engines*/ + void stop(); + std::list<Engine *> engines; Engine *defaultEng;/**<The default output*/ - - //Engine Manager user - //[if there is another 'user, just make this class a struct] - friend class OutMgr; - friend class NioUI; }; extern EngineMgr *sysEngine; diff --git a/src/Nio/InMgr.cpp b/src/Nio/InMgr.cpp @@ -1,4 +1,7 @@ #include "InMgr.h" +#include "MidiIn.h" +#include "EngineMgr.h" +#include "../Misc/Master.h" #include <iostream> using namespace std; @@ -86,3 +89,35 @@ void *InMgr::inputThread() return NULL; } +bool InMgr::setSource(string name) +{ + MidiIn *src = NULL; + for(list<Engine*>::iterator itr = sysEngine->engines.begin(); + itr != sysEngine->engines.end(); ++itr) { + MidiIn *in = dynamic_cast<MidiIn *>(*itr); + if(in) { + if(in->name == name) + src = in; + else + in->setMidiEn(false); + } + } + + if(!src) + return false; + + src->setMidiEn(true); + + return src->getMidiEn(); +} + +string InMgr::getSource() const +{ + for(list<Engine*>::iterator itr = sysEngine->engines.begin(); + itr != sysEngine->engines.end(); ++itr) { + MidiIn *in = dynamic_cast<MidiIn *>(*itr); + if(in && in->getMidiEn()) + return in->name; + } +} + diff --git a/src/Nio/InMgr.h b/src/Nio/InMgr.h @@ -4,7 +4,6 @@ #include <string> #include <pthread.h> #include <semaphore.h> -#include "../Misc/Master.h" #include "../Misc/Atomic.h" #include "SafeQueue.h" @@ -37,6 +36,10 @@ class InMgr void *inputThread(); + bool setSource(std::string name); + + std::string getSource() const; + private: SafeQueue<MidiEvent> queue; sem_t work; diff --git a/src/Nio/JackEngine.cpp b/src/Nio/JackEngine.cpp @@ -33,8 +33,6 @@ using namespace std; JackEngine::JackEngine(OutMgr *out) :AudioOut(out), jackClient(NULL) { - midi.en = true; - audio.en = true; name = "JACK"; audio.jackSamplerate = 0; audio.jackNframes = 0; @@ -47,46 +45,35 @@ JackEngine::JackEngine(OutMgr *out) bool JackEngine::connectServer(string server) { - //temporary (move to a higher level configuation) bool autostart_jack = true; + if(jackClient) + return true; - if (NULL == jackClient) // ie, not already connected - { - string clientname = "zynaddsubfx"; - jack_status_t jackstatus; - bool use_server_name = server.size() && server.compare("default") != 0; - jack_options_t jopts = (jack_options_t) - (((use_server_name) ? JackServerName : JackNullOption) - | ((autostart_jack) ? JackNullOption : JackNoStartServer)); - if (use_server_name) - jackClient = jack_client_open(clientname.c_str(), jopts, &jackstatus, - server.c_str()); - else - jackClient = jack_client_open(clientname.c_str(), jopts, &jackstatus); - if (NULL != jackClient) - return true; - else - cerr << "Error, failed to open jack client on server: " << server - << " status " << jackstatus << endl; - return false; - } + string clientname = "zynaddsubfx"; + jack_status_t jackstatus; + bool use_server_name = server.size() && server.compare("default") != 0; + jack_options_t jopts = (jack_options_t) + (((use_server_name) ? JackServerName : JackNullOption) + | ((autostart_jack) ? JackNullOption : JackNoStartServer)); + if (use_server_name) + jackClient = jack_client_open(clientname.c_str(), jopts, &jackstatus, + server.c_str()); + else + jackClient = jack_client_open(clientname.c_str(), jopts, &jackstatus); + if (NULL != jackClient) + return true; + else + cerr << "Error, failed to open jack client on server: " << server + << " status " << jackstatus << endl; + return false; + return true; } -bool JackEngine::Start() +bool JackEngine::connectJack() { - cout << "Starting Jack" << endl; - if(enabled()) - return true; - - enabled = true; - if(!connectServer("")) - goto bail_out; - if(midi.en) - openMidi(); - if(audio.en) - openAudio(); + connectServer(""); if (NULL != jackClient) { setBufferSize(jack_get_buffer_size(jackClient)); @@ -100,78 +87,76 @@ bool JackEngine::Start() if (jack_set_process_callback(jackClient, _processCallback, this)) { cerr << "Error, JackEngine failed to set process callback" << endl; - goto bail_out; + return false; } if (jack_activate(jackClient)) { cerr << "Error, failed to activate jack client" << endl;; - goto bail_out; + return false; } return true; } else cerr << "Error, NULL jackClient through Start()" << endl; -bail_out: - Stop(); return false; } -void JackEngine::Stop() +void JackEngine::disconnectJack() { - cout << "Stopping Jack" << endl; - if(!enabled()) - return; - enabled = false; - if (jackClient) - { - stopMidi(); - stopAudio(); + if(jackClient) { jack_client_close(jackClient); jackClient = NULL; } } +bool JackEngine::Start() +{ + return openMidi() && openAudio(); +} + +void JackEngine::Stop() +{ + stopMidi(); + stopAudio(); +} + void JackEngine::setMidiEn(bool nval) { - midi.en = nval; - if(enabled()) { //lets rebind the ports - if(nval) - openMidi(); - else - stopMidi(); - } + if(nval) + openMidi(); + else + stopMidi(); } bool JackEngine::getMidiEn() const { - if(enabled()) - return midi.inport; - else - return midi.en; + return midi.inport; } void JackEngine::setAudioEn(bool nval) { - audio.en = nval; - if(enabled()) { //lets rebind the ports - if(nval) - openAudio(); - else - stopAudio(); - } + if(nval) + openAudio(); + else + stopAudio(); } bool JackEngine::getAudioEn() const { - if(enabled()) - return audio.ports[0]; - else - return audio.en; + return audio.ports[0]; } bool JackEngine::openAudio() { + if(getAudioEn()) + return true; + + if(!getMidiEn()) + if(!connectJack()) + return false; + + const char *portnames[] = { "left", "right" }; for (int port = 0; port < 2; ++port) { @@ -199,13 +184,22 @@ void JackEngine::stopAudio() if (NULL != port) jack_port_unregister(jackClient, port); } + if(!getMidiEn()) + disconnectJack(); } bool JackEngine::openMidi() { - return midi.inport = jack_port_register(jackClient, "midi_input", - JACK_DEFAULT_MIDI_TYPE, - JackPortIsInput | JackPortIsTerminal, 0); + if(getMidiEn()) + return true; + if(!getAudioEn()) + if(!connectJack()) + return false; + + midi.inport = jack_port_register(jackClient, "midi_input", + JACK_DEFAULT_MIDI_TYPE, + JackPortIsInput | JackPortIsTerminal, 0); + return midi.inport; } void JackEngine::stopMidi() @@ -214,6 +208,9 @@ void JackEngine::stopMidi() midi.inport = NULL; if(port) jack_port_unregister(jackClient, port); + + if(!getAudioEn()) + disconnectJack(); } int JackEngine::clientId() diff --git a/src/Nio/JackEngine.h b/src/Nio/JackEngine.h @@ -37,7 +37,6 @@ class JackEngine : public AudioOut, MidiIn JackEngine(OutMgr *out); ~JackEngine() { }; - bool setServer(std::string server); bool Start(); void Stop(); @@ -65,6 +64,8 @@ class JackEngine : public AudioOut, MidiIn private: bool connectServer(std::string server); + bool connectJack(); + void disconnectJack(); bool openAudio(); void stopAudio(); bool processAudio(jack_nframes_t nframes); @@ -72,16 +73,14 @@ class JackEngine : public AudioOut, MidiIn void stopMidi(); jack_client_t *jackClient; - struct { - bool en; + struct audio{ unsigned int jackSamplerate; unsigned int jackNframes; jack_port_t *ports[2]; jsample_t *portBuffs[2]; } audio; - struct { + struct midi{ jack_port_t *inport; - bool en; } midi; void handleMidi(unsigned long frames); diff --git a/src/Nio/MidiIn.cpp b/src/Nio/MidiIn.cpp @@ -21,75 +21,42 @@ */ #include "MidiIn.h" +#include "../globals.h" +#include "InMgr.h" -int MidiIn::getcontroller(unsigned char b) +void MidiIn::midiProcess(unsigned char head, unsigned char num, unsigned char value) { - /**\todo there might be a better way to do this*/ - int ctl = C_NULL; - switch(b) { - case 1: - ctl = C_modwheel; //Modulation Wheel - break; - case 7: - ctl = C_volume; //Volume - break; - case 10: - ctl = C_panning; //Panning - break; - case 11: - ctl = C_expression; //Expression - break; - case 64: - ctl = C_sustain; //Sustain pedal - break; - case 65: - ctl = C_portamento; //Portamento - break; - case 71: - ctl = C_filterq; //Filter Q (Sound Timbre) - break; - case 74: - ctl = C_filtercutoff; //Filter Cutoff (Brightness) - break; - case 75: - ctl = C_bandwidth; //BandWidth - break; - case 76: - ctl = C_fmamp; //FM amplitude - break; - case 77: - ctl = C_resonance_center; //Resonance Center Frequency - break; - case 78: - ctl = C_resonance_bandwidth; //Resonance Bandwith - break; - case 120: - ctl = C_allsoundsoff; //All Sounds OFF - break; - case 121: - ctl = C_resetallcontrollers; //Reset All Controllers - break; - case 123: - ctl = C_allnotesoff; //All Notes OFF - break; - //RPN and NRPN - case 0x06: - ctl = C_dataentryhi; //Data Entry (Coarse) - break; - case 0x26: - ctl = C_dataentrylo; //Data Entry (Fine) - break; - case 99: - ctl = C_nrpnhi; //NRPN (Coarse) - break; - case 98: - ctl = C_nrpnlo; //NRPN (Fine) - break; - default: - ctl = C_NULL; //unknown controller - //fprintf(stderr,"Controller=%d , par=%d\n",midievent->data.control.param,cmdparams[1]); - break; + MidiEvent ev; + unsigned char chan = head & 0x0f; + switch(head & 0xf0) + { + case 0x80: //Note Off + ev.type = M_NOTE; + ev.channel = chan; + ev.num = num; + ev.value = 0; + sysIn->putEvent(ev); + break; + case 0x90: //Note On + ev.type = M_NOTE; + ev.channel = chan; + ev.num = num; + ev.value = value; + sysIn->putEvent(ev); + break; + case 0xb0: //Controller + ev.type = M_CONTROLLER; + ev.channel = chan; + ev.num = num; + ev.value = value; + sysIn->putEvent(ev); + break; + case 0xe0: //Pitch Wheel + ev.type = M_CONTROLLER; + ev.channel = chan; + ev.num = C_pitchwheel; + ev.value = (num + value * (int) 128) - 8192; + sysIn->putEvent(ev); + break; } - return ctl; } - diff --git a/src/Nio/MidiIn.h b/src/Nio/MidiIn.h @@ -28,13 +28,13 @@ #include "Engine.h" /**This class is inherited by all the Midi input classes*/ -class MidiIn : public virtual Engine +struct MidiIn : public virtual Engine { - public: - static int getcontroller(unsigned char b); - - virtual void setMidiEn(bool nval)=0; - virtual bool getMidiEn() const=0; + /**Enables or disables driver based upon value*/ + virtual void setMidiEn(bool nval)=0; + /**Returns if driver is initialized*/ + virtual bool getMidiEn() const=0; + static void midiProcess(unsigned char head, unsigned char num, unsigned char value); }; #endif diff --git a/src/Nio/NulEngine.cpp b/src/Nio/NulEngine.cpp @@ -28,88 +28,88 @@ using namespace std; NulEngine::NulEngine(OutMgr *out) - :AudioOut(out) + :AudioOut(out), pThread(NULL) { name = "NULL"; playing_until.tv_sec = 0; playing_until.tv_usec = 0; } - - void *NulEngine::_AudioThread(void *arg) { return (static_cast<NulEngine*>(arg))->AudioThread(); } - void *NulEngine::AudioThread() { - while (enabled()) + while(pThread) { const Stereo<Sample> smps = getNext(); - dummyOut(); - } - pthread_exit(NULL); -} -void NulEngine::dummyOut() -{ - struct timeval now; - int remaining = 0; - gettimeofday(&now, NULL); - if((playing_until.tv_usec == 0) && (playing_until.tv_sec == 0)) { - playing_until.tv_usec = now.tv_usec; - playing_until.tv_sec = now.tv_sec; - } - else { - remaining = (playing_until.tv_usec - now.tv_usec) - + (playing_until.tv_sec - now.tv_sec) * 1000000; - if(remaining > 10000) //Don't sleep() less than 10ms. - //This will add latency... - usleep(remaining - 10000); + struct timeval now; + int remaining = 0; + gettimeofday(&now, NULL); + if((playing_until.tv_usec == 0) && (playing_until.tv_sec == 0)) { + playing_until.tv_usec = now.tv_usec; + playing_until.tv_sec = now.tv_sec; + } + else { + remaining = (playing_until.tv_usec - now.tv_usec) + + (playing_until.tv_sec - now.tv_sec) * 1000000; + if(remaining > 10000) //Don't sleep() less than 10ms. + //This will add latency... + usleep(remaining - 10000); + if(remaining < 0) + cerr << "WARNING - too late" << endl; + } + playing_until.tv_usec += SOUND_BUFFER_SIZE * 1000000 / SAMPLE_RATE; if(remaining < 0) - cerr << "WARNING - too late" << endl; + playing_until.tv_usec -= remaining; + playing_until.tv_sec += playing_until.tv_usec / 1000000; + playing_until.tv_usec %= 1000000; } - playing_until.tv_usec += SOUND_BUFFER_SIZE * 1000000 / SAMPLE_RATE; - if(remaining < 0) - playing_until.tv_usec -= remaining; - playing_until.tv_sec += playing_until.tv_usec / 1000000; - playing_until.tv_usec %= 1000000; + pthread_exit(NULL); } - NulEngine::~NulEngine() { } - bool NulEngine::Start() { - if(enabled()) - return true; - pthread_attr_t attr; - enabled = true; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - pthread_create(&pThread, &attr, _AudioThread, this); - - return true; + setAudioEn(true); + return getAudioEn(); } void NulEngine::Stop() { - if(!enabled()) - return; - enabled = false; - pthread_join(pThread, NULL); + setAudioEn(false); } void NulEngine::setAudioEn(bool nval) -{} +{ + if(nval) { + if(!getAudioEn()) { + pthread_t *thread = new pthread_t; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pThread = thread; + pthread_create(pThread, &attr, _AudioThread, this); + } + } + else { + if(getAudioEn()) { + pthread_t *thread = pThread; + pThread = NULL; + pthread_join(*thread, NULL); + delete thread; + } + } +} bool NulEngine::getAudioEn() const { - return true; + return pThread; } diff --git a/src/Nio/NulEngine.h b/src/Nio/NulEngine.h @@ -24,6 +24,7 @@ #define NUL_ENGINE_H #include <sys/time.h> +#include <pthread.h> #include "../globals.h" #include "AudioOut.h" @@ -44,9 +45,8 @@ class NulEngine: public AudioOut static void *_AudioThread(void *arg); private: - bool en; - void dummyOut(); struct timeval playing_until; + pthread_t *pThread; }; #endif diff --git a/src/Nio/OssEngine.cpp b/src/Nio/OssEngine.cpp @@ -43,8 +43,8 @@ OssEngine::OssEngine(OutMgr *out) { name = "OSS"; - midi.en = true; - audio.en = true; + midi.handle = -1; + audio.handle = -1; audio.smps = new short[SOUND_BUFFER_SIZE * 2]; memset(audio.smps, 0, sizeof(short) * SOUND_BUFFER_SIZE * 2); @@ -58,144 +58,142 @@ OssEngine::~OssEngine() bool OssEngine::openAudio() { + if(audio.handle != -1) + return true; //already open + int snd_bitsize = 16; int snd_fragment = 0x00080009; //fragment size (?); int snd_stereo = 1; //stereo; int snd_format = AFMT_S16_LE; int snd_samplerate = SAMPLE_RATE;; - audio.snd_handle = open(config.cfg.LinuxOSSWaveOutDev, O_WRONLY, 0); - if(audio.snd_handle == -1) { + audio.handle = open(config.cfg.LinuxOSSWaveOutDev, O_WRONLY, 0); + if(audio.handle == -1) { cerr << "ERROR - I can't open the " << config.cfg.LinuxOSSWaveOutDev << '.' << endl; - stopAudio(); return false; } - ioctl(audio.snd_handle, SNDCTL_DSP_RESET, NULL); - ioctl(audio.snd_handle, SNDCTL_DSP_SETFMT, &snd_format); - ioctl(audio.snd_handle, SNDCTL_DSP_STEREO, &snd_stereo); - ioctl(audio.snd_handle, SNDCTL_DSP_SPEED, &snd_samplerate); - ioctl(audio.snd_handle, SNDCTL_DSP_SAMPLESIZE, &snd_bitsize); - ioctl(audio.snd_handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment); + ioctl(audio.handle, SNDCTL_DSP_RESET, NULL); + ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format); + ioctl(audio.handle, SNDCTL_DSP_STEREO, &snd_stereo); + ioctl(audio.handle, SNDCTL_DSP_SPEED, &snd_samplerate); + ioctl(audio.handle, SNDCTL_DSP_SAMPLESIZE, &snd_bitsize); + ioctl(audio.handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment); + + if(!getMidiEn()) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + engThread = new pthread_t; + pthread_create(engThread, &attr, _thread, this); + } return true; } void OssEngine::stopAudio() { - int handle = audio.snd_handle; + int handle = audio.handle; if(handle == -1) //already closed return; - audio.snd_handle = -1; + audio.handle = -1; + + if(!getMidiEn()) + pthread_join(*engThread, NULL); + delete engThread; + engThread = NULL; + close(handle); } bool OssEngine::Start() { - if(enabled()) - return true; - enabled = true; - bool good = true; - if(audio.en) - if(!openAudio()) { - cerr << "Failed to open OSS audio" << endl; - good = false; - } - - if(midi.en) - if(!openMidi()) { - cerr << "Failed to open OSS midi" << endl; - good = false; - } - if(!good) { - return false; + if(!openAudio()) { + cerr << "Failed to open OSS audio" << endl; + good = false; } - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - engThread = new pthread_t; - pthread_create(engThread, &attr, _thread, this); + if(!openMidi()) { + cerr << "Failed to open OSS midi" << endl; + good = false; + } - return true; + return good; } void OssEngine::Stop() { - if(!enabled()) - return; - enabled = false; stopAudio(); stopMidi(); - if(engThread) - pthread_join(*engThread, NULL); - delete engThread; - engThread = NULL; } void OssEngine::setMidiEn(bool nval) { - midi.en = nval; - if(enabled()) { - if(nval) - openMidi(); - else - stopMidi(); - } + if(nval) + openMidi(); + else + stopMidi(); } bool OssEngine::getMidiEn() const { - if(enabled()) - return midi.handle != -1; - else - return midi.en; + return midi.handle != -1; } void OssEngine::setAudioEn(bool nval) { - audio.en = nval; - if(enabled()) { //lets rebind the ports - if(nval) - openAudio(); - else - stopAudio(); - } + if(nval) + openAudio(); + else + stopAudio(); } bool OssEngine::getAudioEn() const { - if(enabled()) - return audio.snd_handle != -1; - else - return audio.en; + return audio.handle != -1; } bool OssEngine::openMidi() { - midi.handle = open(config.cfg.LinuxOSSSeqInDev, O_RDONLY, 0); + int handle = midi.handle; + if(handle != -1) + return true;//already open - if(-1 == midi.handle) { - stopMidi(); - midi.run = false; + handle = open(config.cfg.LinuxOSSSeqInDev, O_RDONLY, 0); + + if(-1 == handle) { return false; } + midi.handle = handle; + + if(!getAudioEn()) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + engThread = new pthread_t; + pthread_create(engThread, &attr, _thread, this); + } - midi.run = true; return true; } void OssEngine::stopMidi() { - int tmp = midi.handle; - if(tmp == -1) //already closed + int handle = midi.handle; + if(handle == -1) //already closed return; - midi.run = false; midi.handle = -1; - close(tmp); + + if(!getAudioEn()) { + pthread_join(*engThread, NULL); + delete engThread; + engThread = NULL; + } + + close(handle); } void *OssEngine::_thread(void *arg) @@ -203,15 +201,13 @@ void *OssEngine::_thread(void *arg) return (static_cast<OssEngine*>(arg))->thread(); } - void *OssEngine::thread() { - MidiEvent ev; unsigned char tmp[4] = {0, 0, 0, 0}; set_realtime(); - while (midi.run || audio.snd_handle != -1) + while (getAudioEn() || getMidiEn()) { - if(audio.snd_handle != -1) + if(getAudioEn()) { const Stereo<Sample> smps = getNext(); @@ -234,15 +230,15 @@ void *OssEngine::thread() audio.smps[i * 2] = (short int) (l * 32767.0); audio.smps[i * 2 + 1] = (short int) (r * 32767.0); } - int handle = audio.snd_handle; + int handle = audio.handle; if(handle != -1) write(handle, audio.smps, SOUND_BUFFER_SIZE * 4); // *2 because is 16 bit, again * 2 because is stereo else break; } - //Collect up to 10 midi events - for (int k = 0; k < 10 && midi.run; ++k) { + //Collect up to 30 midi events + for (int k = 0; k < 30 && getMidiEn(); ++k) { getMidi(tmp); unsigned char type = tmp[0]; unsigned char header = tmp[1]; @@ -266,40 +262,3 @@ void OssEngine::getMidi(unsigned char *midiPtr) read(midi.handle, midiPtr, 4); } -void OssEngine::midiProcess(unsigned char head, unsigned char num, unsigned char value) -{ - MidiEvent ev; - unsigned char chan = head & 0x0f; - switch(head & 0xf0) - { - case 0x80: //Note Off - ev.type = M_NOTE; - ev.channel = chan; - ev.num = num; - ev.value = 0; - sysIn->putEvent(ev); - break; - case 0x90: //Note On - ev.type = M_NOTE; - ev.channel = chan; - ev.num = num; - ev.value = value; - sysIn->putEvent(ev); - break; - case 0xb0: //Controller - ev.type = M_CONTROLLER; - ev.channel = chan; - ev.num = num; - ev.value = value; - sysIn->putEvent(ev); - break; - case 0xe0: //Pitch Wheel - ev.type = M_CONTROLLER; - ev.channel = chan; - ev.num = C_pitchwheel; - ev.value = (num + value * (int) 128) - 8192; - sysIn->putEvent(ev); - break; - } -} - diff --git a/src/Nio/OssEngine.h b/src/Nio/OssEngine.h @@ -49,11 +49,14 @@ class OssEngine: public AudioOut, MidiIn static void *_thread(void *arg); private: + pthread_t *engThread; + //Audio bool openAudio(); void stopAudio(); - struct { - int snd_handle; + + struct audio{ + int handle; short int *smps; //Samples to be sent to soundcard bool en; } audio; @@ -61,13 +64,9 @@ class OssEngine: public AudioOut, MidiIn //Midi bool openMidi(); void stopMidi(); - void midiProcess(unsigned char head, unsigned char num, unsigned char value); - void getMidi(unsigned char *midiPtr); - pthread_t *engThread; - - struct { + struct midi{ int handle; bool en; bool run; diff --git a/src/Nio/OutMgr.cpp b/src/Nio/OutMgr.cpp @@ -27,10 +27,6 @@ OutMgr::OutMgr(Master *nmaster) OutMgr::~OutMgr() { - for(map<string,AudioOut*>::iterator itr = managedOuts.begin(); - itr != managedOuts.end(); ++itr) { - itr->second->Stop(); - } running = false; sem_post(&requested); @@ -40,11 +36,75 @@ OutMgr::~OutMgr() sem_destroy(&requested); } +void OutMgr::add(AudioOut *driver) +{ + pthread_mutex_lock(&mutex); + unmanagedOuts.push_back(driver); + if(running())//hotplug + driver->Start(); + pthread_mutex_unlock(&mutex); +} + +void OutMgr::remove(AudioOut *out) +{ + pthread_mutex_lock(&mutex); + unmanagedOuts.remove(out); + out->Stop();//tells engine to stop + + //gives a dummy sample to make sure it is not stuck + out->out(Stereo<Sample>(Sample(SOUND_BUFFER_SIZE, 0.0), + Sample(SOUND_BUFFER_SIZE, 0.0))); + pthread_mutex_unlock(&mutex); +} + +void OutMgr::requestSamples(unsigned int n) +{ + for(unsigned int i = 0; i < n; ++i) + sem_post(&requested); +} + +int OutMgr::getRunning() +{ + int tmp; + sem_getvalue(&requested, &tmp); + if(tmp < 0) + tmp = 0; + return tmp; +} + void *_outputThread(void *arg) { return (static_cast<OutMgr*>(arg))->outputThread(); } +void OutMgr::run() +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_create(&outThread, &attr, _outputThread, this); +} + +AudioOut *OutMgr::getOut(string name) +{ + return dynamic_cast<AudioOut *>(sysEngine->getEng(name)); +} + +string OutMgr::getDriver() const +{ + for(list<Engine*>::iterator itr = sysEngine->engines.begin(); + itr != sysEngine->engines.end(); ++itr) { + AudioOut *out = dynamic_cast<AudioOut *>(*itr); + if(out && out->getAudioEn()) + return out->name; + } +} + +bool OutMgr::setDriver(string name) +{ + return false; +} + void *OutMgr::outputThread() { defaultOut = dynamic_cast<AudioOut *>(sysEngine->defaultEng); @@ -69,7 +129,6 @@ void *OutMgr::outputThread() if(false) { cout << "Status: "; pthread_mutex_lock(&mutex); - cout << managedOuts.size() << "-"; cout << unmanagedOuts.size(); pthread_mutex_unlock(&mutex); cout << " outs, "; @@ -91,7 +150,7 @@ void *OutMgr::outputThread() for(list<Engine*>::iterator itr = sysEngine->engines.begin(); itr != sysEngine->engines.end(); ++itr) { AudioOut *out = dynamic_cast<AudioOut *>(*itr); - if(out && out->isRunning() && out->getAudioEn()) + if(out && out->getAudioEn()) out->out(smps); } @@ -109,52 +168,35 @@ void *OutMgr::outputThread() return NULL; } -void OutMgr::run() +bool OutMgr::setSink(string name) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - pthread_create(&outThread, &attr, _outputThread, this); -} - -AudioOut *OutMgr::getOut(string name) -{ - return dynamic_cast<AudioOut *>(sysEngine->getEng(name)); -} - -void OutMgr::add(AudioOut *driver) -{ - pthread_mutex_lock(&mutex); - unmanagedOuts.push_back(driver); - if(running())//hotplug - driver->Start(); - pthread_mutex_unlock(&mutex); -} + AudioOut *sink = NULL; + for(list<Engine*>::iterator itr = sysEngine->engines.begin(); + itr != sysEngine->engines.end(); ++itr) { + AudioOut *out = dynamic_cast<AudioOut *>(*itr); + if(out) { + if(out->name == name) + sink = out; + else + out->setAudioEn(false); + } + } -void OutMgr::remove(AudioOut *out) -{ - pthread_mutex_lock(&mutex); - unmanagedOuts.remove(out); - out->Stop();//tells engine to stop + if(!sink) + return false; - //gives a dummy sample to make sure it is not stuck - out->out(Stereo<Sample>(Sample(SOUND_BUFFER_SIZE, 0.0), - Sample(SOUND_BUFFER_SIZE, 0.0))); - pthread_mutex_unlock(&mutex); -} + sink->setAudioEn(true); -int OutMgr::getRunning() -{ - int tmp; - sem_getvalue(&requested, &tmp); - if(tmp < 0) - tmp = 0; - return tmp; + return sink->getAudioEn(); } -void OutMgr::requestSamples(unsigned int n) +string OutMgr::getSink() const { - for(unsigned int i = 0; i < n; ++i) - sem_post(&requested); + for(list<Engine*>::iterator itr = sysEngine->engines.begin(); + itr != sysEngine->engines.end(); ++itr) { + AudioOut *out = dynamic_cast<AudioOut *>(*itr); + if(out && out->getAudioEn()) + return out->name; + } } diff --git a/src/Nio/OutMgr.h b/src/Nio/OutMgr.h @@ -43,13 +43,25 @@ class OutMgr */ AudioOut *getOut(std::string name); + /**Sets the running driver by name + * @return true for successful insertion*/ + bool setDriver(std::string name); + + /**Gets the name of the first running driver + * @return if no running output, "" is returned + */ + std::string getDriver() const; + void *outputThread(); + + bool setSink(std::string name); + + std::string getSink() const; private: Atomic<bool> running; bool init; //should hold outputs here that exist for the life of the OutMgr - std::map<std::string,AudioOut *> managedOuts; AudioOut *defaultOut;/**<The default output*/ //should hold short lived, externally controlled Outputs (eg WavEngine) diff --git a/src/Nio/SafeQueue.cpp b/src/Nio/SafeQueue.cpp @@ -9,7 +9,7 @@ SafeQueue<T>::SafeQueue(size_t maxlen) template<class T> SafeQueue<T>::~SafeQueue() { - delete buffer; + delete[] buffer; } template<class T> @@ -76,3 +76,8 @@ int SafeQueue<T>::pop(T &out) return 0; } +template<class T> +void SafeQueue<T>::clear() +{ + readPtr = writePtr; +} diff --git a/src/Nio/SafeQueue.h b/src/Nio/SafeQueue.h @@ -1,7 +1,7 @@ #ifndef SAFEQUEUE_H #define SAFEQUEUE_H - +#include <cstdlib> /** * C++ thread safe lockless queue @@ -21,6 +21,9 @@ class SafeQueue int push(const T &in); int pop(T &out); + //clears reading space + void clear(); + private: unsigned int wSpace() const; unsigned int rSpace() const; diff --git a/src/Nio/WavEngine.cpp b/src/Nio/WavEngine.cpp @@ -20,11 +20,14 @@ #include <cstdio> #include <iostream> #include <cstdlib> +#include "SafeQueue.h" +#include "../Misc/Util.h" using namespace std; WavEngine::WavEngine(OutMgr *out, string filename, int samplerate, int channels) - :AudioOut(out), file(filename, samplerate, channels) + :AudioOut(out), file(filename, samplerate, channels), + enabled(false) { } @@ -57,48 +60,14 @@ void WavEngine::Stop() return; enabled = false; - //put something in the queue - pthread_mutex_lock(&outBuf_mutex); - outBuf.push(Stereo<Sample>(Sample(1,0.0),Sample(1,0.0))); - pthread_mutex_unlock(&outBuf_mutex); - - //make sure it moves - pthread_cond_signal(&outBuf_cv); - pthread_mutex_unlock(&outBuf_mutex); pthread_join(pThread, NULL); } -//lazy getter -const Stereo<Sample> WavEngine::getNext() -{ - Stereo<Sample> ans; - pthread_mutex_lock(&outBuf_mutex); - bool isEmpty = outBuf.empty(); - pthread_mutex_unlock(&outBuf_mutex); - if(isEmpty)//wait for samples - { - pthread_mutex_lock(&outBuf_mutex); - pthread_cond_wait(&outBuf_cv, &outBuf_mutex); - pthread_mutex_unlock(&outBuf_mutex); - } - pthread_mutex_lock(&outBuf_mutex); - ans = outBuf.front(); - outBuf.pop(); - pthread_mutex_unlock(&outBuf_mutex); - return ans; -} - void *WavEngine::_AudioThread(void *arg) { return (static_cast<WavEngine*>(arg))->AudioThread(); } -template <class T> -T limit(T val, T min, T max) -{ - return (val < min ? min : (val > max ? max : val)); -} - void *WavEngine::AudioThread() { short int *recordbuf_16bit = new short int [SOUND_BUFFER_SIZE*2]; @@ -107,7 +76,7 @@ void *WavEngine::AudioThread() while (enabled()) { - const Stereo<Sample> smps = getNext(); + const Stereo<Sample> smps = getNext(true); for(int i = 0; i < size; i++) { recordbuf_16bit[i*2] = limit((int)(smps.l()[i] * 32767.0), -32768, 32767); recordbuf_16bit[i*2+1] = limit((int)(smps.r()[i] * 32767.0), -32768, 32767); diff --git a/src/Nio/WavEngine.h b/src/Nio/WavEngine.h @@ -24,7 +24,9 @@ #define WAVENGINE_H #include "AudioOut.h" #include "../Misc/WavFile.h" +#include "../Misc/Atomic.h" #include <string> +#include <pthread.h> class WavEngine: public AudioOut { @@ -39,14 +41,14 @@ class WavEngine: public AudioOut void setAudioEn(bool nval){}; bool getAudioEn() const{}; - const Stereo<Sample> getNext(); - protected: void *AudioThread(); static void *_AudioThread(void *arg); private: WavFile file; + Atomic<bool> enabled; + pthread_t pThread; }; #endif diff --git a/src/UI/NioUI.cpp b/src/UI/NioUI.cpp @@ -1,6 +1,7 @@ #include "NioUI.h" #include "../Nio/EngineMgr.h" #include "../Nio/OutMgr.h" +#include "../Nio/InMgr.h" #include "../Nio/AudioOut.h" #include "../Nio/MidiIn.h" #include <cstdio> @@ -33,32 +34,63 @@ NioUI::NioUI() } gen->end(); + Fl_Group *settings = new Fl_Group(0,20,400,400-35,"Settings"); + { + audio = new Fl_Choice(60, 80, 100, 25, "Audio"); + audio->callback(audioCallback); + midi = new Fl_Choice(60, 100, 100, 25, "Midi"); + midi->callback(midiCallback); + } + settings->end(); + for(list<Engine *>::iterator itr = sysEngine->engines.begin(); itr != sysEngine->engines.end(); ++itr) { - bool midi = dynamic_cast<MidiIn *>(*itr); - bool audio = dynamic_cast<AudioOut *>(*itr); - tabs.push_back(new NioTab((*itr)->name, midi, audio)); + Engine *eng = *itr; + if(dynamic_cast<MidiIn *>(eng)) + midi->add(eng->name.c_str()); + if(dynamic_cast<AudioOut *>(eng)) + audio->add(eng->name.c_str()); } + + //for(list<Engine *>::iterator itr = sysEngine->engines.begin(); + // itr != sysEngine->engines.end(); ++itr) { + // bool midi = dynamic_cast<MidiIn *>(*itr); + // bool audio = dynamic_cast<AudioOut *>(*itr); + // tabs.push_back(new NioTab((*itr)->name, midi, audio)); + //} + //add tabs - for(list<NioTab *>::iterator itr = tabs.begin(); - itr != tabs.end(); ++itr) - wintabs->add(*itr); + //for(list<NioTab *>::iterator itr = tabs.begin(); + // itr != tabs.end(); ++itr) + // wintabs->add(*itr); } wintabs->end(); - Fl::scheme("plastic"); resizable(this); size_range(400,300); } void NioUI::refresh() { - for(list<NioTab *>::iterator itr = tabs.begin(); - itr != tabs.end(); ++itr) - (*itr)->refresh(); + //for(list<NioTab *>::iterator itr = tabs.begin(); + // itr != tabs.end(); ++itr) + // (*itr)->refresh(); + } +void NioUI::midiCallback(Fl_Widget *c) +{ + bool good = sysIn->setSource(static_cast<Fl_Choice *>(c)->text()); + static_cast<Fl_Choice *>(c)->textcolor(fl_rgb_color(255*!good,0,0)); +} + +void NioUI::audioCallback(Fl_Widget *c) +{ + bool good = sysOut->setSink(static_cast<Fl_Choice *>(c)->text()); + static_cast<Fl_Choice *>(c)->textcolor(fl_rgb_color(255*!good,0,0)); +} +#if 0 //this is a repetitve block of code //perhaps something on the Engine's side should be refactored void NioTab::nioToggle(Fl_Widget *wid, void *arg) @@ -157,4 +189,5 @@ void NioTab::refresh() this->labelcolor(fl_rgb_color(0,255*state,0)); this->redraw(); } +#endif diff --git a/src/UI/NioUI.h b/src/UI/NioUI.h @@ -7,9 +7,12 @@ #include <FL/Fl_Pack.H> #include <FL/Fl_Spinner.H> #include <FL/Enumerations.H> +#include <FL/Fl_Choice.H> #include <list> #include <string> +//removed from code for now +#if 0 struct NioTab : public Fl_Group { NioTab(std::string name, bool _midi, bool _audio); @@ -26,6 +29,8 @@ struct NioTab : public Fl_Group Fl_Spinner *buffer; const std::string name; }; +#endif + class NioUI : public Fl_Window { @@ -33,7 +38,12 @@ class NioUI : public Fl_Window NioUI(); void refresh(); private: - std::list<NioTab *> tabs; + //std::list<NioTab *> tabs; + + Fl_Choice *midi; + Fl_Choice *audio; + static void midiCallback(Fl_Widget *c); + static void audioCallback(Fl_Widget *c); }; #endif diff --git a/src/main.cpp b/src/main.cpp @@ -222,6 +222,7 @@ void exitprogram() { pthread_mutex_lock(&master->mutex); pthread_mutex_unlock(&master->mutex); + sysEngine->stop(); delete sysOut; delete sysIn; delete sysEngine;