zynaddsubfx

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

commit a3841ecb3c665aa5e746a7056c215a4d6e102b2e
parent e75a6c086287c4deb14fac74078b5c9431807c7f
Author: fundamental <[email protected]>
Date:   Wed,  9 Dec 2009 22:28:30 -0500

Nio: ALSA support enabled.

- Alsa is now functional, but there are several parameters, which are causing
  unwanted latency in the output engine.

Diffstat:
Msrc/Nio/AlsaEngine.cpp | 366++++++++++++++++++++++++-------------------------------------------------------
Msrc/Nio/AlsaEngine.h | 21+++++++++++++++++----
Msrc/Nio/AudioOut.cpp | 86-------------------------------------------------------------------------------
Msrc/Nio/OutMgr.cpp | 9+--------
Msrc/main.cpp | 9+++++----
5 files changed, 131 insertions(+), 360 deletions(-)

diff --git a/src/Nio/AlsaEngine.cpp b/src/Nio/AlsaEngine.cpp @@ -33,12 +33,11 @@ AlsaEngine::AlsaEngine(OutMgr *out) audio.handle = NULL; audio.period_time = 0; audio.samplerate = 0; - audio.buffer_size = 0; + audio.buffer_size = SOUND_BUFFER_SIZE;//0; audio.period_size = 0; - audio.buffer_size = 0; audio.alsaId = -1; audio.pThread = 0; - + // midi.handle = NULL; // midi.alsaId = -1; // midi.pThread = 0; @@ -53,27 +52,8 @@ AlsaEngine::AlsaEngine(OutMgr *out) bool AlsaEngine::openAudio() { - audio.device = "default";//config.cfg.audioDevice; - audio.samplerate = config.cfg.SampleRate; - audio.period_size = config.cfg.SoundBufferSize; - audio.period_time = audio.period_size * 1000000.0f / audio.samplerate; - alsaBad(snd_config_update_free_global(), "failed to update snd config"); - if (alsaBad(snd_pcm_open(&audio.handle, audio.device.c_str(), - SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_CHANNELS), - "failed to open alsa audio device:" + audio.device)) - goto bail_out; - if (alsaBad(snd_pcm_nonblock(audio.handle, 0), "set blocking failed")) - goto bail_out; - if (!prepHwparams()) - goto bail_out; - if (!prepSwparams()) - goto bail_out; - //config.cfg.Samplerate = getSamplerate(); - //config.cfg.Buffersize = getBuffersize(); + OpenStuff(); return true; -bail_out: - Close(); - return false; } @@ -112,24 +92,10 @@ bail_out: void AlsaEngine::Close() { Stop(); - if (audio.handle != NULL) - alsaBad(snd_pcm_close(audio.handle), "close pcm failed"); - audio.handle = NULL; -// if (NULL != midi.handle) -// if (snd_seq_close(midi.handle) < 0) -// cerr << "Error closing Alsa midi connection" << endl; -// midi.handle = NULL; -} - -void AlsaEngine::out(const Stereo<Sample> smps) -{ - pthread_mutex_lock(&outBuf_mutex); - outBuf.push(smps); - pthread_cond_signal(&outBuf_cv); - pthread_mutex_unlock(&outBuf_mutex); + snd_pcm_drain(handle); + snd_pcm_close(handle); } - string AlsaEngine::audioClientName() { string name = "zynaddsubfx"; @@ -146,125 +112,6 @@ string AlsaEngine::audioClientName() // return name; //} - -bool AlsaEngine::prepHwparams() -{ - unsigned int buffer_time = audio.period_time * 4; - unsigned int desired_samplerate = audio.samplerate; - snd_pcm_format_t format = SND_PCM_FORMAT_S16; // Alsa appends _LE/_BE? hmmm - snd_pcm_access_t axs = SND_PCM_ACCESS_MMAP_INTERLEAVED; - snd_pcm_hw_params_t *hwparams = NULL; - snd_pcm_hw_params_alloca(&hwparams); - if (alsaBad(snd_pcm_hw_params_any(audio.handle, hwparams), - "alsa audio no playback configurations available")) - goto bail_out; - if (alsaBad(snd_pcm_hw_params_set_periods_integer(audio.handle, hwparams), - "alsa audio cannot restrict period size to integral value")) - goto bail_out; - if (!alsaBad(snd_pcm_hw_params_set_access(audio.handle, hwparams, axs), - "alsa audio mmap not possible")) - pcmWrite = &snd_pcm_mmap_writei; - else - { - axs = SND_PCM_ACCESS_RW_INTERLEAVED; - if (alsaBad(snd_pcm_hw_params_set_access(audio.handle, hwparams, axs), - "alsa audio failed to set access, both mmap and rw failed")) - goto bail_out; - pcmWrite = &snd_pcm_writei; - } - if (alsaBad(snd_pcm_hw_params_set_format(audio.handle, hwparams, format), - "alsa audio failed to set sample format")) - goto bail_out; - alsaBad(snd_pcm_hw_params_set_rate_resample(audio.handle, hwparams, 1), - "alsa audio failed to set allow resample"); - if (alsaBad(snd_pcm_hw_params_set_rate_near(audio.handle, hwparams, - &audio.samplerate, NULL), - "alsa audio failed to set sample rate to " - + stringFrom<int>(desired_samplerate))) - goto bail_out; - if (alsaBad(snd_pcm_hw_params_set_channels(audio.handle, hwparams, 2), - "alsa audio failed to set channels to 2")) - goto bail_out; - if (!alsaBad(snd_pcm_hw_params_set_buffer_time_near(audio.handle, hwparams, - &buffer_time, NULL), "initial buffer time setting failed")) - { - if (alsaBad(snd_pcm_hw_params_get_buffer_size(hwparams, &audio.buffer_size), - "alsa audio failed to get buffer size")) - goto bail_out; - if (alsaBad(snd_pcm_hw_params_set_period_time_near(audio.handle, hwparams, - &audio.period_time, NULL), "failed to set period time")) - goto bail_out; - if (alsaBad(snd_pcm_hw_params_get_period_size(hwparams, &audio.period_size, - NULL), "alsa audio failed to get period size")) - goto bail_out; - } - else - { - if (alsaBad(snd_pcm_hw_params_set_period_time_near(audio.handle, hwparams, - &audio.period_time, NULL), "failed to set period time")) - goto bail_out; - audio.buffer_size = audio.period_size * 4; - if (alsaBad(snd_pcm_hw_params_set_buffer_size_near(audio.handle, hwparams, - &audio.buffer_size), "failed to set buffer size")) - goto bail_out; - } - if (alsaBad(snd_pcm_hw_params(audio.handle, hwparams), - "alsa audio failed to set hardware parameters")) - goto bail_out; - if (alsaBad(snd_pcm_hw_params_get_buffer_size(hwparams, &audio.buffer_size), - "alsa audio failed to get buffer size")) - goto bail_out; - if (alsaBad(snd_pcm_hw_params_get_period_size(hwparams, &audio.period_size, - NULL), "failed to get period size")) - goto bail_out; - return true; - -bail_out: - if (audio.handle != NULL) - Close(); - return false; -} - - -bool AlsaEngine::prepSwparams() -{ - snd_pcm_sw_params_t *swparams; - snd_pcm_sw_params_alloca(&swparams); - snd_pcm_uframes_t boundary; - if (alsaBad(snd_pcm_sw_params_current(audio.handle, swparams), - "alsa audio failed to get swparams")) - goto bail_out; - if (alsaBad(snd_pcm_sw_params_get_boundary(swparams, &boundary), - "alsa audio failed to get boundary")) - goto bail_out; - if (alsaBad(snd_pcm_sw_params_set_start_threshold(audio.handle, swparams, - boundary + 1), - "failed to set start threshold")) // explicit start, not auto start - goto bail_out; - if (alsaBad(snd_pcm_sw_params_set_stop_threshold(audio.handle, swparams, - boundary), - "alsa audio failed to set stop threshold")) - goto bail_out; - if (alsaBad(snd_pcm_sw_params(audio.handle, swparams), - "alsa audio failed to set software parameters")) - goto bail_out; - return true; - -bail_out: - return false; -} - - -bool AlsaEngine::alsaBad(int op_result, string err_msg) -{ - bool isbad = (op_result < 0); // (op_result < 0) -> is bad -> return true - if (isbad) - cerr << "Error, alsa audio: " << err_msg << ": " - << string(snd_strerror(op_result)) << endl; - return isbad; -} - - void *AlsaEngine::_AudioThread(void *arg) { return (static_cast<AlsaEngine*>(arg))->AudioThread(); @@ -272,69 +119,20 @@ void *AlsaEngine::_AudioThread(void *arg) void *AlsaEngine::AudioThread() -{ - if (NULL == audio.handle) - { - cerr << "Error, null pcm handle into AlsaEngine::AudioThread" << endl; - return NULL; - } - set_realtime(); - alsaBad(snd_pcm_start(audio.handle), "alsa audio pcm start failed"); - while (!threadStop) - { - cout << "AlsaEngine THREAD" << endl; - const Stereo<Sample> smps = getNext(); - - audio.pcm_state = snd_pcm_state(audio.handle); - if (audio.pcm_state != SND_PCM_STATE_RUNNING) - { - switch (audio.pcm_state) - { - case SND_PCM_STATE_XRUN: - case SND_PCM_STATE_SUSPENDED: - if (!xrunRecover()) - break; - // else fall through to ... - case SND_PCM_STATE_SETUP: - if (alsaBad(snd_pcm_prepare(audio.handle), - "alsa audio pcm prepare failed")) - break; - case SND_PCM_STATE_PREPARED: - alsaBad(snd_pcm_start(audio.handle), "pcm start failed"); - break; - default: - cout << "AlsaEngine::AudioThread, weird SND_PCM_STATE: " - << audio.pcm_state << endl; - break; - } - audio.pcm_state = snd_pcm_state(audio.handle); - } - if (audio.pcm_state == SND_PCM_STATE_RUNNING) - { - const short *tmp = interleave(smps); - Write(tmp); - delete [] tmp; - } - else - { - //config.cfg.verbose - // && cerr << "Error, audio pcm still not RUNNING" << endl; - cerr << "Error, audio pcm still not running"; - } - } +{ + RunStuff(); return NULL; } -void AlsaEngine::Write(const short *InterleavedSmps) +void AlsaEngine::Write(const short *InterleavedSmps,int size) { - snd_pcm_uframes_t towrite = getBuffersize(); + snd_pcm_uframes_t towrite = size;//getBuffersize(); snd_pcm_sframes_t wrote = 0; const short int *data = InterleavedSmps; while (towrite > 0) { - //wrote = pcmWrite(audio.handle, &data, towrite); - wrote = snd_pcm_writei(audio.handle, data, towrite); + wrote = pcmWrite(audio.handle, &data, towrite); if (wrote >= 0) { if ((snd_pcm_uframes_t)wrote < towrite || wrote == -EAGAIN) @@ -350,7 +148,7 @@ void AlsaEngine::Write(const short *InterleavedSmps) switch (wrote) { case -EBADFD: - alsaBad(-EBADFD, "alsa audio unfit for writing"); + //alsaBad(-EBADFD, "alsa audio unfit for writing"); break; case -EPIPE: xrunRecover(); @@ -359,7 +157,7 @@ void AlsaEngine::Write(const short *InterleavedSmps) Recover(wrote); break; default: - alsaBad(wrote, "alsa audio, snd_pcm_writei ==> weird state"); + //alsaBad(wrote, "alsa audio, snd_pcm_writei ==> weird state"); break; } wrote = 0; @@ -379,14 +177,14 @@ bool AlsaEngine::Recover(int err) isgood = true; // nuthin to see here break; case -ESTRPIPE: - if (!alsaBad(snd_pcm_prepare(audio.handle), - "Error, AlsaEngine failed to recover from suspend")) - isgood = true; + // if (!alsaBad(snd_pcm_prepare(audio.handle), + // "Error, AlsaEngine failed to recover from suspend")) + // isgood = true; break; case -EPIPE: - if (!alsaBad(snd_pcm_prepare(audio.handle), - "Error, AlsaEngine failed to recover from underrun")) - isgood = true; + // if (!alsaBad(snd_pcm_prepare(audio.handle), + // "Error, AlsaEngine failed to recover from underrun")) + // isgood = true; break; default: break; @@ -400,8 +198,8 @@ bool AlsaEngine::xrunRecover() bool isgood = false; if (audio.handle != NULL) { - if (!alsaBad(snd_pcm_drop(audio.handle), "pcm drop failed")) - if (!alsaBad(snd_pcm_prepare(audio.handle), "pcm prepare failed")) + //if (!alsaBad(snd_pcm_drop(audio.handle), "pcm drop failed")) + // if (!alsaBad(snd_pcm_prepare(audio.handle), "pcm prepare failed")) isgood = true; ;//config.cfg.verbose // && cout << "Info, xrun recovery " << ((isgood) ? "good" : "not good") @@ -416,12 +214,12 @@ bool AlsaEngine::Start(void) int chk; pthread_attr_t attr; threadStop = false; - if (NULL != audio.handle) - { + //if (NULL != audio.handle) + //{ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&audio.pThread, &attr, _AudioThread, this); - } + //} // if (NULL != midi.handle) // { @@ -442,7 +240,7 @@ bail_out: void AlsaEngine::Stop(void) { threadStop = true; - + if (NULL != audio.handle && audio.pThread) if (pthread_cancel(audio.pThread)) cerr << "Error, failed to cancel Alsa audio thread" << endl; @@ -545,40 +343,11 @@ void AlsaEngine::Stop(void) // return NULL; //} -const Stereo<Sample> AlsaEngine::getNext() -{ - Stereo<Sample> ans; - if(outBuf.empty())//fetch samples if possible - { - if(true)//FIXME care about locking state later on - //mgr.requestSamples()!=-1)//samples are being prepared - { - manager->requestSamples(); - pthread_mutex_lock(&outBuf_mutex); - pthread_cond_wait(&outBuf_cv, &outBuf_mutex); - ans = outBuf.front(); - outBuf.pop(); - pthread_mutex_unlock(&outBuf_mutex); - } - } - else - { - pthread_mutex_lock(&outBuf_mutex); - ans = outBuf.front(); - outBuf.pop(); - pthread_mutex_unlock(&outBuf_mutex); - } - return ans; -} - - const short *AlsaEngine::interleave(const Stereo<Sample> smps)const { - //hm, this seems less than optimum - //TODO remove this excessive allocation/deallocation once things are - //integrated - short *shortInterleaved = new short[smps.l().size()*2+1002];//over allocation - /**\todo TODO fix overallocation*/ + /**\todo TODO fix repeated allocation*/ + short *shortInterleaved = new short[smps.l().size()*2];//over allocation + memset(shortInterleaved,0,smps.l().size()*2*sizeof(short)); int idx = 0;//possible off by one error here double scaled; for (int frame = 0; frame < smps.l().size(); ++frame) @@ -588,4 +357,85 @@ const short *AlsaEngine::interleave(const Stereo<Sample> smps)const scaled = smps.r()[frame] * (8.0 * 0x10000000); shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); } + return shortInterleaved; +} + + +void AlsaEngine::OpenStuff() +{ + /* Open PCM device for playback. */ + handle=NULL; + rc = snd_pcm_open(&handle, "default", + SND_PCM_STREAM_PLAYBACK, 0); + if (rc < 0) { + fprintf(stderr, + "unable to open pcm device: %s\n", + snd_strerror(rc)); + exit(1); + } + + /* Allocate a hardware parameters object. */ + snd_pcm_hw_params_alloca(&params); + + /* Fill it in with default values. */ + snd_pcm_hw_params_any(handle, params); + + /* Set the desired hardware parameters. */ + + /* Interleaved mode */ + snd_pcm_hw_params_set_access(handle, params, + SND_PCM_ACCESS_RW_INTERLEAVED); + + /* Signed 16-bit little-endian format */ + snd_pcm_hw_params_set_format(handle, params, + SND_PCM_FORMAT_S16_LE); + + /* Two channels (stereo) */ + snd_pcm_hw_params_set_channels(handle, params, 2); + + /* 44100 bits/second sampling rate (CD quality) + * \TODO make this dynamic*/ + val = 44100; + snd_pcm_hw_params_set_rate_near(handle, params, + &val, NULL);//&dir); + + frames = 32; + snd_pcm_hw_params_set_period_size_near(handle, + params, &frames, NULL);//&dir); + + /* Write the parameters to the driver */ + rc = snd_pcm_hw_params(handle, params); + if (rc < 0) { + fprintf(stderr, + "unable to set hw parameters: %s\n", + snd_strerror(rc)); + exit(1); + } + + /* Set buffer size (in frames). The resulting latency is given by */ + /* latency = periodsize * periods / (rate * bytes_per_frame) */ + snd_pcm_hw_params_set_buffer_size(handle, params, SOUND_BUFFER_SIZE); + + /* Use a buffer large enough to hold one period */ + snd_pcm_hw_params_get_period_size(params, &frames, &dir); + + snd_pcm_hw_params_get_period_time(params, &val, &dir); +} + +void AlsaEngine::RunStuff() +{ + while (!threadStop()) { + buffer = interleave(getNext()); + rc = snd_pcm_writei(handle, buffer, SOUND_BUFFER_SIZE); + delete buffer; + if (rc == -EPIPE) { + /* EPIPE means underrun */ + cerr << "underrun occurred" << endl; + snd_pcm_prepare(handle); + } + else if (rc < 0) + cerr << "error from writei: " << snd_strerror(rc) << endl; + else if (rc != (int)frames) + cerr << "short write, write " << rc << "frames" << endl; + } } diff --git a/src/Nio/AlsaEngine.h b/src/Nio/AlsaEngine.h @@ -42,7 +42,6 @@ class AlsaEngine : public AudioOut//, MidiIn bool Start(); void Stop(); void Close(); - void out(const Stereo<Sample> smps); unsigned int getSamplerate() { return audio.samplerate; }; unsigned int getBuffersize() { return audio.period_size; }; @@ -61,7 +60,7 @@ class AlsaEngine : public AudioOut//, MidiIn private: bool prepHwparams(); bool prepSwparams(); - void Write(const short *InterleavedSmps); + void Write(const short *InterleavedSmps, int size); bool Recover(int err); bool xrunRecover(); bool alsaBad(int op_result, std::string err_msg); @@ -72,7 +71,7 @@ class AlsaEngine : public AudioOut//, MidiIn snd_pcm_uframes_t nframes); /**Get the next sample for output.*/ - const Stereo<Sample> getNext(); + //const Stereo<Sample> getNext(); /**Interleave Samples. \todo move this to util*/ const short *interleave(const Stereo<Sample> smps) const; @@ -95,7 +94,21 @@ class AlsaEngine : public AudioOut//, MidiIn // pthread_t pThread; //} midi; - bool threadStop; + //from alsa example + long loops; + int rc; + int size; + snd_pcm_t *handle; + snd_pcm_hw_params_t *params; + unsigned int val; + int dir; + snd_pcm_uframes_t frames; + const short *buffer; + + void RunStuff(); + void OpenStuff(); + + //bool threadStop; //outside audio interface queue<Stereo<Sample> > outBuf; diff --git a/src/Nio/AudioOut.cpp b/src/Nio/AudioOut.cpp @@ -33,91 +33,6 @@ AudioOut::AudioOut(OutMgr *out) pthread_cond_init (&outBuf_cv, NULL); } -//bool AudioOut::prepAudiobuffers(unsigned int nframes, bool with_interleaved) -//{ -// if (nframes > 0) -// { -// buffersize = nframes; -// outl = new float[buffersize]; -// outr = new float[buffersize]; -// if (outl == NULL || outr == NULL) -// goto bail_out; -// memset(outl, 0, buffersize * sizeof(float)); -// memset(outr, 0, buffersize * sizeof(float)); -// if (with_interleaved) -// { -// shortInterleaved = new short int[buffersize * 2]; -// if (shortInterleaved == NULL) -// goto bail_out; -// memset(shortInterleaved, 0, buffersize * 2 * sizeof(short int)); -// } -// return true; -// } -// -//bail_out: -// cerr << "Error, failed to allocate audio buffers, size " << buffersize << endl; -// if (outl != NULL) -// delete [] outl; -// outl = NULL; -// if (outr != NULL) -// delete [] outr; -// outr = NULL; -// if (with_interleaved) -// { -// if (shortInterleaved != NULL) -// delete [] shortInterleaved; -// shortInterleaved = NULL; -// } -// return false; -//} -// -// -//bool AudioOut::getAudio(bool lockrequired) -//{ -// if (NULL != master && outr != NULL && outl != NULL) -// return master->MasterAudio(outl, outr, lockrequired); -// return false; -//} -// -// -//bool AudioOut::getAudioInterleaved(bool lockrequired) -//{ -// if (shortInterleaved != NULL) -// { -// if (getAudio(lockrequired)) -// { -// int idx = 0; -// double scaled; -// for (int frame = 0; frame < buffersize; ++frame) -// { // with a nod to libsamplerate ... -// scaled = outl[frame] * (8.0 * 0x10000000); -// shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); -// scaled = outr[frame] * (8.0 * 0x10000000); -// shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); -// } -// return true; -// } -// } -// return false; -//} -// -// -//void AudioOut::silenceBuffers() -//{ -// memset(outl, 0, buffersize * sizeof(float)); -// memset(outr, 0, buffersize * sizeof(float)); -//} -// -// -//void AudioOut::dimBuffers() -//{ -// for (int i = 0; i < buffersize; ++i) -// { -// outl[i] *= 0.84033613; // -1.5dB -// outr[i] *= 0.84033613; -// } -//} - void AudioOut::out(const Stereo<Sample> smps) { pthread_mutex_lock(&outBuf_mutex); @@ -134,7 +49,6 @@ const Stereo<Sample> AudioOut::getNext() pthread_mutex_unlock(&outBuf_mutex); if(isEmpty)//fetch samples if possible { - //cerr << manager << endl; if(manager->getRunning()<4) manager->requestSamples(); if(true) diff --git a/src/Nio/OutMgr.cpp b/src/Nio/OutMgr.cpp @@ -23,7 +23,6 @@ OutMgr::OutMgr(Master *nmaster) //initialize mutex pthread_mutex_init(&mutex, NULL); pthread_mutex_init(&processing, NULL); - //pthread_mutex_init(&request_m, NULL); pthread_cond_init(&needsProcess, NULL); //init samples outr = new REALTYPE[SOUND_BUFFER_SIZE]; @@ -142,18 +141,12 @@ void OutMgr::remove(AudioOut *out) int OutMgr::getRunning() { - //int tmp; - //pthread_mutex_lock(&request_m); - //tmp=eumRequests; - //pthread_mutex_unlock(&request_m); - return numRequests();//tmp; + return numRequests(); } int OutMgr::requestSamples() { - //pthread_mutex_lock(&request_m); ++numRequests; - //pthread_mutex_unlock(&request_m); pthread_mutex_lock(&processing); pthread_cond_signal(&needsProcess); diff --git a/src/main.cpp b/src/main.cpp @@ -687,11 +687,12 @@ int main(int argc, char *argv[]) sysOut=NULL; sysOut = new OutMgr(master); if(sysOut); - //AlsaEngine *tmp = new AlsaEngine(); - AudioOut *tmp = new OssEngine(sysOut); - if(tmp); + AudioOut *tmp = new AlsaEngine(sysOut); + tmp->openAudio(); //tmp->openAudio(); - sysOut->add(tmp); + //AudioOut *tmp = new OssEngine(sysOut); + if(tmp) + sysOut->add(tmp); sysOut->run(); #endif