Mixxx

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

Go to the documentation of this file.
00001 /***************************************************************************
00002                           enginebuffer.cpp  -  description
00003                              -------------------
00004     begin                : Wed Feb 20 2002
00005     copyright            : (C) 2002 by Tue and Ken Haste Andersen
00006     email                :
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 <QEvent>
00019 #include <QtDebug>
00020 
00021 #include "engine/enginebuffer.h"
00022 #include "cachingreader.h"
00023 #include "sampleutil.h"
00024 
00025 #include "controlpushbutton.h"
00026 #include "controlobjectthreadmain.h"
00027 #include "configobject.h"
00028 #include "controlpotmeter.h"
00029 #include "engine/enginebufferscalest.h"
00030 #include "engine/enginebufferscalelinear.h"
00031 #include "engine/enginebufferscaledummy.h"
00032 #include "mathstuff.h"
00033 #include "engine/engineworkerscheduler.h"
00034 #include "engine/readaheadmanager.h"
00035 #include "engine/enginecontrol.h"
00036 #include "engine/loopingcontrol.h"
00037 #include "engine/ratecontrol.h"
00038 #include "engine/bpmcontrol.h"
00039 #include "engine/quantizecontrol.h"
00040 
00041 #ifdef __VINYLCONTROL__
00042 #include "engine/vinylcontrolcontrol.h"
00043 #endif
00044 
00045 #include "trackinfoobject.h"
00046 
00047 #ifdef _MSC_VER
00048 #include <float.h>  // for _isnan() on VC++
00049 #define isnan(x) _isnan(x)  // VC++ uses _isnan() instead of isnan()
00050 #else
00051 #include <math.h>  // for isnan() everywhere else
00052 #endif
00053 
00054 const double kMaxPlayposRange = 1.14;
00055 const double kMinPlayposRange = -0.14;
00056 
00057 EngineBuffer::EngineBuffer(const char * _group, ConfigObject<ConfigValue> * _config) :
00058     m_engineLock(QMutex::Recursive),
00059     group(_group),
00060     m_pConfig(_config),
00061     m_pLoopingControl(NULL),
00062     m_pRateControl(NULL),
00063     m_pBpmControl(NULL),
00064     m_pReadAheadManager(NULL),
00065     m_pOtherEngineBuffer(NULL),
00066     m_pReader(NULL),
00067     filepos_play(0.),
00068     rate_old(0.),
00069     file_length_old(-1),
00070     file_srate_old(0),
00071     m_iSamplesCalculated(0),
00072     m_iUiSlowTick(0),
00073     m_pTrackEnd(NULL),
00074     m_pRepeat(NULL),
00075     startButton(NULL),
00076     endButton(NULL),
00077     m_pScale(NULL),
00078     m_pScaleLinear(NULL),
00079     m_pScaleST(NULL),
00080     m_bScalerChanged(false),
00081     m_bLastBufferPaused(true),
00082     m_fRampValue(0.0),
00083     m_iRampState(ENGINE_RAMP_NONE),
00084     m_pDitherBuffer(new CSAMPLE[MAX_BUFFER_LEN]),
00085     m_iDitherBufferReadIndex(0) {
00086 
00087     // Generate dither values
00088     for (int i = 0; i < MAX_BUFFER_LEN; ++i) {
00089         m_pDitherBuffer[i] = static_cast<float>(rand() % 32768) / 32768.0 - 0.5;
00090     }
00091 
00092     m_fLastSampleValue[0] = 0;
00093     m_fLastSampleValue[1] = 0;
00094 
00095     m_pReader = new CachingReader(_group, _config);
00096     connect(m_pReader, SIGNAL(trackLoaded(TrackPointer, int, int)),
00097             this, SLOT(slotTrackLoaded(TrackPointer, int, int)),
00098             Qt::DirectConnection);
00099     connect(m_pReader, SIGNAL(trackLoadFailed(TrackPointer, QString)),
00100             this, SLOT(slotTrackLoadFailed(TrackPointer, QString)),
00101             Qt::DirectConnection);
00102 
00103     // Play button
00104     playButton = new ControlPushButton(ConfigKey(group, "play"));
00105     playButton->setToggleButton(true);
00106     connect(playButton, SIGNAL(valueChanged(double)),
00107             this, SLOT(slotControlPlay(double)),
00108             Qt::DirectConnection);
00109     playButtonCOT = new ControlObjectThreadMain(playButton);
00110 
00111     //Play from Start Button (for sampler)
00112     playStartButton = new ControlPushButton(ConfigKey(group, "start_play"));
00113     connect(playStartButton, SIGNAL(valueChanged(double)),
00114             this, SLOT(slotControlPlayFromStart(double)),
00115             Qt::DirectConnection);
00116     playStartButton->set(0);
00117     playStartButtonCOT = new ControlObjectThreadMain(playStartButton);
00118 
00119     // Jump to start and stop button
00120     stopStartButton = new ControlPushButton(ConfigKey(group, "start_stop"));
00121     connect(stopStartButton, SIGNAL(valueChanged(double)),
00122             this, SLOT(slotControlJumpToStartAndStop(double)),
00123             Qt::DirectConnection);
00124     stopStartButton->set(0);
00125     stopStartButtonCOT = new ControlObjectThreadMain(stopStartButton);
00126 
00127     //Stop playback (for sampler)
00128     stopButton = new ControlPushButton(ConfigKey(group, "stop"));
00129     connect(stopButton, SIGNAL(valueChanged(double)),
00130             this, SLOT(slotControlStop(double)),
00131             Qt::DirectConnection);
00132     stopButton->set(0);
00133     stopButtonCOT = new ControlObjectThreadMain(stopButton);
00134 
00135     // Start button
00136     startButton = new ControlPushButton(ConfigKey(group, "start"));
00137     connect(startButton, SIGNAL(valueChanged(double)),
00138             this, SLOT(slotControlStart(double)),
00139             Qt::DirectConnection);
00140 
00141     // End button
00142     endButton = new ControlPushButton(ConfigKey(group, "end"));
00143     connect(endButton, SIGNAL(valueChanged(double)),
00144             this, SLOT(slotControlEnd(double)),
00145             Qt::DirectConnection);
00146 
00147     // Actual rate (used in visuals, not for control)
00148     rateEngine = new ControlObject(ConfigKey(group, "rateEngine"));
00149 
00150     // BPM to display in the UI (updated more slowly than the actual bpm)
00151     visualBpm = new ControlObject(ConfigKey(group, "visual_bpm"));
00152 
00153     // Slider to show and change song position
00154     //these bizarre choices map conveniently to the 0-127 range of midi
00155     playposSlider = new ControlPotmeter(
00156         ConfigKey(group, "playposition"), kMinPlayposRange, kMaxPlayposRange);
00157     connect(playposSlider, SIGNAL(valueChanged(double)),
00158             this, SLOT(slotControlSeek(double)),
00159             Qt::DirectConnection);
00160 
00161     // Control used to communicate ratio playpos to GUI thread
00162     visualPlaypos = new ControlPotmeter(
00163         ConfigKey(group, "visual_playposition"), kMinPlayposRange, kMaxPlayposRange);
00164 
00165     // m_pTrackEnd is used to signal when at end of file during
00166     // playback. TODO(XXX) This should not even be a control object because it
00167     // is an internal flag used only by the EngineBuffer.
00168     m_pTrackEnd = new ControlObject(ConfigKey(group, "TrackEnd"));
00169     //A COTM for use in slots that are called by the GUI thread.
00170     m_pTrackEndCOT = new ControlObjectThreadMain(m_pTrackEnd);
00171 
00172     m_pRepeat = new ControlPushButton(ConfigKey(group, "repeat"));
00173     m_pRepeat->setToggleButton(true);
00174 
00175     // Sample rate
00176     m_pSampleRate = ControlObject::getControl(ConfigKey("[Master]","samplerate"));
00177 
00178     m_pTrackSamples = new ControlObject(ConfigKey(group, "track_samples"));
00179     m_pTrackSampleRate = new ControlObject(ConfigKey(group, "track_samplerate"));
00180 
00181     // Quantization Controller for enabling and disabling the
00182     // quantization (alignment) of loop in/out positions and (hot)cues with
00183     // beats.
00184     addControl(new QuantizeControl(_group, _config));
00185 
00186     // Create the Loop Controller
00187     m_pLoopingControl = new LoopingControl(_group, _config);
00188     addControl(m_pLoopingControl);
00189 
00190 #ifdef __VINYLCONTROL__
00191     // If VinylControl is enabled, add a VinylControlControl. This must be done
00192     // before RateControl is created.
00193     addControl(new VinylControlControl(group, _config));
00194 #endif
00195 
00196     // Create the Rate Controller
00197     m_pRateControl = new RateControl(_group, _config);
00198     addControl(m_pRateControl);
00199 
00200     fwdButton = ControlObject::getControl(ConfigKey(_group, "fwd"));
00201     backButton = ControlObject::getControl(ConfigKey(_group, "back"));
00202 
00203     // Create the BPM Controller
00204     m_pBpmControl = new BpmControl(_group, _config);
00205     addControl(m_pBpmControl);
00206 
00207     m_pReadAheadManager = new ReadAheadManager(m_pReader);
00208     m_pReadAheadManager->addEngineControl(m_pLoopingControl);
00209     m_pReadAheadManager->addEngineControl(m_pRateControl);
00210 
00211     // Construct scaling objects
00212     m_pScaleLinear = new EngineBufferScaleLinear(m_pReadAheadManager);
00213 
00214     m_pScaleST = new EngineBufferScaleST(m_pReadAheadManager);
00215     //m_pScaleST = (EngineBufferScaleST*)new EngineBufferScaleDummy(m_pReadAheadManager);
00216     setPitchIndpTimeStretch(false); // default to VE, let the user specify PITS in their mix
00217 
00218     setNewPlaypos(0.);
00219 
00220     m_pKeylock = new ControlPushButton(ConfigKey(group, "keylock"));
00221     m_pKeylock->setToggleButton(true);
00222     m_pKeylock->set(false);
00223 
00224     m_pEject = new ControlPushButton(ConfigKey(group, "eject"));
00225     connect(m_pEject, SIGNAL(valueChanged(double)),
00226             this, SLOT(slotEjectTrack(double)),
00227             Qt::DirectConnection);
00228 
00229     //m_iRampIter = 0;
00230 
00231     /*df.setFileName("mixxx-debug.csv");
00232     df.open(QIODevice::WriteOnly | QIODevice::Text);
00233     writer.setDevice(&df);*/
00234 }
00235 
00236 EngineBuffer::~EngineBuffer()
00237 {
00238     //close the writer
00239     /*df.close();*/
00240     delete m_pReadAheadManager;
00241     delete m_pReader;
00242 
00243     delete playButtonCOT;
00244     delete playButton;
00245     delete playStartButtonCOT;
00246     delete playStartButton;
00247     delete startButton;
00248     delete endButton;
00249     delete stopStartButtonCOT;
00250     delete stopButtonCOT;
00251     delete stopButton;
00252     delete rateEngine;
00253     delete playposSlider;
00254     delete visualPlaypos;
00255 
00256     delete m_pTrackEndCOT;
00257     delete m_pTrackEnd;
00258 
00259     delete m_pRepeat;
00260 
00261     delete m_pTrackSamples;
00262     delete m_pTrackSampleRate;
00263 
00264     delete m_pScaleLinear;
00265     delete m_pScaleST;
00266 
00267     delete m_pKeylock;
00268     delete m_pEject;
00269 
00270     delete [] m_pDitherBuffer;
00271 
00272     while (m_engineControls.size() > 0) {
00273         EngineControl* pControl = m_engineControls.takeLast();
00274         delete pControl;
00275     }
00276 }
00277 
00278 void EngineBuffer::setPitchIndpTimeStretch(bool b)
00279 {
00280     // MUST ACQUIRE THE PAUSE MUTEX BEFORE CALLING THIS METHOD
00281 
00282     // Change sound scale mode
00283 
00284     //SoundTouch's linear interpolation code doesn't sound very good.
00285     //Our own EngineBufferScaleLinear sounds slightly better, but it's
00286     //not working perfectly. Eventually we should have our own working
00287     //better, so scratching sounds good.
00288 
00289     //Update Dec 30/2007
00290     //If we delete the m_pScale object and recreate it, it eventually
00291     //causes some weird bad pointer somewhere, which will either cause
00292     //the waveform the roll in a weird way or fire an ASSERT from
00293     //visualchannel.cpp or something. Need to valgrind this or something.
00294 
00295     if (b == true) {
00296         m_pScale = m_pScaleST;
00297         ((EngineBufferScaleST *)m_pScaleST)->setPitchIndpTimeStretch(b);
00298     } else {
00299         m_pScale = m_pScaleLinear;
00300     }
00301     m_bScalerChanged = true;
00302 }
00303 
00304 double EngineBuffer::getBpm()
00305 {
00306     return m_pBpmControl->getBpm();
00307 }
00308 
00309 void EngineBuffer::setOtherEngineBuffer(EngineBuffer * pOtherEngineBuffer)
00310 {
00311     if (!m_pOtherEngineBuffer) {
00312         m_pOtherEngineBuffer = pOtherEngineBuffer;
00313         m_pBpmControl->setOtherEngineBuffer(pOtherEngineBuffer);
00314     } else
00315         qCritical("EngineBuffer: Other engine buffer already set!");
00316 }
00317 
00318 void EngineBuffer::setNewPlaypos(double newpos)
00319 {
00320     //qDebug() << "engine new pos " << newpos;
00321 
00322     filepos_play = newpos;
00323 
00324     // Ensures that the playpos slider gets updated in next process call
00325     m_iSamplesCalculated = 1000000;
00326 
00327     // The right place to do this?
00328     if (m_pScale)
00329         m_pScale->clear();
00330     m_pReadAheadManager->notifySeek(filepos_play);
00331 
00332     // Must hold the engineLock while using m_engineControls
00333     m_engineLock.lock();
00334     for (QList<EngineControl*>::iterator it = m_engineControls.begin();
00335          it != m_engineControls.end(); it++) {
00336         EngineControl *pControl = *it;
00337         pControl->notifySeek(filepos_play);
00338     }
00339     m_engineLock.unlock();
00340 }
00341 
00342 const char * EngineBuffer::getGroup()
00343 {
00344     return group;
00345 }
00346 
00347 double EngineBuffer::getRate()
00348 {
00349     return m_pRateControl->getRawRate();
00350 }
00351 
00352 // WARNING: Always called from the EngineWorker thread pool
00353 void EngineBuffer::slotTrackLoaded(TrackPointer pTrack,
00354                                    int iTrackSampleRate,
00355                                    int iTrackNumSamples) {
00356     pause.lock();
00357     m_pCurrentTrack = pTrack;
00358     file_srate_old = iTrackSampleRate;
00359     file_length_old = iTrackNumSamples;
00360     m_pTrackSamples->set(iTrackNumSamples);
00361     m_pTrackSampleRate->set(iTrackSampleRate);
00362     slotControlSeek(0.);
00363 
00364     // Let the engine know that a track is loaded now.
00365     m_pTrackEndCOT->slotSet(0.0f); //XXX: Not sure if to use the COT or CO here
00366 
00367     pause.unlock();
00368 
00369     emit(trackLoaded(pTrack));
00370 }
00371 
00372 // WARNING: Always called from the EngineWorker thread pool
00373 void EngineBuffer::slotTrackLoadFailed(TrackPointer pTrack,
00374                                        QString reason) {
00375     ejectTrack();
00376     emit(trackLoadFailed(pTrack, reason));
00377 }
00378 
00379 TrackPointer EngineBuffer::getLoadedTrack() const {
00380     return m_pCurrentTrack;
00381 }
00382 
00383 void EngineBuffer::ejectTrack() {
00384     // Don't allow ejections while playing a track. We don't need to lock to
00385     // call ControlObject::get() so this is fine.
00386     if (playButton->get() > 0)
00387         return;
00388 
00389     pause.lock();
00390     TrackPointer pTrack = m_pCurrentTrack;
00391     m_pCurrentTrack.clear();
00392     file_srate_old = 0;
00393     file_length_old = 0;
00394     playButton->set(0.0);
00395     slotControlSeek(0.);
00396     m_pTrackSamples->set(0);
00397     m_pTrackSampleRate->set(0);
00398     pause.unlock();
00399 
00400     emit(trackUnloaded(pTrack));
00401 }
00402 
00403 // WARNING: This method runs in both the GUI thread and the Engine Thread
00404 void EngineBuffer::slotControlSeek(double change)
00405 {
00406     if(isnan(change) || change > kMaxPlayposRange || change < kMinPlayposRange) {
00407         // This seek is ridiculous.
00408         return;
00409     }
00410 
00411     // Find new playpos, restrict to valid ranges.
00412     double new_playpos = round(change*file_length_old);
00413 
00414     // TODO(XXX) currently not limiting seeks file_length_old instead of
00415     // kMaxPlayposRange.
00416     if (new_playpos > file_length_old)
00417         new_playpos = file_length_old;
00418 
00419     // Ensure that the file position is even (remember, stereo channel files...)
00420     if (!even((int)new_playpos))
00421         new_playpos--;
00422 
00423     setNewPlaypos(new_playpos);
00424 }
00425 
00426 // WARNING: This method runs in both the GUI thread and the Engine Thread
00427 void EngineBuffer::slotControlSeekAbs(double abs)
00428 {
00429     slotControlSeek(abs/file_length_old);
00430 }
00431 
00432 void EngineBuffer::slotControlPlay(double v)
00433 {
00434     // If no track is currently loaded, turn play off.
00435     if (v > 0.0 && !m_pCurrentTrack) {
00436         playButton->set(0.0f);
00437     }
00438 }
00439 
00440 void EngineBuffer::slotControlStart(double v)
00441 {
00442     if (v > 0.0) {
00443         slotControlSeek(0.);
00444     }
00445 }
00446 
00447 void EngineBuffer::slotControlEnd(double v)
00448 {
00449     if (v > 0.0) {
00450         slotControlSeek(1.);
00451     }
00452 }
00453 
00454 void EngineBuffer::slotControlPlayFromStart(double v)
00455 {
00456     if (v > 0.0) {
00457         slotControlSeek(0.);
00458         playButton->set(1);
00459     }
00460 }
00461 
00462 void EngineBuffer::slotControlJumpToStartAndStop(double v)
00463 {
00464     if (v > 0.0) {
00465         slotControlSeek(0.);
00466         playButton->set(0);
00467     }
00468 }
00469 
00470 void EngineBuffer::slotControlStop(double v)
00471 {
00472     if (v > 0.0) {
00473         playButton->set(0);
00474     }
00475 }
00476 
00477 void EngineBuffer::process(const CSAMPLE *, const CSAMPLE * pOut, const int iBufferSize)
00478 {
00479     Q_ASSERT(even(iBufferSize));
00480     m_pReader->process();
00481     // Steps:
00482     // - Lookup new reader information
00483     // - Calculate current rate
00484     // - Scale the audio with m_pScale, copy the resulting samples into the
00485     //   output buffer
00486     // - Give EngineControl's a chance to do work / request seeks, etc
00487     // - Process repeat mode if we're at the end or beginning of a track
00488     // - Set last sample value (m_fLastSampleValue) so that rampOut works? Other
00489     //   miscellaneous upkeep issues.
00490 
00491     CSAMPLE * pOutput = (CSAMPLE *)pOut;
00492     bool bCurBufferPaused = false;
00493     double rate = 0;
00494 
00495     if (!m_pTrackEnd->get() && pause.tryLock()) {
00496         float sr = m_pSampleRate->get();
00497 
00498         double baserate = 0.0f;
00499         if (sr > 0)
00500             baserate = ((double)file_srate_old/sr);
00501 
00502         bool paused = playButton->get() != 0.0f ? false : true;
00503 
00504         bool is_scratching = false;
00505         rate = m_pRateControl->calculateRate(baserate, paused, iBufferSize,
00506                                              &is_scratching);
00507         //qDebug() << "rate" << rate << " paused" << paused;
00508 
00509         // Scratching always disables keylock because keylock sounds terrible
00510         // when not going at a constant rate.
00511         if (is_scratching && m_pScale != m_pScaleLinear) {
00512             setPitchIndpTimeStretch(false);
00513         } else if (!is_scratching) {
00514             if (m_pKeylock->get() && m_pScale != m_pScaleST) {
00515                 setPitchIndpTimeStretch(true);
00516             } else if (!m_pKeylock->get() && m_pScale == m_pScaleST) {
00517                 setPitchIndpTimeStretch(false);
00518             }
00519         }
00520 
00521         // If the rate has changed, set it in the scale object
00522         if (rate != rate_old || m_bScalerChanged) {
00523             // The rate returned by the scale object can be different from the wanted rate!
00524             // Make sure new scaler has proper position
00525             if (m_bScalerChanged) {
00526                 setNewPlaypos(filepos_play);
00527             } else if (m_pScale != m_pScaleLinear) { //linear scaler does this part for us now
00528                 //XXX: Trying to force RAMAN to read from correct
00529                 //     playpos when rate changes direction - Albert
00530                 if ((rate_old <= 0 && rate > 0) ||
00531                     (rate_old >= 0 && rate < 0)) {
00532                     setNewPlaypos(filepos_play);
00533                 }
00534             }
00535 
00536             rate_old = rate;
00537             if (baserate > 0) //Prevent division by 0
00538                 rate = baserate*m_pScale->setTempo(rate/baserate);
00539             m_pScale->setBaseRate(baserate);
00540             rate_old = rate;
00541             // Scaler is up to date now.
00542             m_bScalerChanged = false;
00543         }
00544 
00545         bool at_start = filepos_play <= 0;
00546         bool at_end = filepos_play >= file_length_old;
00547         bool backwards = rate < 0;
00548 
00549         // If we're playing past the end, playing before the start, or standing
00550         // still then by definition the buffer is paused.
00551         bCurBufferPaused = rate == 0 ||
00552             //(at_start && backwards) ||
00553             (at_end && !backwards);
00554 
00555 
00556         // If the buffer is not paused, then scale the audio.
00557         if (!bCurBufferPaused) {
00558             CSAMPLE *output;
00559 
00560             // The fileposition should be: (why is this thing a double anyway!?
00561             // Integer valued.
00562             double filepos_play_rounded = round(filepos_play);
00563             if (filepos_play_rounded != filepos_play) {
00564                 qWarning() << __FILE__ << __LINE__ << "ERROR: filepos_play is not round:" << filepos_play;
00565                 filepos_play = filepos_play_rounded;
00566             }
00567 
00568             // Even.
00569             if (!even(filepos_play)) {
00570                 qWarning() << "ERROR: filepos_play is not even:" << filepos_play;
00571                 filepos_play--;
00572             }
00573 
00574             // Perform scaling of Reader buffer into buffer.
00575             output = m_pScale->scale(0,
00576                                      iBufferSize,
00577                                      0,
00578                                      0);
00579             double samplesRead = m_pScale->getNewPlaypos();
00580 
00581             // qDebug() << "sourceSamples used " << iSourceSamples
00582             //          <<" samplesRead " << samplesRead
00583             //          << ", buffer pos " << iBufferStartSample
00584             //          << ", play " << filepos_play
00585             //          << " bufferlen " << iBufferSize;
00586 
00587             // Copy scaled audio into pOutput
00588             memcpy(pOutput, output, sizeof(pOutput[0]) * iBufferSize);
00589 
00590             // Adjust filepos_play by the amount we processed. TODO(XXX) what
00591             // happens if samplesRead is a fraction?
00592             filepos_play =
00593                     m_pReadAheadManager->getEffectiveVirtualPlaypositionFromLog(
00594                         static_cast<int>(filepos_play), samplesRead);
00595         } // else (bCurBufferPaused)
00596 
00597         m_engineLock.lock();
00598         QListIterator<EngineControl*> it(m_engineControls);
00599         while (it.hasNext()) {
00600             EngineControl* pControl = it.next();
00601             pControl->setCurrentSample(filepos_play, file_length_old);
00602             double control_seek = pControl->process(rate, filepos_play,
00603                                                     file_length_old, iBufferSize);
00604 
00605             if (control_seek != kNoTrigger) {
00606                 // If we have not processed loops by this point then we have a
00607                 // bug. RAMAN should be in charge of taking loops now. This
00608                 // final step is more to notify all the EngineControls of the
00609                 // happenings of the engine. TODO(rryan) log condition to a
00610                 // stats-pipe once we have them.
00611 
00612                 filepos_play = control_seek;
00613                 double filepos_play_rounded = round(filepos_play);
00614                 if (filepos_play_rounded != filepos_play) {
00615                     qWarning() << __FILE__ << __LINE__ << "ERROR: filepos_play is not round:" << filepos_play;
00616                     filepos_play = filepos_play_rounded;
00617                 }
00618 
00619                 // Fix filepos_play so that it is not out of bounds.
00620                 if (file_length_old > 0) {
00621                     if (filepos_play > file_length_old) {
00622                         // TODO(XXX) limit to kMaxPlayposRange instead of file_length_old
00623                         filepos_play = file_length_old;
00624                     } else if(filepos_play < file_length_old * kMinPlayposRange) {
00625                         filepos_play = kMinPlayposRange * file_length_old;
00626                     }
00627                 }
00628 
00629                 // Safety check that the EngineControl didn't pass us a bogus
00630                 // value
00631                 if (!even(filepos_play))
00632                     filepos_play--;
00633 
00634                 // TODO(XXX) need to re-evaluate this later. If we
00635                 // setNewPlaypos, that clear()'s soundtouch, which might screw
00636                 // up the audio. This sort of jump is a normal event. Also, the
00637                 // EngineControl which caused this jump will get a notifySeek
00638                 // for the same jump which might be confusing. For 1.8.0
00639                 // purposes this works fine. If we do not notifySeek the RAMAN,
00640                 // the engine and RAMAN can get out of sync.
00641 
00642                 //setNewPlaypos(filepos_play);
00643                 m_pReadAheadManager->notifySeek(filepos_play);
00644                 // Notify seek the rate control since it needs to track things
00645                 // like looping. Hacky, I know, but this helps prevent things
00646                 // like the scratch controller from flipping out.
00647                 m_pRateControl->notifySeek(filepos_play);
00648             }
00649         }
00650         m_engineLock.unlock();
00651 
00652 
00653         // Update all the indicators that EngineBuffer publishes to allow
00654         // external parts of Mixxx to observe its status.
00655         updateIndicators(rate, iBufferSize);
00656 
00657         // Handle repeat mode
00658         at_start = filepos_play <= 0;
00659         at_end = filepos_play >= file_length_old;
00660 
00661         bool repeat_enabled = m_pRepeat->get() != 0.0f;
00662 
00663         bool end_of_track = //(at_start && backwards) ||
00664             (at_end && !backwards);
00665 
00666         // If playbutton is pressed, check if we are at start or end of track
00667         if ((playButton->get() || (fwdButton->get() || backButton->get()))
00668             && end_of_track) {
00669             if (repeat_enabled) {
00670                 double seekPosition = at_start ? file_length_old : 0;
00671                 slotControlSeek(seekPosition);
00672             } else {
00673                 playButton->set(0.);
00674             }
00675         }
00676 
00677         // release the pauselock
00678         pause.unlock();
00679     } else { // if (!m_pTrackEnd->get() && pause.tryLock()) {
00680         // If we can't get the pause lock then this buffer will be silence.
00681         bCurBufferPaused = true;
00682     }
00683 
00684     // Give the Reader hints as to which chunks of the current song we
00685     // really care about. It will try very hard to keep these in memory
00686     hintReader(rate, iBufferSize);
00687 
00688     if (m_bLastBufferPaused && !bCurBufferPaused) {
00689         if (fabs(rate) > 0.005) //at very slow forward rates, don't ramp up
00690             m_iRampState = ENGINE_RAMP_UP;
00691     } else if (!m_bLastBufferPaused && bCurBufferPaused) {
00692         m_iRampState = ENGINE_RAMP_DOWN;
00693     } else { //we are not changing state
00694         //make sure we aren't accidentally ramping down
00695         //this is how we make sure that ramp value will become 1.0 eventually
00696         if (fabs(rate) > 0.005 && m_iRampState != ENGINE_RAMP_UP && m_fRampValue < 1.0)
00697             m_iRampState = ENGINE_RAMP_UP;
00698     }
00699 
00700     //let's try holding the last sample value constant, and pull it
00701     //towards zero
00702     float ramp_inc = 0;
00703     if (m_iRampState == ENGINE_RAMP_UP ||
00704         m_iRampState == ENGINE_RAMP_DOWN) {
00705         ramp_inc = m_iRampState * 300 / m_pSampleRate->get();
00706 
00707         for (int i=0; i<iBufferSize; i+=2) {
00708             if (bCurBufferPaused) {
00709                 float dither = m_pDitherBuffer[m_iDitherBufferReadIndex];
00710                 m_iDitherBufferReadIndex = (m_iDitherBufferReadIndex + 1) % MAX_BUFFER_LEN;
00711                 pOutput[i] = m_fLastSampleValue[0] * m_fRampValue + dither;
00712                 pOutput[i+1] = m_fLastSampleValue[1] * m_fRampValue + dither;
00713             } else {
00714                 pOutput[i] = pOutput[i] * m_fRampValue;
00715                 pOutput[i+1] = pOutput[i+1] * m_fRampValue;
00716             }
00717 
00718             m_fRampValue += ramp_inc;
00719             if (m_fRampValue >= 1.0) {
00720                 m_iRampState = ENGINE_RAMP_NONE;
00721                 m_fRampValue = 1.0;
00722             }
00723             if (m_fRampValue <= 0.0) {
00724                 m_iRampState = ENGINE_RAMP_NONE;
00725                 m_fRampValue = 0.0;
00726             }
00727         }
00728     } else if (m_fRampValue == 0.0) {
00729         SampleUtil::applyGain(pOutput, 0.0, iBufferSize);
00730     }
00731 
00732     if ((!bCurBufferPaused && m_iRampState == ENGINE_RAMP_NONE) ||
00733         (bCurBufferPaused && m_fRampValue == 0.0)) {
00734         m_fLastSampleValue[0] = pOutput[iBufferSize-2];
00735         m_fLastSampleValue[1] = pOutput[iBufferSize-1];
00736     }
00737 
00738     /*for (int i=0; i<iBufferSize; i+=2) {
00739         writer << pOutput[i] <<  "\n";
00740     }*/
00741 
00742     m_bLastBufferPaused = bCurBufferPaused;
00743 }
00744 
00745 void EngineBuffer::updateIndicators(double rate, int iBufferSize) {
00746 
00747     // Increase samplesCalculated by the buffer size
00748     m_iSamplesCalculated += iBufferSize;
00749 
00750     double fFractionalPlaypos = 0.0;
00751     if (file_length_old!=0.) {
00752         fFractionalPlaypos = math_min(filepos_play,file_length_old);
00753         fFractionalPlaypos /= file_length_old;
00754     } else {
00755         fFractionalPlaypos = 0.;
00756     }
00757 
00758     // Update indicators that are only updated after every
00759     // sampleRate/kiUpdateRate samples processed.  (e.g. playposSlider,
00760     // rateEngine)
00761     if (m_iSamplesCalculated > (m_pSampleRate->get()/kiUpdateRate)) {
00762         playposSlider->set(fFractionalPlaypos);
00763 
00764         if(rate != rateEngine->get())
00765             rateEngine->set(rate);
00766 
00767         //Update the BPM even more slowly
00768         m_iUiSlowTick = (m_iUiSlowTick + 1) % kiBpmUpdateRate;
00769         if (m_iUiSlowTick == 0) {
00770             visualBpm->set(m_pBpmControl->getBpm());
00771         }
00772 
00773         // Reset sample counter
00774         m_iSamplesCalculated = 0;
00775     }
00776 
00777     // Update visual control object, this needs to be done more often than the
00778     // rateEngine and playpos slider
00779     visualPlaypos->set(fFractionalPlaypos);
00780 }
00781 
00782 void EngineBuffer::hintReader(const double dRate,
00783                               const int iSourceSamples) {
00784     m_engineLock.lock();
00785 
00786     m_hintList.clear();
00787     m_pReadAheadManager->hintReader(dRate, m_hintList, iSourceSamples);
00788 
00789     QListIterator<EngineControl*> it(m_engineControls);
00790     while (it.hasNext()) {
00791         EngineControl* pControl = it.next();
00792         pControl->hintReader(m_hintList);
00793     }
00794     m_pReader->hintAndMaybeWake(m_hintList);
00795 
00796     m_engineLock.unlock();
00797 }
00798 
00799 // WARNING: This method runs in the GUI thread
00800 void EngineBuffer::slotLoadTrack(TrackPointer pTrack) {
00801     // Raise the track end flag so the EngineBuffer stops processing frames
00802     m_pTrackEndCOT->slotSet(1.0);
00803 
00804     //Stop playback
00805     playButtonCOT->slotSet(0.0);
00806 
00807     // Signal to the reader to load the track. The reader will respond with
00808     // either trackLoaded or trackLoadFailed signals.
00809     m_pReader->newTrack(pTrack);
00810     m_pReader->wake();
00811 }
00812 
00813 void EngineBuffer::addControl(EngineControl* pControl) {
00814     // Connect to signals from EngineControl here...
00815     m_engineLock.lock();
00816     m_engineControls.push_back(pControl);
00817     m_engineLock.unlock();
00818     connect(pControl, SIGNAL(seek(double)),
00819             this, SLOT(slotControlSeek(double)),
00820             Qt::DirectConnection);
00821     connect(pControl, SIGNAL(seekAbs(double)),
00822             this, SLOT(slotControlSeekAbs(double)),
00823             Qt::DirectConnection);
00824     connect(this, SIGNAL(trackLoaded(TrackPointer)),
00825             pControl, SLOT(trackLoaded(TrackPointer)),
00826             Qt::DirectConnection);
00827     connect(this, SIGNAL(trackUnloaded(TrackPointer)),
00828             pControl, SLOT(trackUnloaded(TrackPointer)),
00829             Qt::DirectConnection);
00830 }
00831 
00832 void EngineBuffer::bindWorkers(EngineWorkerScheduler* pWorkerScheduler) {
00833     pWorkerScheduler->bindWorker(m_pReader);
00834 }
00835 
00836 bool EngineBuffer::isTrackLoaded() {
00837     if (m_pCurrentTrack) {
00838         return true;
00839     }
00840     return false;
00841 }
00842 
00843 void EngineBuffer::slotEjectTrack(double v) {
00844     if (v > 0) {
00845         ejectTrack();
00846     }
00847 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines