2.5 Audio: The jack backend is now realtime capable and will not produce so much...
authorJoerg Mueller <nexyon@gmail.com>
Thu, 11 Feb 2010 20:09:45 +0000 (20:09 +0000)
committerJoerg Mueller <nexyon@gmail.com>
Thu, 11 Feb 2010 20:09:45 +0000 (20:09 +0000)
intern/audaspace/intern/AUD_C-API.cpp
intern/audaspace/jack/AUD_JackDevice.cpp
intern/audaspace/jack/AUD_JackDevice.h

index b2d5209e9d62272031c24de2fcdb6b595f55aa70..3c29b5d9e08ead6d2000aa9b0eb5f358d6ecbb9c 100644 (file)
@@ -113,7 +113,7 @@ int AUD_init(AUD_DeviceType device, AUD_DeviceSpecs specs, int buffersize)
 #endif
 #ifdef WITH_JACK
                case AUD_JACK_DEVICE:
-                       dev = new AUD_JackDevice(specs);
+                       dev = new AUD_JackDevice(specs, buffersize);
                        break;
 #endif
                default:
index acd37de870ce68154361f048960a4764e973800e..d22449dc12922391d7e21622a0dff865d5b41389 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 
-// 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();
 }
index 5a073a6943282ff0fb04024f92a81cb6d647e3ef..96388e1ee50f130cf0a949a8042458d62d409c88 100644 (file)
@@ -31,6 +31,7 @@
 class AUD_Buffer;
 
 #include <jack.h>
+#include <ringbuffer.h>
 
 /**
  * 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.