![]() |
Mixxx
|
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