Mixxx

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

Go to the documentation of this file.
00001 // readaheadmanager.cpp
00002 // Created 8/2/2009 by RJ Ryan (rryan@mit.edu)
00003 
00004 #include <QMutexLocker>
00005 
00006 #include "engine/readaheadmanager.h"
00007 
00008 #include "mathstuff.h"
00009 #include "engine/enginecontrol.h"
00010 #include "cachingreader.h"
00011 
00012 
00013 ReadAheadManager::ReadAheadManager(CachingReader* pReader) :
00014     m_iCurrentPosition(0),
00015     m_pReader(pReader) {
00016 }
00017 
00018 ReadAheadManager::~ReadAheadManager() {
00019 }
00020 
00021 int ReadAheadManager::getNextSamples(double dRate, CSAMPLE* buffer,
00022                                      int requested_samples) {
00023     Q_ASSERT(even(requested_samples));
00024 
00025     bool in_reverse = dRate < 0;
00026     int start_sample = m_iCurrentPosition;
00027     //qDebug() << "start" << start_sample << requested_samples;
00028     int samples_needed = requested_samples;
00029     CSAMPLE* base_buffer = buffer;
00030 
00031     // A loop will only limit the amount we can read in one shot.
00032 
00033     QPair<int, double> next_loop;
00034     next_loop.first = 0;
00035     next_loop.second = m_sEngineControls[0]->nextTrigger(dRate,
00036                                                          m_iCurrentPosition,
00037                                                          0, 0);
00038 
00039     if (next_loop.second != kNoTrigger) {
00040         int samples_available;
00041         if (in_reverse) {
00042             samples_available = m_iCurrentPosition - next_loop.second;
00043         } else {
00044             samples_available = next_loop.second - m_iCurrentPosition;
00045         }
00046         samples_needed = math_max(0, math_min(samples_needed,
00047                                               samples_available));
00048     }
00049 
00050     if (in_reverse) {
00051         start_sample = m_iCurrentPosition - samples_needed;
00052         /*if (start_sample < 0) {
00053             samples_needed = math_max(0, samples_needed + start_sample);
00054             start_sample = 0;
00055         }*/
00056     }
00057 
00058     // Sanity checks
00059     //Q_ASSERT(start_sample >= 0);
00060     Q_ASSERT(samples_needed >= 0);
00061 
00062     int samples_read = m_pReader->read(start_sample, samples_needed,
00063                                        base_buffer);
00064 
00065     if (samples_read != samples_needed)
00066         qDebug() << "didn't get what we wanted" << samples_read << samples_needed;
00067 
00068     // Increment or decrement current read-ahead position
00069     if (in_reverse) {
00070         addReadLogEntry(m_iCurrentPosition, m_iCurrentPosition - samples_read);
00071         m_iCurrentPosition -= samples_read;
00072     } else {
00073         addReadLogEntry(m_iCurrentPosition, m_iCurrentPosition + samples_read);
00074         m_iCurrentPosition += samples_read;
00075     }
00076 
00077     // Activate on this trigger if necessary
00078     if (next_loop.second != kNoTrigger) {
00079         double loop_trigger = next_loop.second;
00080         double loop_target = m_sEngineControls[next_loop.first]->
00081             getTrigger(dRate,
00082                        m_iCurrentPosition,
00083                        0, 0);
00084 
00085         if (loop_target != kNoTrigger &&
00086             ((in_reverse && m_iCurrentPosition <= loop_trigger) ||
00087             (!in_reverse && m_iCurrentPosition >= loop_trigger))) {
00088             m_iCurrentPosition = loop_target;
00089         }
00090     }
00091 
00092     // Reverse the samples in-place
00093     if (in_reverse) {
00094         // TODO(rryan) pull this into MixxxUtil or something
00095         CSAMPLE temp1, temp2;
00096         for (int j = 0; j < samples_read/2; j += 2) {
00097             const int endpos = samples_read-1-j-1;
00098             temp1 = base_buffer[j];
00099             temp2 = base_buffer[j+1];
00100             base_buffer[j] = base_buffer[endpos];
00101             base_buffer[j+1] = base_buffer[endpos+1];
00102             base_buffer[endpos] = temp1;
00103             base_buffer[endpos+1] = temp2;
00104         }
00105     }
00106 
00107     //qDebug() << "read" << m_iCurrentPosition << samples_read;
00108     return samples_read;
00109 }
00110 
00111 void ReadAheadManager::addEngineControl(EngineControl* pControl) {
00112     Q_ASSERT(pControl);
00113     m_sEngineControls.append(pControl);
00114 }
00115 
00116 void ReadAheadManager::setNewPlaypos(int iNewPlaypos) {
00117     QMutexLocker locker(&m_mutex);
00118     m_iCurrentPosition = iNewPlaypos;
00119     m_readAheadLog.clear();
00120 }
00121 
00122 void ReadAheadManager::notifySeek(int iSeekPosition) {
00123     QMutexLocker locker(&m_mutex);
00124     m_iCurrentPosition = iSeekPosition;
00125     m_readAheadLog.clear();
00126 
00127     // TODO(XXX) notifySeek on the engine controls. EngineBuffer currently does
00128     // a fine job of this so it isn't really necessary but eventually I think
00129     // RAMAN should do this job. rryan 11/2011
00130 
00131     // foreach (EngineControl* pControl, m_sEngineControls) {
00132     //     pControl->notifySeek(iSeekPosition);
00133     // }
00134 }
00135 
00136 void ReadAheadManager::hintReader(double dRate, QList<Hint>& hintList,
00137                                   int iSamplesPerBuffer) {
00138     bool in_reverse = dRate < 0;
00139     Hint current_position;
00140 
00141     // SoundTouch can read up to 2 chunks ahead. Always keep 2 chunks ahead in
00142     // cache.
00143     int length_to_cache = 2*CachingReader::kSamplesPerChunk;
00144 
00145     current_position.length = length_to_cache;
00146     current_position.sample = in_reverse ?
00147             m_iCurrentPosition - length_to_cache :
00148             m_iCurrentPosition;
00149 
00150     // If we are trying to cache before the start of the track,
00151     // Then we don't need to cache because it's all zeros!
00152     if (current_position.sample < 0 &&
00153         current_position.sample + current_position.length < 0)
00154         return;
00155 
00156     // top priority, we need to read this data immediately
00157     current_position.priority = 1;
00158     hintList.append(current_position);
00159 }
00160 
00161 void ReadAheadManager::addReadLogEntry(double virtualPlaypositionStart,
00162                                        double virtualPlaypositionEndNonInclusive) {
00163     QMutexLocker locker(&m_mutex);
00164     ReadLogEntry newEntry(virtualPlaypositionStart,
00165                           virtualPlaypositionEndNonInclusive);
00166     if (m_readAheadLog.size() > 0) {
00167         ReadLogEntry& last = m_readAheadLog.last();
00168         if (last.merge(newEntry)) {
00169             return;
00170         }
00171     }
00172     m_readAheadLog.append(newEntry);
00173 }
00174 
00175 int ReadAheadManager::getEffectiveVirtualPlaypositionFromLog(double currentVirtualPlayposition,
00176                                                              double numConsumedSamples) {
00177     if (numConsumedSamples == 0) {
00178         return currentVirtualPlayposition;
00179     }
00180 
00181     QMutexLocker locker(&m_mutex);
00182     if (m_readAheadLog.size() == 0) {
00183         // No log entries to read from.
00184         qDebug() << this << "No read ahead log entries to read from. Case not currently handled.";
00185         // TODO(rryan) log through a stats pipe eventually
00186         return currentVirtualPlayposition;
00187     }
00188 
00189     double virtualPlayposition = 0;
00190     bool shouldNotifySeek = false;
00191     bool direction = true;
00192     while (m_readAheadLog.size() > 0 && numConsumedSamples > 0) {
00193         ReadLogEntry& entry = m_readAheadLog.first();
00194         direction = entry.direction();
00195 
00196         // Notify EngineControls that we have taken a seek.
00197         if (shouldNotifySeek) {
00198             foreach (EngineControl* pControl, m_sEngineControls) {
00199                 pControl->notifySeek(entry.virtualPlaypositionStart);
00200             }
00201         }
00202 
00203         double consumed = entry.consume(numConsumedSamples);
00204         numConsumedSamples -= consumed;
00205 
00206         // Advance our idea of the current virtual playposition to this
00207         // ReadLogEntry's start position.
00208         virtualPlayposition = entry.virtualPlaypositionStart;
00209 
00210         if (entry.length() == 0) {
00211             // This entry is empty now.
00212             m_readAheadLog.removeFirst();
00213         }
00214         shouldNotifySeek = true;
00215     }
00216     int result = 0;
00217     if (direction) {
00218         result = static_cast<int>(floor(virtualPlayposition));
00219         if (!even(result)) {
00220             result--;
00221         }
00222     } else {
00223         result = static_cast<int>(ceil(virtualPlayposition));
00224         if (!even(result)) {
00225             result++;
00226         }
00227     }
00228     return result;
00229 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines