Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/engine/enginesidechain.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           enginesidechain.cpp
00003                              -------------------
00004     copyright            : (C) 2008 Albert Santoni
00005     email                : gamegod \a\t users.sf.net
00006 ***************************************************************************/
00007 
00008 /***************************************************************************
00009 *                                                                         *
00010 *   This program is free software; you can redistribute it and/or modify  *
00011 *   it under the terms of the GNU General Public License as published by  *
00012 *   the Free Software Foundation; either version 2 of the License, or     *
00013 *   (at your option) any later version.                                   *
00014 *                                                                         *
00015 ***************************************************************************/
00016 
00017 /* This class provides a way to do audio processing that does not need
00018  * to be executed in real-time. For example, m_shoutcast encoding/broadcasting
00019  * and recording encoding can be done here. This class uses double-buffering
00020  * to increase the amount of time the CPU has to do whatever work needs to
00021  * be done, and that work is executed in a separate thread. (Threading
00022  * allows the next buffer to be filled while processing a buffer that's is
00023  * already full.)
00024  *
00025  */
00026 
00027 #include <QtCore>
00028 #include <QtDebug>
00029 
00030 #include "engine/enginesidechain.h"
00031 #include "engine/enginebuffer.h"
00032 #include "recording/enginerecord.h"
00033 
00034 #ifdef __SHOUTCAST__
00035 #include "engine/engineshoutcast.h"
00036 #endif
00037 
00038 EngineSideChain::EngineSideChain(ConfigObject<ConfigValue> * pConfig) {
00039     m_pConfig = pConfig;
00040     m_bStopThread = false;
00041 
00042     m_bufferFront = new CSAMPLE[SIDECHAIN_BUFFER_SIZE];
00043     m_bufferBack  = new CSAMPLE[SIDECHAIN_BUFFER_SIZE];
00044     m_buffer = m_bufferFront;
00045 
00046     m_iBufferEnd = 0;
00047 
00048 #ifdef __SHOUTCAST__
00049     // Shoutcast
00050         m_shoutcast = new EngineShoutcast(m_pConfig);
00051 #endif
00052 
00053     m_rec = new EngineRecord(m_pConfig);
00054     connect(m_rec, SIGNAL(bytesRecorded(int)),
00055             this, SIGNAL(bytesRecorded(int)));
00056     connect(m_rec, SIGNAL(isRecording(bool)),
00057             this, SIGNAL(isRecording(bool)));
00058 
00059 
00060         start(QThread::LowPriority);    //Starts the thread and goes to the "run()" function below.
00061 }
00062 
00063 EngineSideChain::~EngineSideChain() {
00064     m_backBufferLock.lock();
00065 
00066     m_waitLock.lock();
00067     m_bStopThread = true;
00068     m_waitForFullBuffer.wakeAll();
00069     m_waitLock.unlock();
00070 
00071     wait(); //Wait until the thread has finished.
00072 
00073 #ifdef __SHOUTCAST__
00074     if (m_shoutcast)
00075         m_shoutcast->shutdown();
00076 #endif
00077 
00078     //Free up memory
00079     delete [] m_bufferFront;
00080     delete [] m_bufferBack;
00081 
00082 #ifdef __SHOUTCAST__
00083     delete m_shoutcast;
00084 #endif
00085 
00086     if(m_rec) delete m_rec;
00087 
00088     m_backBufferLock.unlock();
00089 }
00090 
00092 void EngineSideChain::submitSamples(CSAMPLE* newBuffer, int buffer_size)
00093 {
00094     //Copy samples into m_buffer.
00095     if (m_iBufferEnd + buffer_size <= SIDECHAIN_BUFFER_SIZE)    //FIXME: is <= correct?
00096     {
00097         memcpy(&m_buffer[m_iBufferEnd], newBuffer, buffer_size * sizeof(CSAMPLE));
00098         m_iBufferEnd += buffer_size;
00099     }
00100     else //If the new buffer won't fit, copy as much of it as we can over and then copy the rest after swapping.
00101     {
00102         memcpy(&m_buffer[m_iBufferEnd], newBuffer, (SIDECHAIN_BUFFER_SIZE - m_iBufferEnd)*sizeof(CSAMPLE));
00103         //Save the number of samples written because m_iBufferEnd gets reset in swapBuffers:
00104         int iNumSamplesWritten = SIDECHAIN_BUFFER_SIZE - m_iBufferEnd;
00105 
00106         // This will block the callback thread if the buffering overflows. As of
00107         // 10/2009 this lock is only used to protect the buffer pointers, so it
00108         // won't cause blocking.
00109         m_backBufferLock.lock();
00110         swapBuffers(); //Swaps buffers and resets m_iBufferEnd to zero.
00111         m_backBufferLock.unlock();
00112 
00113         //Since we swapped buffers, we now have a full buffer that needs processing.
00114         m_waitForFullBuffer.wakeAll(); //... so wake the thread up and get processing. :)
00115 
00116         //Calculate how many leftover samples need to be written to the other buffer.
00117         int iNumSamplesStillToWrite = buffer_size - iNumSamplesWritten;
00118 
00119         //Check to see if the remaining samples will fit in the other empty buffer.
00120         if (iNumSamplesStillToWrite > SIDECHAIN_BUFFER_SIZE)
00121         {
00122             iNumSamplesStillToWrite = SIDECHAIN_BUFFER_SIZE; //Drop samples if they won't fit.
00123             qDebug() << "EngineSideChain warning: dropped samples";
00124         }
00125         memcpy(&m_buffer[m_iBufferEnd], &newBuffer[iNumSamplesWritten], iNumSamplesStillToWrite*sizeof(CSAMPLE));
00126         m_iBufferEnd += iNumSamplesStillToWrite;
00127 
00128     }
00129 }
00130 
00131 /* Swaps the buffers in the double-buffering mechanism we use */
00132 void EngineSideChain::swapBuffers()
00133 {
00134     if (m_buffer == m_bufferFront)
00135     {
00136         m_buffer = m_bufferBack;
00137         m_filledBuffer = m_bufferFront;
00138     }
00139     else
00140     {
00141         m_buffer = m_bufferFront;
00142         m_filledBuffer = m_bufferBack;
00143     }
00144 
00145     m_iBufferEnd = 0;
00146 }
00147 
00148 void EngineSideChain::run()
00149 {
00150     unsigned static id = 0; //the id of this thread, for debugging purposes //XXX copypasta (should factor this out somehow), -kousu 2/2009
00151     QThread::currentThread()->setObjectName(QString("EngineSideChain %1").arg(++id));
00152 
00153     while (true)
00154     {
00155         m_waitLock.lock();
00156         // Check to see if we're supposed to exit/stop this thread.
00157         if (m_bStopThread) {
00158             m_waitLock.unlock();
00159             return;
00160         }
00161         m_waitForFullBuffer.wait(&m_waitLock);  //Sleep until the buffer has been filled.
00162         // Check to see if we're supposed to exit/stop this thread.
00163         if (m_bStopThread) {
00164             m_waitLock.unlock();
00165             return;
00166         }
00167         m_waitLock.unlock();
00168 
00169         //This portion of the code should be able to touch the buffer without having to use
00170         //the m_bufferLock mutex, because the buffers should have been swapped.
00171 
00172         //IMPORTANT: The filled buffer is "m_filledBuffer" - that's the audio we need to process here.
00173 
00174         //Do CPU intensive and non-realtime processing here.
00175 
00176         //m_backBufferLock.lock(); //This will cause the audio/callback thread to block if the buffers overflow,
00177                                    //so don't even think about enabling this. (I'm leaving it here as a
00178                                    //warning to anyone who wants to work on this code in the future.) - Albert
00179 
00180         // We need to use this lock when copying the pointer to the buffer or
00181         // else we could end up with a bogus pointer. We don't have to hold the
00182         // lock during the processing though.
00183 
00184         m_backBufferLock.lock();
00185         CSAMPLE* pBuffer = m_filledBuffer;
00186         m_backBufferLock.unlock();
00187 
00188 #ifdef __SHOUTCAST__
00189         m_shoutcast->process(pBuffer, pBuffer, SIDECHAIN_BUFFER_SIZE);
00190 #endif
00191         m_rec->process(pBuffer, pBuffer, SIDECHAIN_BUFFER_SIZE);
00192         //m_backBufferLock.unlock();
00193     }
00194 
00195 }
00196 
00197 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines