Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/cachingreader.cpp

Go to the documentation of this file.
00001 
00002 #include <math.h>
00003 
00004 #include <QtDebug>
00005 #include <QFileInfo>
00006 
00007 #include "controlobject.h"
00008 #include "controlobjectthread.h"
00009 
00010 #include "cachingreader.h"
00011 #include "trackinfoobject.h"
00012 #include "soundsourceproxy.h"
00013 #include "sampleutil.h"
00014 
00015 
00016 // There's a little math to this, but not much: 48khz stereo audio is 384kb/sec
00017 // if using float samples. We want the chunk size to be a power of 2 so it's
00018 // easier to memory align, and roughly 1/2 - 1/4th of a second of audio. 2**17
00019 // and 2**16 are nice candidates. 2**16 is 170ms of audio, which is well above
00020 // (hopefully) the latencies people are seeing. at 10ms latency, one chunk is
00021 // enough for 17 callbacks. We may need to tweak this later.
00022 
00023 // Must be divisible by 8, 4, and 2. Just pick a power of 2.
00024 #define CHUNK_LENGTH 65536
00025 //#define CHUNK_LENGTH 524288
00026 
00027 const int CachingReader::kChunkLength = CHUNK_LENGTH;
00028 const int CachingReader::kSamplesPerChunk = CHUNK_LENGTH / sizeof(CSAMPLE);
00029 
00030 CachingReader::CachingReader(const char* _group,
00031                              ConfigObject<ConfigValue>* _config) :
00032         m_pGroup(_group),
00033         m_pConfig(_config),
00034         m_chunkReadRequestFIFO(1024),
00035         m_readerStatusFIFO(1024),
00036         m_readerStatus(INVALID),
00037         m_mruChunk(NULL),
00038         m_lruChunk(NULL),
00039         m_pRawMemoryBuffer(NULL),
00040         m_pCurrentSoundSource(NULL),
00041         m_iTrackSampleRate(0),
00042         m_iTrackNumSamples(0),
00043         m_iTrackNumSamplesCallbackSafe(0),
00044         m_pSample(NULL) {
00045     initialize();
00046 }
00047 
00048 CachingReader::~CachingReader() {
00049     m_freeChunks.clear();
00050     m_allocatedChunks.clear();
00051     m_lruChunk = m_mruChunk = NULL;
00052 
00053     for (int i=0; i < m_chunks.size(); i++) {
00054         Chunk* c = m_chunks[i];
00055         delete c;
00056     }
00057 
00058     delete [] m_pSample;
00059 
00060     delete [] m_pRawMemoryBuffer;
00061     m_pRawMemoryBuffer = NULL;
00062 
00063     delete m_pCurrentSoundSource;
00064 }
00065 
00066 void CachingReader::initialize() {
00067     int memory_to_use = 5000000; // 5mb, TODO
00068     Q_ASSERT(memory_to_use >= kChunkLength);
00069 
00070     // Only allocate as many bytes as we will actually use.
00071     memory_to_use -= (memory_to_use % kChunkLength);
00072 
00073     m_pSample = new SAMPLE[kSamplesPerChunk];
00074 
00075     Q_ASSERT(kSamplesPerChunk * sizeof(CSAMPLE) == kChunkLength);
00076 
00077     int total_chunks = memory_to_use / kChunkLength;
00078 
00079     qDebug() << "CachingReader using" << memory_to_use << "bytes.";
00080 
00081     int rawMemoryBufferLength = kSamplesPerChunk * total_chunks;
00082     m_pRawMemoryBuffer = new CSAMPLE[rawMemoryBufferLength];
00083 
00084     m_allocatedChunks.reserve(total_chunks);
00085 
00086     CSAMPLE* bufferStart = m_pRawMemoryBuffer;
00087 
00088     // Divide up the allocated raw memory buffer into total_chunks
00089     // chunks. Initialize each chunk to hold nothing and add it to the free
00090     // list.
00091     for (int i=0; i < total_chunks; i++) {
00092         Chunk* c = new Chunk;
00093         c->chunk_number = -1;
00094         c->length = 0;
00095         c->data = bufferStart;
00096         c->next_lru = NULL;
00097         c->prev_lru = NULL;
00098 
00099         m_chunks.push_back(c);
00100         m_freeChunks.push_back(c);
00101 
00102         bufferStart += kSamplesPerChunk;
00103     }
00104 }
00105 
00106 // static
00107 Chunk* CachingReader::removeFromLRUList(Chunk* chunk, Chunk* head) {
00108     Q_ASSERT(chunk);
00109 
00110     // Remove chunk from the doubly-linked list.
00111     Chunk* next = chunk->next_lru;
00112     Chunk* prev = chunk->prev_lru;
00113 
00114     if (next) {
00115         next->prev_lru = prev;
00116     }
00117 
00118     if (prev) {
00119         prev->next_lru = next;
00120     }
00121 
00122     chunk->next_lru = NULL;
00123     chunk->prev_lru = NULL;
00124 
00125     if (chunk == head)
00126         return next;
00127 
00128     return head;
00129 }
00130 
00131 // static
00132 Chunk* CachingReader::insertIntoLRUList(Chunk* chunk, Chunk* head) {
00133     Q_ASSERT(chunk);
00134 
00135     // Chunk is the new head of the list, so connect the head as the next from
00136     // chunk.
00137     chunk->next_lru = head;
00138     chunk->prev_lru = NULL;
00139 
00140     // If there are any elements in the list, point their prev pointer back at
00141     // chunk since it is the new head
00142     if (head) {
00143         head->prev_lru = chunk;
00144     }
00145 
00146     // Chunk is the new head
00147     return chunk;
00148 }
00149 
00150 
00151 void CachingReader::freeChunk(Chunk* pChunk) {
00152     int removed = m_allocatedChunks.remove(pChunk->chunk_number);
00153 
00154     // We'll tolerate not being in allocatedChunks because sometime you free a
00155     // chunk right after you allocated it.
00156     Q_ASSERT(removed <= 1);
00157 
00158     // If this is the LRU chunk then set its previous LRU chunk to the LRU
00159     if (m_lruChunk == pChunk) {
00160         m_lruChunk = pChunk->prev_lru;
00161     }
00162 
00163     m_mruChunk = removeFromLRUList(pChunk, m_mruChunk);
00164 
00165     pChunk->chunk_number = -1;
00166     pChunk->length = 0;
00167     m_freeChunks.push_back(pChunk);
00168 }
00169 
00170 void CachingReader::freeAllChunks() {
00171     m_allocatedChunks.clear();
00172     m_mruChunk = NULL;
00173 
00174     QSet<Chunk*> reserved = QSet<Chunk*>::fromList(m_chunksBeingRead.values());
00175 
00176     for (int i=0; i < m_chunks.size(); i++) {
00177         Chunk* c = m_chunks[i];
00178         if (reserved.contains(c)) {
00179             continue;
00180         }
00181         if (!m_freeChunks.contains(c)) {
00182             c->chunk_number = -1;
00183             c->length = 0;
00184             c->next_lru = NULL;
00185             c->prev_lru = NULL;
00186             m_freeChunks.push_back(c);
00187         }
00188     }
00189 }
00190 
00191 Chunk* CachingReader::allocateChunk() {
00192     if (m_freeChunks.count() == 0)
00193         return NULL;
00194     return m_freeChunks.takeFirst();
00195 }
00196 
00197 Chunk* CachingReader::allocateChunkExpireLRU() {
00198     Chunk* chunk = allocateChunk();
00199     if (chunk == NULL) {
00200         Q_ASSERT(m_lruChunk);
00201         //qDebug() << "Expiring LRU" << m_lruChunk << m_lruChunk->chunk_number;
00202         freeChunk(m_lruChunk);
00203         chunk = allocateChunk();
00204         Q_ASSERT(chunk);
00205     }
00206     //qDebug() << "allocateChunkExpireLRU" << chunk;
00207     return chunk;
00208 }
00209 
00210 Chunk* CachingReader::lookupChunk(int chunk_number) {
00211     // Defaults to NULL if it's not in the hash.
00212     Chunk* chunk = NULL;
00213 
00214     if (m_allocatedChunks.contains(chunk_number)) {
00215         chunk = m_allocatedChunks.value(chunk_number);
00216 
00217         // Make sure we're all in agreement here.
00218         Q_ASSERT(chunk_number == chunk->chunk_number);
00219 
00220 
00221         // If this is the LRU chunk then set the previous LRU to the new LRU
00222         if (chunk == m_lruChunk && chunk->prev_lru != NULL) {
00223             m_lruChunk = chunk->prev_lru;
00224         }
00225         // Remove the chunk from the list and insert it at the head.
00226         m_mruChunk = removeFromLRUList(chunk, m_mruChunk);
00227         m_mruChunk = insertIntoLRUList(chunk, m_mruChunk);
00228     }
00229 
00230     return chunk;
00231 }
00232 
00233 void CachingReader::processChunkReadRequest(ChunkReadRequest* request,
00234                                             ReaderStatusUpdate* update) {
00235     int chunk_number = request->chunk->chunk_number;
00236     //qDebug() << "Processing ChunkReadRequest for" << chunk_number;
00237     update->status = CHUNK_READ_INVALID;
00238     update->chunk = request->chunk;
00239     update->chunk->length = 0;
00240 
00241     if (m_pCurrentSoundSource == NULL || chunk_number < 0) {
00242         return;
00243     }
00244 
00245     // Stereo samples
00246     int sample_position = sampleForChunk(chunk_number);
00247     int samples_remaining = m_iTrackNumSamples - sample_position;
00248     int samples_to_read = math_min(kSamplesPerChunk, samples_remaining);
00249 
00250     // Bogus chunk number
00251     if (samples_to_read <= 0) {
00252         update->status = CHUNK_READ_EOF;
00253         return;
00254     }
00255 
00256     m_pCurrentSoundSource->seek(sample_position);
00257     int samples_read = m_pCurrentSoundSource->read(samples_to_read,
00258                                                    m_pSample);
00259 
00260     // If we've run out of music, the SoundSource can return 0 samples.
00261     // Remember that SoundSourc->getLength() (which is m_iTrackNumSamples) can
00262     // lie to us about the length of the song!
00263     if (samples_read <= 0) {
00264         update->status = CHUNK_READ_EOF;
00265         return;
00266     }
00267 
00268     // TODO(XXX) This loop can't be done with a memcpy, but could be done with
00269     // SSE.
00270     CSAMPLE* buffer = request->chunk->data;
00271     //qDebug() << "Reading into " << buffer;
00272     SampleUtil::convert(buffer, m_pSample, samples_read);
00273     update->status = CHUNK_READ_SUCCESS;
00274     update->chunk->length = samples_read;
00275 }
00276 
00277 void CachingReader::newTrack(TrackPointer pTrack) {
00278     m_trackQueueMutex.lock();
00279     m_trackQueue.enqueue(pTrack);
00280     m_trackQueueMutex.unlock();
00281 }
00282 
00283 void CachingReader::process() {
00284     ReaderStatusUpdate status;
00285     while (m_readerStatusFIFO.read(&status, 1) == 1) {
00286         // qDebug() << "Got ReaderStatusUpdate:" << status.status
00287         //          << (status.chunk ? status.chunk->chunk_number : -1);
00288         if (status.status == TRACK_NOT_LOADED) {
00289             m_readerStatus = status.status;
00290         } else if (status.status == TRACK_LOADED) {
00291             freeAllChunks();
00292             m_readerStatus = status.status;
00293             m_iTrackNumSamplesCallbackSafe = status.trackNumSamples;
00294         } else if (status.status == CHUNK_READ_SUCCESS) {
00295             Chunk* pChunk = status.chunk;
00296             Q_ASSERT(pChunk != NULL);
00297             Chunk* pChunk2 = m_chunksBeingRead.take(pChunk->chunk_number);
00298             if (pChunk2 != pChunk) {
00299                 qDebug() << "Mismatch in requested chunk to read!";
00300             }
00301 
00302             Chunk* pAlreadyExisting = lookupChunk(pChunk->chunk_number);
00303             // If this chunk is already in the cache, then we just freshened
00304             // it. Free this chunk.
00305             if (pAlreadyExisting != NULL) {
00306                 qDebug() << "CHUNK" << pChunk->chunk_number << "ALREADY EXISTS!";
00307                 freeChunk(pChunk);
00308             } else {
00309                 //qDebug() << "Inserting chunk" << pChunk << pChunk->chunk_number;
00310                 m_allocatedChunks.insert(pChunk->chunk_number, pChunk);
00311 
00312                 // Insert the chunk into the LRU list
00313                 m_mruChunk = insertIntoLRUList(pChunk, m_mruChunk);
00314 
00315                 // If this chunk has no next LRU then it is the LRU. This only
00316                 // happens if this is the first allocated chunk.
00317                 if (pChunk->next_lru == NULL) {
00318                     m_lruChunk = pChunk;
00319                 }
00320             }
00321         } else if (status.status == CHUNK_READ_EOF) {
00322             Chunk* pChunk = status.chunk;
00323             Q_ASSERT(pChunk != NULL);
00324             Chunk* pChunk2 = m_chunksBeingRead.take(pChunk->chunk_number);
00325             if (pChunk2 != pChunk) {
00326                 qDebug() << "Mismatch in requested chunk to read!";
00327             }
00328             freeChunk(pChunk);
00329         } else if (status.status == CHUNK_READ_INVALID) {
00330             qDebug() << "WARNING: READER THREAD RECEIVED INVALID CHUNK READ";
00331             Chunk* pChunk = status.chunk;
00332             Q_ASSERT(pChunk != NULL);
00333             Chunk* pChunk2 = m_chunksBeingRead.take(pChunk->chunk_number);
00334             if (pChunk2 != pChunk) {
00335                 qDebug() << "Mismatch in requested chunk to read!";
00336             }
00337             freeChunk(pChunk);
00338         }
00339     }
00340 }
00341 
00342 int CachingReader::read(int sample, int num_samples, CSAMPLE* buffer) {
00343     int zerosWritten = 0;
00344     // Check for bogus sample numbers
00345     //Q_ASSERT(sample >= 0);
00346     QString temp = QString("Sample = %1").arg(sample);
00347     QByteArray tempBA = QString(temp).toUtf8();
00348     Q_ASSERT_X(sample % 2 == 0,"CachingReader::read",tempBA);
00349     Q_ASSERT(num_samples >= 0);
00350 
00351     // If asked to read 0 samples, don't do anything. (this is a perfectly
00352     // reasonable request that happens sometimes. If no track is loaded, don't
00353     // do anything.
00354     if (num_samples == 0 || m_readerStatus != TRACK_LOADED) {
00355         return 0;
00356     }
00357 
00358     // Process messages from the reader thread.
00359     process();
00360 
00361     // TODO: is it possible to move this code out of caching reader
00362     // and into enginebuffer?  It doesn't quite make sense here, although
00363     // it makes preroll completely transparent to the rest of the code
00364 
00365     //if we're in preroll...
00366     if (sample < 0) {
00367         if (sample + num_samples <= 0) {
00368             //everything is zeros, easy
00369             memset(buffer, 0, sizeof(*buffer) * num_samples);
00370             return num_samples;
00371         } else {
00372             //some of the buffer is zeros, some is from the file
00373             memset(buffer, 0, sizeof(*buffer) * (0 - sample));
00374             buffer += (0 - sample);
00375             num_samples = sample + num_samples;
00376             zerosWritten = (0 - sample);
00377             sample = 0;
00378             //continue processing the rest of the chunks normally
00379         }
00380     }
00381 
00382     int start_sample = math_min(m_iTrackNumSamplesCallbackSafe,
00383                                 sample);
00384     int start_chunk = chunkForSample(start_sample);
00385     int end_sample = math_min(m_iTrackNumSamplesCallbackSafe,
00386                               sample + num_samples - 1);
00387     int end_chunk = chunkForSample(end_sample);
00388 
00389     int samples_remaining = num_samples;
00390     int current_sample = sample;
00391 
00392     // Sanity checks
00393     Q_ASSERT(start_chunk <= end_chunk);
00394 
00395     for (int chunk_num = start_chunk; chunk_num <= end_chunk; chunk_num++) {
00396         Chunk* current = lookupChunk(chunk_num);
00397 
00398         // If the chunk is not in cache, then we must return an error.
00399         if (current == NULL) {
00400             qDebug() << "Couldn't get chunk " << chunk_num
00401                      << " in read() of [" << sample << "," << sample+num_samples
00402                      << "] chunks " << start_chunk << "-" << end_chunk;
00403 
00404             // Something is wrong. Break out of the loop, that should fill the
00405             // samples requested with zeroes.
00406             break;
00407         }
00408 
00409         int chunk_start_sample = sampleForChunk(chunk_num);
00410         int chunk_offset = current_sample - chunk_start_sample;
00411         int chunk_remaining_samples = current->length - chunk_offset;
00412 
00413         // More sanity checks
00414         Q_ASSERT(current_sample >= chunk_start_sample);
00415         Q_ASSERT(current_sample % 2 == 0);
00416 
00417         if (start_chunk != chunk_num) {
00418             Q_ASSERT(chunk_start_sample == current_sample);
00419         }
00420 
00421         Q_ASSERT(samples_remaining >= 0);
00422         // It is completely possible that chunk_remaining_samples is less than
00423         // zero. If the caller is trying to read from beyond the end of the
00424         // file, then this can happen. We should tolerate it.
00425 
00426         int samples_to_read = math_max(0, math_min(samples_remaining,
00427                                                    chunk_remaining_samples));
00428 
00429         // samples_to_read should be non-negative and even
00430         Q_ASSERT(samples_to_read >= 0);
00431         Q_ASSERT(samples_to_read % 2 == 0);
00432 
00433         CSAMPLE *data = current->data + chunk_offset;
00434 
00435         // If we did not decide to read any samples from this chunk then that
00436         // means we have exhausted all the samples in the song.
00437         if (samples_to_read == 0) {
00438             break;
00439         }
00440 
00441         // TODO(rryan) do a test and see if using memcpy is faster than gcc
00442         // optimizing the for loop
00443         memcpy(buffer, data, sizeof(*buffer) * samples_to_read);
00444         // for (int i=0; i < samples_to_read; i++) {
00445         //     buffer[i] = data[i];
00446         // }
00447 
00448         buffer += samples_to_read;
00449         current_sample += samples_to_read;
00450         samples_remaining -= samples_to_read;
00451     }
00452 
00453     // If we didn't supply all the samples requested, that probably means we're
00454     // at the end of the file, or something is wrong. Provide zeroes and pretend
00455     // all is well. The caller can't be bothered to check how long the file is.
00456     // TODO(XXX) memset
00457     for (int i=0; i<samples_remaining; i++) {
00458         buffer[i] = 0.0f;
00459     }
00460     samples_remaining = 0;
00461 
00462     Q_ASSERT(samples_remaining == 0);
00463     return zerosWritten + num_samples - samples_remaining;
00464 }
00465 
00466 void CachingReader::hintAndMaybeWake(QList<Hint>& hintList) {
00467     // If no file is loaded, skip.
00468     if (m_readerStatus != TRACK_LOADED) {
00469         return;
00470     }
00471 
00472     QListIterator<Hint> iterator(hintList);
00473 
00474     // To prevent every bit of code having to guess how many samples
00475     // forward it makes sense to keep in memory, the hinter can provide
00476     // either 0 for a forward hint or -1 for a backward hint. We should
00477     // be calculating an appropriate number of samples to go backward as
00478     // some function of the latency, but for now just leave this as a
00479     // constant. 2048 is a pretty good number of samples because 25ms
00480     // latency corresponds to 1102.5 mono samples and we need double
00481     // that for stereo samples.
00482     const int default_samples = 2048;
00483 
00484     QSet<int> chunksToFreshen;
00485     while (iterator.hasNext()) {
00486         // Copy, don't use reference.
00487         Hint hint = iterator.next();
00488 
00489         if (hint.length == 0) {
00490             hint.length = default_samples;
00491         } else if (hint.length == -1) {
00492             hint.sample -= default_samples;
00493             hint.length = default_samples;
00494             if (hint.sample < 0) {
00495                 hint.length += hint.sample;
00496                 hint.sample = 0;
00497             }
00498         }
00499         Q_ASSERT(hint.length >= 0);
00500         int start_sample = math_max(0, math_min(
00501             m_iTrackNumSamplesCallbackSafe, hint.sample));
00502         int start_chunk = chunkForSample(start_sample);
00503         int end_sample = math_max(0, math_min(
00504             m_iTrackNumSamplesCallbackSafe, hint.sample + hint.length - 1));
00505         int end_chunk = chunkForSample(end_sample);
00506 
00507         for (int current = start_chunk; current <= end_chunk; ++current) {
00508             chunksToFreshen.insert(current);
00509         }
00510     }
00511 
00512     // For every chunk that the hints indicated, check if it is in the cache. If
00513     // any are not, then wake.
00514     bool shouldWake = false;
00515     QSetIterator<int> setIterator(chunksToFreshen);
00516     while (setIterator.hasNext()) {
00517         int chunk = setIterator.next();
00518 
00519         // This will cause the chunk to be 'freshened' in the cache. The
00520         // chunk will be moved to the end of the LRU list.
00521         if (!m_chunksBeingRead.contains(chunk) && lookupChunk(chunk) == NULL) {
00522             shouldWake = true;
00523             Chunk* pChunk = allocateChunkExpireLRU();
00524             Q_ASSERT(pChunk != NULL);
00525 
00526             m_chunksBeingRead.insert(chunk, pChunk);
00527             ChunkReadRequest request;
00528             pChunk->chunk_number = chunk;
00529             request.chunk = pChunk;
00530             // qDebug() << "Requesting read of chunk" << chunk << "into" << pChunk;
00531             // qDebug() << "Requesting read into " << request.chunk->data;
00532             if (m_chunkReadRequestFIFO.write(&request, 1) != 1) {
00533                 qDebug() << "ERROR: Could not submit read request for "
00534                          << chunk;
00535             }
00536             //qDebug() << "Checking chunk " << chunk << " shouldWake:" << shouldWake << " chunksToRead" << m_chunksToRead.size();
00537         }
00538     }
00539 
00540     // If there are chunks to be read, wake up.
00541     if (shouldWake) {
00542         wake();
00543     }
00544 }
00545 
00546 void CachingReader::run() {
00547     // Notify the EngineWorkerScheduler that the work we scheduled is starting.
00548     emit(workStarting(this));
00549 
00550     m_trackQueueMutex.lock();
00551     TrackPointer pLoadTrack = TrackPointer();
00552     if (!m_trackQueue.isEmpty()) {
00553         pLoadTrack = m_trackQueue.takeLast();
00554         m_trackQueue.clear();
00555     }
00556     m_trackQueueMutex.unlock();
00557 
00558     if (pLoadTrack) {
00559         loadTrack(pLoadTrack);
00560     } else {
00561         // Read the requested chunks.
00562         ChunkReadRequest request;
00563         ReaderStatusUpdate status;
00564         while (m_chunkReadRequestFIFO.read(&request, 1) == 1) {
00565             processChunkReadRequest(&request, &status);
00566             m_readerStatusFIFO.writeBlocking(&status, 1);
00567         }
00568     }
00569 
00570     // Notify the EngineWorkerScheduler that the work we did is done.
00571     emit(workDone(this));
00572 }
00573 
00574 void CachingReader::wake() {
00575     //qDebug() << m_pGroup << "CachingReader::wake()";
00576     emit(workReady(this));
00577 }
00578 
00579 void CachingReader::loadTrack(TrackPointer pTrack) {
00580     //qDebug() << m_pGroup << "CachingReader::loadTrack() lock acquired for load.";
00581 
00582     ReaderStatusUpdate status;
00583     status.status = TRACK_LOADED;
00584     status.chunk = NULL;
00585     status.trackNumSamples = 0;
00586 
00587     if (m_pCurrentSoundSource != NULL) {
00588         delete m_pCurrentSoundSource;
00589         m_pCurrentSoundSource = NULL;
00590     }
00591     m_iTrackSampleRate = 0;
00592     m_iTrackNumSamples = 0;
00593 
00594     QString filename = pTrack->getLocation();
00595 
00596     if (filename.isEmpty() || !pTrack->exists()) {
00597         // Must unlock before emitting to avoid deadlock
00598         qDebug() << m_pGroup << "CachingReader::loadTrack() load failed for\""
00599                  << filename << "\", unlocked reader lock";
00600         status.status = TRACK_NOT_LOADED;
00601         m_readerStatusFIFO.writeBlocking(&status, 1);
00602         emit(trackLoadFailed(
00603             pTrack, QString("The file '%1' could not be found.").arg(filename)));
00604         return;
00605     }
00606 
00607     m_pCurrentSoundSource = new SoundSourceProxy(pTrack);
00608     bool open(m_pCurrentSoundSource->open() == OK); //Open the song for reading
00609     m_iTrackSampleRate = m_pCurrentSoundSource->getSampleRate();
00610     m_iTrackNumSamples = status.trackNumSamples =
00611             m_pCurrentSoundSource->length();
00612 
00613     if (!open || m_iTrackNumSamples == 0 || m_iTrackSampleRate == 0) {
00614         // Must unlock before emitting to avoid deadlock
00615         qDebug() << m_pGroup << "CachingReader::loadTrack() load failed for\""
00616                  << filename << "\", file invalid, unlocked reader lock";
00617         status.status = TRACK_NOT_LOADED;
00618         m_readerStatusFIFO.writeBlocking(&status, 1);
00619         emit(trackLoadFailed(
00620             pTrack, QString("The file '%1' could not be loaded.").arg(filename)));
00621         return;
00622     }
00623 
00624     m_readerStatusFIFO.writeBlocking(&status, 1);
00625 
00626     // Clear the chunks to read list.
00627     ChunkReadRequest request;
00628     while (m_chunkReadRequestFIFO.read(&request, 1) == 1) {
00629         qDebug() << "Skipping read request for " << request.chunk->chunk_number;
00630         status.status = CHUNK_READ_INVALID;
00631         status.chunk = request.chunk;
00632         m_readerStatusFIFO.writeBlocking(&status, 1);
00633     }
00634 
00635     // Emit that the track is loaded.
00636     emit(trackLoaded(pTrack, m_iTrackSampleRate, m_iTrackNumSamples));
00637 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines