void CachingReaderWorker::processChunkReadRequest(ChunkReadRequest* request, ReaderStatusUpdate* update) { int chunk_number = request->chunk->chunk_number; //qDebug() << "Processing ChunkReadRequest for" << chunk_number; update->chunk = request->chunk; update->chunk->length = 0; if (m_pCurrentSoundSource == NULL || chunk_number < 0) { update->status = CHUNK_READ_INVALID; return; } // Stereo samples int sample_position = sampleForChunk(chunk_number); int samples_remaining = m_iTrackNumSamples - sample_position; int samples_to_read = math_min(kSamplesPerChunk, samples_remaining); // Bogus chunk number if (samples_to_read <= 0) { update->status = CHUNK_READ_EOF; return; } m_pCurrentSoundSource->seek(sample_position); int samples_read = m_pCurrentSoundSource->read(samples_to_read, m_pSample); // If we've run out of music, the SoundSource can return 0 samples. // Remember that SoundSourc->getLength() (which is m_iTrackNumSamples) can // lie to us about the length of the song! if (samples_read <= 0) { update->status = CHUNK_READ_EOF; return; } // TODO(XXX) This loop can't be done with a memcpy, but could be done with // SSE. CSAMPLE* buffer = request->chunk->data; //qDebug() << "Reading into " << buffer; SampleUtil::convert(buffer, m_pSample, samples_read); update->status = CHUNK_READ_SUCCESS; update->chunk->length = samples_read; }
int CachingReader::read(int sample, int num_samples, CSAMPLE* buffer) { // Check for bad inputs if (sample % 2 != 0 || num_samples < 0 || !buffer) { QString temp = QString("Sample = %1").arg(sample); QByteArray tempBA = QString(temp).toUtf8(); qDebug() << "CachingReader::read() invalid arguments sample:" << sample << "num_samples:" << num_samples << "buffer:" << buffer; return 0; } // If asked to read 0 samples, don't do anything. (this is a perfectly // reasonable request that happens sometimes. If no track is loaded, don't // do anything. if (num_samples == 0 || m_readerStatus != TRACK_LOADED) { return 0; } // Process messages from the reader thread. process(); // TODO: is it possible to move this code out of caching reader // and into enginebuffer? It doesn't quite make sense here, although // it makes preroll completely transparent to the rest of the code //if we're in preroll... int zerosWritten = 0; if (sample < 0) { if (sample + num_samples <= 0) { //everything is zeros, easy memset(buffer, 0, sizeof(*buffer) * num_samples); return num_samples; } else { //some of the buffer is zeros, some is from the file memset(buffer, 0, sizeof(*buffer) * (0 - sample)); buffer += (0 - sample); num_samples = sample + num_samples; zerosWritten = (0 - sample); sample = 0; //continue processing the rest of the chunks normally } } int start_sample = math_min(m_iTrackNumSamplesCallbackSafe, sample); int start_chunk = chunkForSample(start_sample); int end_sample = math_min(m_iTrackNumSamplesCallbackSafe, sample + num_samples - 1); int end_chunk = chunkForSample(end_sample); int samples_remaining = num_samples; int current_sample = sample; // Sanity checks if (start_chunk > end_chunk) { qDebug() << "CachingReader::read() bad chunk range to read [" << start_chunk << end_chunk << "]"; return 0; } for (int chunk_num = start_chunk; chunk_num <= end_chunk; chunk_num++) { Chunk* current = lookupChunk(chunk_num); // If the chunk is not in cache, then we must return an error. if (current == NULL) { // qDebug() << "Couldn't get chunk " << chunk_num // << " in read() of [" << sample << "," << sample+num_samples // << "] chunks " << start_chunk << "-" << end_chunk; // Something is wrong. Break out of the loop, that should fill the // samples requested with zeroes. break; } int chunk_start_sample = sampleForChunk(chunk_num); int chunk_offset = current_sample - chunk_start_sample; int chunk_remaining_samples = current->length - chunk_offset; // More sanity checks if (current_sample < chunk_start_sample || current_sample % 2 != 0) { qDebug() << "CachingReader::read() bad chunk parameters" << "chunk_start_sample" << chunk_start_sample << "current_sample" << current_sample; break; } // If we're past the start_chunk then current_sample should be // chunk_start_sample. if (start_chunk != chunk_num && chunk_start_sample != current_sample) { qDebug() << "CachingReader::read() bad chunk parameters" << "chunk_num" << chunk_num << "start_chunk" << start_chunk << "chunk_start_sample" << chunk_start_sample << "current_sample" << current_sample; break; } if (samples_remaining < 0) { qDebug() << "CachingReader::read() bad samples remaining" << samples_remaining; break; } // It is completely possible that chunk_remaining_samples is less than // zero. If the caller is trying to read from beyond the end of the // file, then this can happen. We should tolerate it. int samples_to_read = math_max(0, math_min(samples_remaining, chunk_remaining_samples)); // If we did not decide to read any samples from this chunk then that // means we have exhausted all the samples in the song. if (samples_to_read == 0) { break; } // samples_to_read should be non-negative and even if (samples_to_read < 0 || samples_to_read % 2 != 0) { qDebug() << "CachingReader::read() samples_to_read invalid" << samples_to_read; break; } // TODO(rryan) do a test and see if using memcpy is faster than gcc // optimizing the for loop CSAMPLE *data = current->data + chunk_offset; memcpy(buffer, data, sizeof(*buffer) * samples_to_read); // for (int i=0; i < samples_to_read; i++) { // buffer[i] = data[i]; // } buffer += samples_to_read; current_sample += samples_to_read; samples_remaining -= samples_to_read; } // If we didn't supply all the samples requested, that probably means we're // at the end of the file, or something is wrong. Provide zeroes and pretend // all is well. The caller can't be bothered to check how long the file is. // TODO(XXX) memset for (int i=0; i<samples_remaining; i++) { buffer[i] = 0.0f; } samples_remaining = 0; if (samples_remaining != 0) { qDebug() << "CachingReader::read() did read all requested samples."; } return zerosWritten + num_samples - samples_remaining; }