![]() |
Mixxx
|
00001 /*************************************************************************** 00002 enginebufferscalest.cpp - description 00003 ------------------- 00004 begin : November 2004 00005 copyright : (C) 2004 by Tue Haste Andersen 00006 email : haste@diku.dk 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00018 #include <QtCore> 00019 00020 #include "enginebufferscalest.h" 00021 00022 #include "SoundTouch.h" 00023 #include "mathstuff.h" 00024 #include "controlobject.h" 00025 #include "engine/readaheadmanager.h" 00026 #include "engine/engineobject.h" 00027 00028 using namespace soundtouch; 00029 00030 EngineBufferScaleST::EngineBufferScaleST(ReadAheadManager *pReadAheadManager) : 00031 EngineBufferScale(), 00032 m_bBackwards(false), 00033 m_bPitchIndpTimeStretch(false), 00034 m_bClear(true), 00035 m_pReadAheadManager(pReadAheadManager) 00036 { 00037 m_qMutex.lock(); 00038 m_pSoundTouch = new soundtouch::SoundTouch(); 00039 m_dBaseRate = 1.; 00040 m_dTempo = 1.; 00041 00042 m_pSoundTouch->setChannels(2); 00043 m_pSoundTouch->setRate(m_dBaseRate); 00044 m_pSoundTouch->setTempo(m_dTempo); 00045 m_pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, 1); 00046 m_qMutex.unlock(); 00047 00048 slotSetSamplerate(44100.); 00049 ControlObject * p = ControlObject::getControl(ConfigKey("[Master]","samplerate")); 00050 connect(p, SIGNAL(valueChanged(double)), this, SLOT(slotSetSamplerate(double))); 00051 00052 buffer_back = new CSAMPLE[kiSoundTouchReadAheadLength*2]; 00053 } 00054 00055 EngineBufferScaleST::~EngineBufferScaleST() 00056 { 00057 delete m_pSoundTouch; 00058 delete [] buffer_back; 00059 } 00060 00061 void EngineBufferScaleST::setPitchIndpTimeStretch(bool b) 00062 { 00063 m_bPitchIndpTimeStretch = b; 00064 m_qMutex.lock(); 00065 if (m_bPitchIndpTimeStretch) 00066 m_pSoundTouch->setRate(1.); 00067 else 00068 m_pSoundTouch->setTempo(1.); 00069 m_qMutex.unlock(); 00070 } 00071 00072 bool EngineBufferScaleST::getPitchIndpTimeStretch(void) 00073 { 00074 return m_bPitchIndpTimeStretch; 00075 } 00076 00077 00078 void EngineBufferScaleST::setBaseRate(double dBaseRate) 00079 { 00080 m_dBaseRate = dBaseRate; 00081 00082 m_qMutex.lock(); 00083 if (m_bPitchIndpTimeStretch) { 00084 m_pSoundTouch->setRate(m_dBaseRate); 00085 } 00086 //or if if we use ST for linear interpolation... 00087 else if (m_dBaseRate >= MIN_SEEK_SPEED) { 00088 m_pSoundTouch->setRate(m_dBaseRate*m_dTempo); 00089 } 00090 //It's an error to pass a rate or tempo smaller than MIN_SEEK_SPEED to SoundTouch. 00091 //if (m_dBaseRate <= MIN_SEEK_SPEED) 00092 // m_pSoundTouch->setRate(0.010f); 00093 //else if(m_dBaseRate >= MIN_SEEK_SPEED) 00094 // m_pSoundTouch->setRate(m_dBaseRate*m_dTempo); 00095 m_qMutex.unlock(); 00096 } 00097 00098 00099 void EngineBufferScaleST::clear() 00100 { 00101 m_qMutex.lock(); 00102 m_pSoundTouch->clear(); 00103 m_bClear = true; 00104 m_qMutex.unlock(); 00105 } 00106 00107 void EngineBufferScaleST::slotSetSamplerate(double dSampleRate) 00108 { 00109 int iSrate = (int)dSampleRate; 00110 00111 m_qMutex.lock(); 00112 if (iSrate>0) 00113 m_pSoundTouch->setSampleRate(iSrate); 00114 else 00115 m_pSoundTouch->setSampleRate(44100); 00116 m_qMutex.unlock(); 00117 } 00118 00119 double EngineBufferScaleST::setTempo(double dTempo) 00120 { 00121 double dTempoOld = m_dTempo; 00122 m_dTempo = fabs(dTempo); 00123 00124 if (m_dTempo>MAX_SEEK_SPEED) 00125 m_dTempo = MAX_SEEK_SPEED; 00126 else if (m_dTempo<MIN_SEEK_SPEED) 00127 m_dTempo = 0.0; 00128 00129 m_qMutex.lock(); 00130 //It's an error to pass a rate or tempo smaller than MIN_SEEK_SPEED to SoundTouch. 00131 if (dTempoOld != m_dTempo && m_dTempo != 0.0) 00132 { 00133 if (m_bPitchIndpTimeStretch) 00134 m_pSoundTouch->setTempo(m_dTempo); 00135 else 00136 m_pSoundTouch->setRate(m_dBaseRate*m_dTempo); 00137 } 00138 m_qMutex.unlock(); 00139 00140 if (dTempo<0.) 00141 { 00142 if (!m_bBackwards) 00143 clear(); 00144 00145 m_bBackwards = true; 00146 return -m_dTempo; 00147 } 00148 else 00149 { 00150 if (m_bBackwards) 00151 clear(); 00152 00153 m_bBackwards = false; 00154 return m_dTempo; 00155 } 00156 } 00157 00164 CSAMPLE* EngineBufferScaleST::scale(double playpos, unsigned long buf_size, 00165 CSAMPLE* pBase, unsigned long iBaseLength) { 00166 Q_UNUSED (pBase); 00167 Q_UNUSED (iBaseLength); 00168 new_playpos = 0.0; 00169 00170 m_qMutex.lock(); 00171 00172 int iCurPos = playpos; 00173 if (!even(iCurPos)) { 00174 iCurPos--; 00175 } 00176 00177 //If we've just cleared SoundTouch's FIFO of unprocessed samples, 00178 //then reset our "read ahead position" because we probably need 00179 //to read backwards instead of forwards or something like that. 00180 // if (true || m_bClear) 00181 // { 00182 // m_iReadAheadPos = (unsigned long)playpos; 00183 // if (!even(m_iReadAheadPos)) 00184 // m_iReadAheadPos--; 00185 // //m_iReadAheadPos = (m_iReadAheadPos+1)%iBaseLength; 00186 // m_bClear = false; 00187 // } 00188 //Q_ASSERT(m_iReadAheadPos >= 0); 00189 00190 unsigned long total_received_frames = 0; 00191 unsigned long total_read_frames = 0; 00192 00193 unsigned long remaining_frames = buf_size/2; 00194 //long remaining_source_frames = iBaseLength/2; 00195 CSAMPLE* read = buffer; 00196 bool last_read_failed = false; 00197 while (remaining_frames > 0) { 00198 unsigned long received_frames = m_pSoundTouch->receiveSamples((SAMPLETYPE*)read, remaining_frames); 00199 remaining_frames -= received_frames; 00200 total_received_frames += received_frames; 00201 read += received_frames*2; 00202 00203 if (remaining_frames > 0) { 00204 // math_min(kiSoundTouchReadAheadLength,remaining_source_frames); 00205 unsigned long iLenFrames = kiSoundTouchReadAheadLength; 00206 unsigned long iAvailSamples = m_pReadAheadManager 00207 ->getNextSamples((m_bBackwards ? -1.0f : 1.0f) * m_dBaseRate * m_dTempo, 00208 buffer_back, 00209 iLenFrames * 2); 00210 unsigned long iAvailFrames = iAvailSamples / 2; 00211 00212 if (iAvailFrames > 0) { 00213 last_read_failed = false; 00214 total_read_frames += iAvailFrames; 00215 m_pSoundTouch->putSamples(buffer_back, iAvailFrames); 00216 } else { 00217 if (last_read_failed) 00218 break; 00219 last_read_failed = true; 00220 m_pSoundTouch->flush(); 00221 } 00222 } 00223 } 00224 00225 //Feed more samples into SoundTouch until it has processed enough to 00226 //fill the audio buffer that we need to fill. 00227 //SoundTouch::numSamples() returns the number of _FRAMES_ that 00228 //are in its FIFO audio buffer... 00229 00230 00231 // Calculate new playpos 00232 00233 //Get the stretched _frames_ (not Samples, as the function call 00234 //erroroneously implies) 00235 //long receivedFrames = m_pSoundTouch->receiveSamples((SAMPLETYPE*)buffer, buf_size/2); 00236 00237 // qDebug() << "Fed ST" << total_read_frames*2 00238 // << "samples to get" << total_received_frames*2 << "samples"; 00239 if (total_received_frames != buf_size/2) 00240 { 00241 qDebug() << __FILE__ << "- only wrote" << total_received_frames << "frames instead of requested" << buf_size; 00242 } 00243 00244 //for (unsigned long i = 0; i < buf_size; i++) 00245 // qDebug() << buffer[i]; 00246 00247 // new_playpos is now interpreted as the total number of virtual samples 00248 // consumed to produce the scaled buffer. Due to this, we do not take into 00249 // account directionality or starting point. 00250 new_playpos = m_dTempo*m_dBaseRate*total_received_frames*2; 00251 00252 m_qMutex.unlock(); 00253 00254 return buffer; 00255 } 00256