From 1c4c833c86b38de435f9ed359e1719ed3b6a1f59 Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Thu, 11 Feb 2010 20:09:45 +0000 Subject: 2.5 Audio: The jack backend is now realtime capable and will not produce so much xruns anymore. :-) --- intern/audaspace/jack/AUD_JackDevice.cpp | 136 +++++++++++++++++++++++++++---- intern/audaspace/jack/AUD_JackDevice.h | 20 ++++- 2 files changed, 137 insertions(+), 19 deletions(-) (limited to 'intern/audaspace/jack') diff --git a/intern/audaspace/jack/AUD_JackDevice.cpp b/intern/audaspace/jack/AUD_JackDevice.cpp index acd37de870c..d22449dc129 100644 --- a/intern/audaspace/jack/AUD_JackDevice.cpp +++ b/intern/audaspace/jack/AUD_JackDevice.cpp @@ -31,24 +31,78 @@ #include #include -// AUD_XXX this is not realtime suitable! +void* AUD_JackDevice::runThread(void* device) +{ + ((AUD_JackDevice*)device)->updateRingBuffers(); + return NULL; +} + +void AUD_JackDevice::updateRingBuffers() +{ + size_t size, temp; + unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs); + unsigned int i, j; + unsigned int channels = m_specs.channels; + sample_t* buffer = m_buffer->getBuffer(); + float* deinterleave = m_deinterleavebuf->getBuffer(); + + pthread_mutex_lock(&m_lock); + while(m_valid) + { + size = jack_ringbuffer_write_space(m_ringbuffers[0]); + for(i = 1; i < channels; i++) + if((temp = jack_ringbuffer_write_space(m_ringbuffers[i])) < size) + size = temp; + + while(size > samplesize) + { + size /= samplesize; + mix((data_t*)buffer, size); + for(i = 0; i < channels; i++) + { + for(j = 0; j < size; j++) + deinterleave[i * size + j] = buffer[i + j * channels]; + jack_ringbuffer_write(m_ringbuffers[i], (char*)(deinterleave + i * size), size * sizeof(float)); + } + + size = jack_ringbuffer_write_space(m_ringbuffers[0]); + for(i = 1; i < channels; i++) + if((temp = jack_ringbuffer_write_space(m_ringbuffers[i])) < size) + size = temp; + } + + pthread_cond_wait(&m_condition, &m_lock); + } + pthread_mutex_unlock(&m_lock); +} + int AUD_JackDevice::jack_mix(jack_nframes_t length, void *data) { AUD_JackDevice* device = (AUD_JackDevice*)data; - unsigned int samplesize = AUD_SAMPLE_SIZE(device->m_specs); - if(device->m_buffer->getSize() < samplesize * length) - device->m_buffer->resize(samplesize * length); - device->mix((data_t*)device->m_buffer->getBuffer(), length); - - float* in = (float*) device->m_buffer->getBuffer(); - float* out; + unsigned int i; int count = device->m_specs.channels; + char* buffer; + + size_t temp; + size_t readsamples = jack_ringbuffer_read_space(device->m_ringbuffers[0]); + for(i = 1; i < count; i++) + if((temp = jack_ringbuffer_read_space(device->m_ringbuffers[i])) < readsamples) + readsamples = temp; + + readsamples = AUD_MIN(readsamples / sizeof(float), length); for(unsigned int i = 0; i < count; i++) { - out = (float*)jack_port_get_buffer(device->m_ports[i], length); - for(unsigned int j = 0; j < length; j++) - out[j] = in[j * count + i]; + buffer = (char*)jack_port_get_buffer(device->m_ports[i], length); + jack_ringbuffer_read(device->m_ringbuffers[i], buffer, readsamples * sizeof(float)); + if(readsamples < length) + memset(buffer + readsamples * sizeof(float), 0, (length - readsamples) * sizeof(float)); + } + + if(pthread_mutex_trylock(&(device->m_lock)) == 0) + { + pthread_cond_signal(&(device->m_condition)); + pthread_mutex_unlock(&(device->m_lock)); } return 0; @@ -60,7 +114,7 @@ void AUD_JackDevice::jack_shutdown(void *data) device->m_valid = false; } -AUD_JackDevice::AUD_JackDevice(AUD_DeviceSpecs specs) +AUD_JackDevice::AUD_JackDevice(AUD_DeviceSpecs specs, int buffersize) { if(specs.channels == AUD_CHANNELS_INVALID) specs.channels = AUD_CHANNELS_STEREO; @@ -77,8 +131,6 @@ AUD_JackDevice::AUD_JackDevice(AUD_DeviceSpecs specs) if(m_client == NULL) AUD_THROW(AUD_ERROR_JACK); - m_buffer = new AUD_Buffer(); AUD_NEW("buffer"); - // set callbacks jack_set_process_callback(m_client, AUD_JackDevice::jack_mix, this); jack_on_shutdown(m_client, AUD_JackDevice::jack_shutdown, this); @@ -98,9 +150,31 @@ AUD_JackDevice::AUD_JackDevice(AUD_DeviceSpecs specs) if(m_ports[i] == NULL) AUD_THROW(AUD_ERROR_JACK); } + } + catch(AUD_Exception) + { + jack_client_close(m_client); + delete[] m_ports; AUD_DELETE("jack_port") + throw; + } + + m_specs.rate = (AUD_SampleRate)jack_get_sample_rate(m_client); - m_specs.rate = (AUD_SampleRate)jack_get_sample_rate(m_client); + buffersize /= 256; + buffersize *= m_specs.rate; + buffersize /= 4 * sizeof(sample_t); + buffersize *= sizeof(sample_t); + m_ringbuffers = new jack_ringbuffer_t*[specs.channels]; AUD_NEW("jack_buffers") + for(unsigned int i = 0; i < specs.channels; i++) + m_ringbuffers[i] = jack_ringbuffer_create(buffersize); + buffersize *= specs.channels; + m_buffer = new AUD_Buffer(buffersize); AUD_NEW("buffer"); + m_deinterleavebuf = new AUD_Buffer(buffersize); AUD_NEW("buffer"); + + create(); + try + { // activate the client if(jack_activate(m_client)) AUD_THROW(AUD_ERROR_JACK); @@ -110,6 +184,11 @@ AUD_JackDevice::AUD_JackDevice(AUD_DeviceSpecs specs) jack_client_close(m_client); delete[] m_ports; AUD_DELETE("jack_port") delete m_buffer; AUD_DELETE("buffer"); + delete m_deinterleavebuf; AUD_DELETE("buffer"); + for(unsigned int i = 0; i < specs.channels; i++) + jack_ringbuffer_free(m_ringbuffers[i]); + delete[] m_ringbuffers; AUD_DELETE("jack_buffers") + destroy(); throw; } @@ -125,17 +204,38 @@ AUD_JackDevice::AUD_JackDevice(AUD_DeviceSpecs specs) m_valid = true; - create(); + pthread_mutex_init(&m_lock, NULL); + pthread_cond_init(&m_condition, NULL); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + pthread_create(&m_thread, &attr, runThread, this); + + pthread_attr_destroy(&attr); } AUD_JackDevice::~AUD_JackDevice() { - lock(); if(m_valid) jack_client_close(m_client); + m_valid = false; + delete[] m_ports; AUD_DELETE("jack_port") + + pthread_mutex_lock(&m_lock); + pthread_cond_signal(&m_condition); + pthread_mutex_unlock(&m_lock); + pthread_join(m_thread, NULL); + + pthread_cond_destroy(&m_condition); + pthread_mutex_destroy(&m_lock); delete m_buffer; AUD_DELETE("buffer"); - unlock(); + delete m_deinterleavebuf; AUD_DELETE("buffer"); + for(unsigned int i = 0; i < m_specs.channels; i++) + jack_ringbuffer_free(m_ringbuffers[i]); + delete[] m_ringbuffers; AUD_DELETE("jack_buffers") destroy(); } diff --git a/intern/audaspace/jack/AUD_JackDevice.h b/intern/audaspace/jack/AUD_JackDevice.h index 5a073a69432..96388e1ee50 100644 --- a/intern/audaspace/jack/AUD_JackDevice.h +++ b/intern/audaspace/jack/AUD_JackDevice.h @@ -31,6 +31,7 @@ class AUD_Buffer; #include +#include /** * This device plays back through Jack. @@ -53,6 +54,8 @@ private: */ AUD_Buffer* m_buffer; + AUD_Buffer* m_deinterleavebuf; + /** * Whether the device is valid. */ @@ -72,6 +75,21 @@ private: */ static int jack_mix(jack_nframes_t length, void *data); + static void* runThread(void* device); + + void updateRingBuffers(); + + jack_ringbuffer_t** m_ringbuffers; + + /** + * The streaming thread. + */ + pthread_t m_thread; + + pthread_mutex_t m_lock; + + pthread_cond_t m_condition; + protected: virtual void playing(bool playing); @@ -81,7 +99,7 @@ public: * \param specs The wanted audio specification, where only the channel count is important. * \exception AUD_Exception Thrown if the audio device cannot be opened. */ - AUD_JackDevice(AUD_DeviceSpecs specs); + AUD_JackDevice(AUD_DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE); /** * Closes the Jack client. -- cgit v1.2.3