BlockArray *Sequence::Blockify(samplePtr buffer, sampleCount len) { BlockArray *list = new BlockArray(); list->Alloc(10); if (len == 0) return list; int num = (len + (mMaxSamples - 1)) / mMaxSamples; for (int i = 0; i < num; i++) { SeqBlock *b = new SeqBlock(); b->start = i * len / num; int newLen = ((i + 1) * len / num) - b->start; samplePtr bufStart = buffer + (b->start * SAMPLE_SIZE(mSampleFormat)); b->f = mDirManager->NewSimpleBlockFile(bufStart, newLen, mSampleFormat); list->Add(b); } return list; }
/// Reads the specified data from the aliased file, using libsndfile, /// and converts it to the given sample format. /// /// @param data The buffer to read the sample data into. /// @param format The format to convert the data into /// @param start The offset within the block to begin reading /// @param len The number of samples to read int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { SF_INFO info; if(!mAliasedFileName.IsOk()){ // intentionally silenced memset(data,0,SAMPLE_SIZE(format)*len); return len; } wxFile f; // will be closed when it goes out of scope SNDFILE *sf = NULL; { Maybe<wxLogNull> silence{}; if (mSilentAliasLog) silence.create(); memset(&info, 0, sizeof(info)); if (f.Exists(mAliasedFileName.GetFullPath())) { // Don't use Open if file does not exits if (f.Open(mAliasedFileName.GetFullPath())) { // Even though there is an sf_open() that takes a filename, use the one that // takes a file descriptor since wxWidgets can open a file with a Unicode name and // libsndfile can't (under Windows). ODManager::LockLibSndFileMutex(); sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE); ODManager::UnlockLibSndFileMutex(); } } if (!sf){ memset(data, 0, SAMPLE_SIZE(format)*len); silence.reset(); mSilentAliasLog = TRUE; // Set a marker to display an error message for the silence if (!wxGetApp().ShouldShowMissingAliasedFileWarning()) wxGetApp().MarkAliasedFilesMissingWarning(this); return len; } } mSilentAliasLog=FALSE; ODManager::LockLibSndFileMutex(); sf_seek(sf, mAliasStart + start, SEEK_SET); ODManager::UnlockLibSndFileMutex(); SampleBuffer buffer(len * info.channels, floatSample); int framesRead = 0; if (format == int16Sample && !sf_subtype_more_than_16_bits(info.format)) { // Special case: if the file is in 16-bit (or less) format, // and the calling method wants 16-bit data, go ahead and // read 16-bit data directly. This is a pretty common // case, as most audio files are 16-bit. ODManager::LockLibSndFileMutex(); framesRead = sf_readf_short(sf, (short *)buffer.ptr(), len); ODManager::UnlockLibSndFileMutex(); for (int i = 0; i < framesRead; i++) ((short *)data)[i] = ((short *)buffer.ptr())[(info.channels * i) + mAliasChannel]; } else { // Otherwise, let libsndfile handle the conversion and // scaling, and pass us normalized data as floats. We can // then convert to whatever format we want. ODManager::LockLibSndFileMutex(); framesRead = sf_readf_float(sf, (float *)buffer.ptr(), len); ODManager::UnlockLibSndFileMutex(); float *bufferPtr = &((float *)buffer.ptr())[mAliasChannel]; CopySamples((samplePtr)bufferPtr, floatSample, (samplePtr)data, format, framesRead, true, info.channels); } ODManager::LockLibSndFileMutex(); sf_close(sf); ODManager::UnlockLibSndFileMutex(); return framesRead; }
// This only decides if we must dither at all, the dithers // are all implemented using macros. void Dither::Apply(enum DitherType ditherType, const samplePtr source, sampleFormat sourceFormat, samplePtr dest, sampleFormat destFormat, unsigned int len, unsigned int stride /* = 1 */) { unsigned int i; // This code is not designed for 16-bit or 64-bit machine wxASSERT(sizeof(int) == 4); wxASSERT(sizeof(short) == 2); // Check parameters wxASSERT(source); wxASSERT(dest); wxASSERT(len >= 0); wxASSERT(stride > 0); if (len == 0) return; // nothing to do if (sourceFormat == destFormat) { // No need to dither, because source and destination // format are the same. Just copy samples. unsigned int srcBytes = SAMPLE_SIZE(destFormat); if (stride == 1) memcpy(dest, source, len * srcBytes); else { samplePtr s = source; samplePtr d = dest; for (i = 0; i < len; i++) { memcpy(d, s, srcBytes); s += stride * srcBytes; d += srcBytes; } } } else if (destFormat == floatSample) { // No need to dither, just convert samples to float. // No clipping should be necessary. float* d = (float*)dest; if (sourceFormat == int16Sample) { short* s = (short*)source; for (i = 0; i < len; i++, d++, s+= stride) *d = FROM_INT16(s); } else if (sourceFormat == int24Sample) { int* s = (int*)source; for (i = 0; i < len; i++, d++, s+= stride) *d = FROM_INT24(s); } else { wxASSERT(false); // source format unknown } } else if (sourceFormat == int16Sample && destFormat == int24Sample) { // Special case when promoting 16 bit to 24 bit short* s = (short*)source; int* d = (int*)dest; for (i = 0; i < len; i++) { *d = ((int)*s) << 8; s += stride; d++; } } else { // We must do dithering switch (ditherType) { case none: DITHER(NoDither, dest, destFormat, source, sourceFormat, len, stride); break; case rectangle: DITHER(RectangleDither, dest, destFormat, source, sourceFormat, len, stride); break; case triangle: DITHER(TriangleDither, dest, destFormat, source, sourceFormat, len, stride); break; case shaped: Reset(); // reset shaped dither filter for this new conversion DITHER(ShapedDither, dest, destFormat, source, sourceFormat, len, stride); break; default: wxASSERT(false); // unknown dither algorithm } } }
/// Read the data portion of the block file using libsndfile. Convert it /// to the given format if it is not already. /// /// @param data The buffer where the data will be stored /// @param format The format the data will be stored in /// @param start The offset in this block file /// @param len The number of samples to read int LegacyBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { SF_INFO info; memset(&info, 0, sizeof(info)); switch(mFormat) { case int16Sample: info.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_CPU; break; default: case floatSample: info.format = SF_FORMAT_RAW | SF_FORMAT_FLOAT | SF_ENDIAN_CPU; break; case int24Sample: info.format = SF_FORMAT_RAW | SF_FORMAT_PCM_32 | SF_ENDIAN_CPU; break; } info.samplerate = 44100; // Doesn't matter info.channels = 1; info.frames = mLen + (mSummaryInfo.totalSummaryBytes / SAMPLE_SIZE(mFormat)); wxFile f; // will be closed when it goes out of scope SNDFILE *sf = NULL; if (f.Open(mFileName.GetFullPath())) { // Even though there is an sf_open() that takes a filename, use the one that // takes a file descriptor since wxWidgets can open a file with a Unicode name and // libsndfile can't (under Windows). sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE); } { Maybe<wxLogNull> silence{}; if (mSilentLog) silence.create(); if (!sf){ memset(data, 0, SAMPLE_SIZE(format)*len); mSilentLog = TRUE; return len; } } mSilentLog=FALSE; sf_count_t seekstart = start + (mSummaryInfo.totalSummaryBytes / SAMPLE_SIZE(mFormat)); sf_seek(sf, seekstart , SEEK_SET); SampleBuffer buffer(len, floatSample); int framesRead = 0; // If both the src and dest formats are integer formats, // read integers from the file (otherwise we would be // converting to float and back, which is unneccesary) if (format == int16Sample && sf_subtype_is_integer(info.format)) { framesRead = sf_readf_short(sf, (short *)data, len); }else if (format == int24Sample && sf_subtype_is_integer(info.format)) { framesRead = sf_readf_int(sf, (int *)data, len); // libsndfile gave us the 3 byte sample in the 3 most // significant bytes -- we want it in the 3 least // significant bytes. int *intPtr = (int *)data; for( int i = 0; i < framesRead; i++ ) intPtr[i] = intPtr[i] >> 8; } else {
bool Sequence::Delete(sampleCount start, sampleCount len) { if (len == 0) return true; if (len < 0 || start < 0 || start >= mNumSamples) return false; //TODO: add a ref-deref mechanism to SeqBlock/BlockArray so we don't have to make this a critical section. //On-demand threads iterate over the mBlocks and the GUI thread deletes them, so for now put a mutex here over //both functions, LockDeleteUpdateMutex(); unsigned int numBlocks = mBlock->Count(); unsigned int newNumBlocks = 0; unsigned int b0 = FindBlock(start); unsigned int b1 = FindBlock(start + len - 1); int sampleSize = SAMPLE_SIZE(mSampleFormat); // Special case: if the samples to delete are all within a single // block and the resulting length is not too small, perform the // deletion within this block: if (b0 == b1 && mBlock->Item(b0)->f->GetLength() - len >= mMinSamples) { SeqBlock *b = mBlock->Item(b0); sampleCount pos = start - b->start; sampleCount newLen = b->f->GetLength() - len; samplePtr buffer = NewSamples(newLen, mSampleFormat); Read(buffer, mSampleFormat, b, 0, pos); Read(buffer + (pos * sampleSize), mSampleFormat, b, pos + len, newLen - pos); SeqBlock *newBlock = new SeqBlock(); newBlock->start = b->start; newBlock->f = mDirManager->NewSimpleBlockFile(buffer, newLen, mSampleFormat); mBlock->Item(b0) = newBlock; for (unsigned int j = b0 + 1; j < numBlocks; j++) mBlock->Item(j)->start -= len; DeleteSamples(buffer); mDirManager->Deref(b->f); delete b; mNumSamples -= len; UnlockDeleteUpdateMutex(); return ConsistencyCheck(wxT("Delete - branch one")); } // Create a new array of blocks BlockArray *newBlock = new BlockArray(); newBlock->Alloc(numBlocks - (b1 - b0) + 2); // Copy the blocks before the deletion point over to // the new array unsigned int i; for (i = 0; i < b0; i++) { newBlock->Add(mBlock->Item(i)); newNumBlocks++; } // First grab the samples in block b0 before the deletion point // into preBuffer. If this is enough samples for its own block, // or if this would be the first block in the array, write it out. // Otherwise combine it with the previous block (splitting them // 50/50 if necessary). SeqBlock *preBlock = mBlock->Item(b0); sampleCount preBufferLen = start - preBlock->start; if (preBufferLen) { if (preBufferLen >= mMinSamples || b0 == 0) { SeqBlock *insBlock = new SeqBlock(); insBlock->start = preBlock->start; samplePtr preBuffer = NewSamples(preBufferLen, mSampleFormat); Read(preBuffer, mSampleFormat, preBlock, 0, preBufferLen); insBlock->f = mDirManager->NewSimpleBlockFile(preBuffer, preBufferLen, mSampleFormat); DeleteSamples(preBuffer); newBlock->Add(insBlock); newNumBlocks++; if (b0 != b1) { mDirManager->Deref(preBlock->f); delete preBlock; } } else { SeqBlock *prepreBlock = mBlock->Item(b0 - 1); sampleCount prepreLen = prepreBlock->f->GetLength(); sampleCount sum = prepreLen + preBufferLen; samplePtr sumBuffer = NewSamples(sum, mSampleFormat); Read(sumBuffer, mSampleFormat, prepreBlock, 0, prepreLen); Read(sumBuffer + prepreLen*sampleSize, mSampleFormat, preBlock, 0, preBufferLen); BlockArray *split = Blockify(sumBuffer, sum); split->Item(0)->start += prepreBlock->start; newBlock->Item(b0 - 1) = split->Item(0); for (i = 1; i < split->Count(); i++) { split->Item(i)->start += prepreBlock->start; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; DeleteSamples(sumBuffer); mDirManager->Deref(prepreBlock->f); delete prepreBlock; if (b0 != b1) { mDirManager->Deref(preBlock->f); delete preBlock; } } } else { // The sample where we begin deletion happens to fall // right on the beginning of a block. if (b0 != b1) { mDirManager->Deref(mBlock->Item(b0)->f); delete mBlock->Item(b0); } } // Next, delete blocks strictly between b0 and b1 for (i = b0 + 1; i < b1; i++) { mDirManager->Deref(mBlock->Item(i)->f); delete mBlock->Item(i); } // Now, symmetrically, grab the samples in block b1 after the // deletion point into postBuffer. If this is enough samples // for its own block, or if this would be the last block in // the array, write it out. Otherwise combine it with the // subsequent block (splitting them 50/50 if necessary). SeqBlock *postBlock = mBlock->Item(b1); sampleCount postBufferLen = (postBlock->start + postBlock->f->GetLength()) - (start + len); if (postBufferLen) { if (postBufferLen >= mMinSamples || b1 == numBlocks - 1) { SeqBlock *insBlock = new SeqBlock(); insBlock->start = start; samplePtr postBuffer = NewSamples(postBufferLen, mSampleFormat); sampleCount pos = (start + len) - postBlock->start; Read(postBuffer, mSampleFormat, postBlock, pos, postBufferLen); insBlock->f = mDirManager->NewSimpleBlockFile(postBuffer, postBufferLen, mSampleFormat); DeleteSamples(postBuffer); newBlock->Add(insBlock); newNumBlocks++; mDirManager->Deref(postBlock->f); delete postBlock; } else { SeqBlock *postpostBlock = mBlock->Item(b1 + 1); sampleCount postpostLen = postpostBlock->f->GetLength(); sampleCount sum = postpostLen + postBufferLen; samplePtr sumBuffer = NewSamples(sum, mSampleFormat); sampleCount pos = (start + len) - postBlock->start; Read(sumBuffer, mSampleFormat, postBlock, pos, postBufferLen); Read(sumBuffer + (postBufferLen * sampleSize), mSampleFormat, postpostBlock, 0, postpostLen); BlockArray *split = Blockify(sumBuffer, sum); for (i = 0; i < split->Count(); i++) { split->Item(i)->start += start; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; b1++; DeleteSamples(sumBuffer); mDirManager->Deref(postpostBlock->f); delete postpostBlock; mDirManager->Deref(postBlock->f); delete postBlock; } } else { // The sample where we begin deletion happens to fall // right on the end of a block. mDirManager->Deref(mBlock->Item(b1)->f); delete mBlock->Item(b1); } // Copy the remaining blocks over from the old array for (i = b1 + 1; i < numBlocks; i++) { mBlock->Item(i)->start -= len; newBlock->Add(mBlock->Item(i)); newNumBlocks++; } // Substitute our new array for the old one delete mBlock; mBlock = newBlock; // Update total number of samples and do a consistency check. mNumSamples -= len; UnlockDeleteUpdateMutex(); return ConsistencyCheck(wxT("Delete - branch two")); }
// Pass NULL to set silence bool Sequence::Set(samplePtr buffer, sampleFormat format, sampleCount start, sampleCount len) { if (start < 0 || start > mNumSamples || start+len > mNumSamples) return false; samplePtr temp = NULL; if (format != mSampleFormat) { temp = NewSamples(mMaxSamples, mSampleFormat); wxASSERT(temp); } samplePtr silence = NULL; if (!buffer) { silence = NewSamples(mMaxSamples, format); wxASSERT(silence); ClearSamples(silence, format, 0, mMaxSamples); } int b = FindBlock(start); while (len) { int blen = mBlock->Item(b)->start + mBlock->Item(b)->f->GetLength() - start; if (blen > len) blen = len; if (buffer) { if (format == mSampleFormat) CopyWrite(buffer, mBlock->Item(b), start - mBlock->Item(b)->start, blen); else { CopySamples(buffer, format, temp, mSampleFormat, blen); CopyWrite(temp, mBlock->Item(b), start - mBlock->Item(b)->start, blen); } buffer += (blen * SAMPLE_SIZE(format)); } else { // If it's a full block of silence if (start == mBlock->Item(b)->start && blen == mBlock->Item(b)->f->GetLength()) { mDirManager->Deref(mBlock->Item(b)->f); mBlock->Item(b)->f = new SilentBlockFile(blen); } else { // Otherwise write silence just to the portion of the block CopyWrite(silence, mBlock->Item(b), start - mBlock->Item(b)->start, blen); } } len -= blen; start += blen; b++; } if (!buffer) DeleteSamples(silence); if (format != mSampleFormat) DeleteSamples(temp); return ConsistencyCheck(wxT("Set")); }
int ODFFmpegDecoder::Decode(SampleBuffer & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel) { format = mScs[mStreamIndex]->m_osamplefmt; data.Allocate(len, format); samplePtr bufStart = data.ptr(); streamContext* sc = NULL; // printf("start %llu len %llu\n", start, len); //TODO update this to work with seek - this only works linearly now. if(mCurrentPos > start && mCurrentPos <= start+len + kDecodeSampleAllowance) { //this next call takes data, start and len as reference variables and updates them to reflect the NEW area that is needed. FillDataFromCache(bufStart, format, start,len,channel); } bool seeking = false; //look at the decoding timestamp and see if the next sample that will be decoded is not the next sample we need. if(len && (mCurrentPos > start + len || mCurrentPos + kDecodeSampleAllowance < start ) && SeekingAllowed()) { sc = mScs[mStreamIndex]; AVStream* st = sc->m_stream; int stindex = -1; uint64_t targetts; //printf("attempting seek to %llu\n", start); //we have to find the index for this stream. for (unsigned int i = 0; i < mFormatContext->nb_streams; i++) { if (mFormatContext->streams[i] == sc->m_stream ) stindex =i; } if(stindex >=0) { int numAttempts = 0; //reset mCurrentPos to a bogus value mCurrentPos = start+len +1; while(numAttempts++ < kMaxSeekRewindAttempts && mCurrentPos > start) { //we want to move slightly before the start of the block file, but not too far ahead targetts = (start-kDecodeSampleAllowance*numAttempts/kMaxSeekRewindAttempts) * ((double)st->time_base.den/(st->time_base.num * st->codec->sample_rate )); if(targetts<0) targetts=0; //printf("attempting seek to %llu, attempts %d\n", targetts, numAttempts); if(av_seek_frame(mFormatContext,stindex,targetts,0) >= 0){ //find out the dts we've seekd to. sampleCount actualDecodeStart = 0.5 + st->codec->sample_rate * st->cur_dts * ((double)st->time_base.num/st->time_base.den); //this is mostly safe because den is usually 1 or low number but check for high values. mCurrentPos = actualDecodeStart; seeking = true; //if the seek was past our desired position, rewind a bit. //printf("seek ok to %llu samps, float: %f\n",actualDecodeStart,actualDecodeStartDouble); } else { printf("seek failed"); break; } } if(mCurrentPos>start){ mSeekingAllowedStatus = (bool)ODFFMPEG_SEEKING_TEST_FAILED; // url_fseek(mFormatContext->pb,sc->m_pkt.pos,SEEK_SET); printf("seek fail, reverting to previous pos\n"); return -1; } } } bool firstpass = true; //we decode up to the end of the blockfile while (len>0 && (mCurrentPos < start+len) && (sc = ReadNextFrame()) != NULL) { // ReadNextFrame returns 1 if stream is not to be imported if (sc != (streamContext*)1) { //find out the dts we've seekd to. can't use the stream->cur_dts because it is faulty. also note that until we do the first seek, pkt.dts can be false and will change for the same samples after the initial seek. sampleCount actualDecodeStart = mCurrentPos; // we need adjacent samples, so don't use dts most of the time which will leave gaps between frames // for some formats // The only other case for inserting silence is for initial offset and ImportFFmpeg.cpp does this for us if (seeking) { actualDecodeStart = 0.52 + (sc->m_stream->codec->sample_rate * sc->m_pkt.dts * ((double)sc->m_stream->time_base.num / sc->m_stream->time_base.den)); //this is mostly safe because den is usually 1 or low number but check for high values. //hack to get rounding to work to neareset frame size since dts isn't exact if (sc->m_stream->codec->frame_size) { actualDecodeStart = ((actualDecodeStart + sc->m_stream->codec->frame_size/2) / sc->m_stream->codec->frame_size) * sc->m_stream->codec->frame_size; } // reset for the next one seeking = false; } if(actualDecodeStart != mCurrentPos) printf("ts not matching - now:%llu , last:%llu, lastlen:%llu, start %llu, len %llu\n",actualDecodeStart, mCurrentPos, mCurrentLen, start, len); //if we've skipped over some samples, fill the gap with silence. This could happen often in the beginning of the file. if(actualDecodeStart>start && firstpass) { // find the number of samples for the leading silence int amt = actualDecodeStart - start; FFMpegDecodeCache* cache = new FFMpegDecodeCache; //printf("skipping/zeroing %i samples. - now:%llu (%f), last:%llu, lastlen:%llu, start %llu, len %llu\n",amt,actualDecodeStart, actualDecodeStartdouble, mCurrentPos, mCurrentLen, start, len); //put it in the cache so the other channels can use it. cache->numChannels = sc->m_stream->codec->channels; cache->len = amt; cache->start=start; // 8 bit and 16 bit audio output from ffmpeg means // 16 bit int out. // 32 bit int, float, double mean float out. if (format == int16Sample) cache->samplefmt = SAMPLE_FMT_S16; else cache->samplefmt = SAMPLE_FMT_FLT; cache->samplePtr = (uint8_t*) malloc(amt * cache->numChannels * SAMPLE_SIZE(format)); memset(cache->samplePtr, 0, amt * cache->numChannels * SAMPLE_SIZE(format)); InsertCache(cache); } firstpass=false; mCurrentPos = actualDecodeStart; //decode the entire packet (unused bits get saved in cache, so as long as cache size limit is bigger than the //largest packet size, we're ok. while (sc->m_pktRemainingSiz > 0) //Fill the cache with decoded samples if (DecodeFrame(sc,false) < 0) break; // Cleanup after frame decoding if (sc->m_pktValid) { av_free_packet(&sc->m_pkt); sc->m_pktValid = 0; } } } // Flush the decoders if we're done. if((!sc || sc == (streamContext*) 1)&& len>0) { for (int i = 0; i < mNumStreams; i++) { if (DecodeFrame(mScs[i], true) == 0) { if (mScs[i]->m_pktValid) { av_free_packet(&mScs[i]->m_pkt); mScs[i]->m_pktValid = 0; } } } } //this next call takes data, start and len as reference variables and updates them to reflect the NEW area that is needed. FillDataFromCache(bufStart, format, start, len, channel); // CHECK: not sure if we need this. In any case it has to be updated for the NEW float case (not just int16) //if for some reason we couldn't get the samples, fill them with silence /* int16_t* outBuf = (int16_t*) bufStart; for(int i=0;i<len;i++) outBuf[i]=0; */ return 1; }
void Mixer::MixDiffRates(int *channelFlags, WaveTrack * src, double t0, double t1) { if ((t0 - src->GetOffset()) >= src->GetNumSamples() / src->GetRate() || (t1 - src->GetOffset()) <= 0) return; int s0 = int ((t0 - src->GetOffset()) * src->GetRate()); // get a couple more samples than we need int slen = int ((t1 - t0) * src->GetRate()) + 2; int destlen = int ((t1 - t0) * mRate + 0.5); GetSamples(src, s0, slen); double volume; if (mUseVolumeSlider) volume = mControlToolBar->GetSoundVol(); else volume = 1.0; Envelope *e = src->GetEnvelope(); e->GetValues(mEnvValues, mBufferSize, t0, 1.0 / mRate); // Mix it down to the appropriate tracks for (int c = 0; c < mNumChannels; c++) { if (!channelFlags[c]) continue; samplePtr destPtr; int skip; if (mInterleaved) { destPtr = mBuffer[0] + c*SAMPLE_SIZE(mFormat); skip = mNumChannels; } else { destPtr = mBuffer[c]; skip = 1; } // This is the mixing inner loop, which we want // as optimized as possible int i = 0; switch(mFormat) { case int16Sample: { short *temp = (short *)mTemp; short *dest = (short *)destPtr; int frac = int(32768.0 * (t0 - s0/src->GetRate())); int fracstep = int(32768.0 * src->GetRate()/mRate + 0.5); for (int j = 0; j < destlen; j++) { short value = (temp[i]*(32768-frac) + temp[i+1]*frac) >> 15; frac += fracstep; i += (frac >> 15); // frac/32768 frac = (frac & 0x7FFF); // frac%32768 *dest += short(value * volume * mEnvValues[j] + 0.5); dest += skip; } } break; case int24Sample: { int *temp = (int *)mTemp; int *dest = (int *)destPtr; float frac = t0 - s0/src->GetRate(); float fracstep = src->GetRate()/mRate; for (int j = 0; j < destlen; j++) { float value = (temp[i]*(1.0-frac) + temp[i+1]*frac); frac += fracstep; int integerPart = (int)frac; i += integerPart; frac -= (float)integerPart; *dest += int(value * volume * mEnvValues[j] + 0.5); dest += skip; } } break; case floatSample: { float *temp = (float *)mTemp; float *dest = (float *)destPtr; float frac = t0 - s0/src->GetRate(); float fracstep = src->GetRate()/mRate; for (int j = 0; j < destlen; j++) { float value = temp[i]*(1.0-frac) + temp[i+1]*frac; frac += fracstep; int integerPart = (int)frac; i += integerPart; frac -= (float)integerPart; *dest += value * volume * mEnvValues[j]; dest += skip; } } break; } // switch } }
bool SimpleBlockFile::WriteSimpleBlockFile( samplePtr sampleData, sampleCount sampleLen, sampleFormat format, void* summaryData) { wxFFile file(mFileName.GetFullPath(), wxT("wb")); if( !file.IsOpened() ){ // Can't do anything else. return false; } auHeader header; // AU files can be either big or little endian. Which it is is // determined implicitly by the byte-order of the magic 0x2e736e64 // (.snd). We want it to be native-endian, so we write the magic // to memory and then let it write that to a file in native // endianness header.magic = 0x2e736e64; // We store the summary data at the end of the header, so the data // offset is the length of the summary data plus the length of the header header.dataOffset = sizeof(auHeader) + mSummaryInfo.totalSummaryBytes; // dataSize is optional, and we opt out header.dataSize = 0xffffffff; switch(format) { case int16Sample: header.encoding = AU_SAMPLE_FORMAT_16; break; case int24Sample: header.encoding = AU_SAMPLE_FORMAT_24; break; case floatSample: header.encoding = AU_SAMPLE_FORMAT_FLOAT; break; } // TODO: don't fabricate header.sampleRate = 44100; // BlockFiles are always mono header.channels = 1; // Write the file ArrayOf<char> cleanup; if (!summaryData) summaryData = /*BlockFile::*/CalcSummary(sampleData, sampleLen, format, cleanup); //mchinen:allowing virtual override of calc summary for ODDecodeBlockFile. // PRL: cleanup fixes a possible memory leak! size_t nBytesToWrite = sizeof(header); size_t nBytesWritten = file.Write(&header, nBytesToWrite); if (nBytesWritten != nBytesToWrite) { wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite); return false; } nBytesToWrite = mSummaryInfo.totalSummaryBytes; nBytesWritten = file.Write(summaryData, nBytesToWrite); if (nBytesWritten != nBytesToWrite) { wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite); return false; } if( format == int24Sample ) { // we can't write the buffer directly to disk, because 24-bit samples // on disk need to be packed, not padded to 32 bits like they are in // memory int *int24sampleData = (int*)sampleData; for( int i = 0; i < sampleLen; i++ ) { nBytesToWrite = 3; nBytesWritten = #if wxBYTE_ORDER == wxBIG_ENDIAN file.Write((char*)&int24sampleData[i] + 1, nBytesToWrite); #else file.Write((char*)&int24sampleData[i], nBytesToWrite); #endif if (nBytesWritten != nBytesToWrite) { wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite); return false; } } } else { // for all other sample formats we can write straight from the buffer // to disk nBytesToWrite = sampleLen * SAMPLE_SIZE(format); nBytesWritten = file.Write(sampleData, nBytesToWrite); if (nBytesWritten != nBytesToWrite) { wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite); return false; } } return true; }
sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track, longSampleCount *pos, float *queue, int *queueStart, int *queueLen, Resample *SRC) { double initialWarp = mRate / track->GetRate(); double t = *pos / track->GetRate(); int sampleSize = SAMPLE_SIZE(floatSample); int i, c; sampleCount out = 0; while(out < mMaxOut) { if (*queueLen < mProcessLen) { memmove(queue, &queue[*queueStart], (*queueLen)*sampleSize); *queueStart = 0; int getLen = mQueueMaxLen - *queueLen; #if 0 // TODO: fix this code so that extra silence isn't added // to the end of a track double trackTime = (*pos + getLen) / track->GetRate(); if (trackTime > track->GetEndTime()) { getLen = (int)(0.5 + track->GetRate() * (track->GetEndTime() - ((*pos) / track->GetRate()))); } #endif track->Get((samplePtr)&queue[*queueLen], floatSample, *pos, getLen); track->GetEnvelopeValues(mEnvValues, getLen, (*pos) / track->GetRate(), 1.0 / track->GetRate()); for(i=0; i<getLen; i++) queue[(*queueLen)+i] *= mEnvValues[i]; *queueLen += getLen; *pos += getLen; } sampleCount thisProcessLen = mProcessLen; if (*queueLen < mProcessLen) thisProcessLen = *queueLen; double factor = initialWarp; if (mTimeTrack) { double warpFactor = mTimeTrack->GetEnvelope()->GetValue(t); warpFactor = (mTimeTrack->GetRangeLower() * (1 - warpFactor) + warpFactor * mTimeTrack->GetRangeUpper())/100.0; factor /= warpFactor; } int input_used; bool last = (*queueLen < mProcessLen); int outgen = SRC->Process(factor, &queue[*queueStart], thisProcessLen, last, &input_used, &mFloatBuffer[out], mMaxOut - out); if (outgen < 0) return 0; *queueStart += input_used; *queueLen -= input_used; out += outgen; t += (input_used / track->GetRate()); if (last) break; } for(c=0; c<mNumChannels; c++) if (mApplyTrackGains) mGains[c] = track->GetChannelGain(c); else mGains[c] = 1.0; MixBuffers(mNumChannels, channelFlags, mGains, (samplePtr)mFloatBuffer, mTemp, mMaxOut, mInterleaved); return mMaxOut; }
void Mixer::Clear() { for (int c = 0; c < mNumBuffers; c++) { memset(mTemp[c], 0, mInterleavedBufferSize * SAMPLE_SIZE(floatSample)); } }
int BlockFile::ReadData(void *data, sampleFormat format, sampleCount start, sampleCount len) { int i; if (mType == BLOCK_TYPE_ALIAS) { SF_INFO info; SNDFILE *sf = sf_open(mAliasFullPath, SFM_READ, &info); if (!sf) return 0; sf_seek(sf, mStart + start, SEEK_SET); samplePtr buffer = NewSamples(len * info.channels, floatSample); int framesRead = 0; switch(format) { case int16Sample: framesRead = sf_readf_short(sf, (short *)buffer, len); for (i = 0; i < framesRead; i++) ((short *)data)[i] = ((short *)buffer)[(info.channels * i) + mChannel]; break; case floatSample: framesRead = sf_readf_float(sf, (float *)buffer, len); for (i = 0; i < framesRead; i++) ((float *)data)[i] = ((float *)buffer)[(info.channels * i) + mChannel]; break; default: framesRead = sf_readf_float(sf, (float *)buffer, len); for (i = 0; i < framesRead; i++) ((float *)buffer)[i] = ((float *)buffer)[(info.channels * i) + mChannel]; CopySamples((samplePtr)buffer, floatSample, (samplePtr)data, format, framesRead); } DeleteSamples(buffer); sf_close(sf); return framesRead; } else { wxFFile file; int read; if (!file.Open((const wxChar *) mFullPath, "rb")) return 0; file.Seek(mSummaryLen + start * SAMPLE_SIZE(mSampleFormat), wxFromStart); if (format == mSampleFormat) { int bytes = len * SAMPLE_SIZE(mSampleFormat); read = (int)file.Read(data, (size_t)bytes); read /= SAMPLE_SIZE(mSampleFormat); } else { samplePtr buffer = NewSamples(len, mSampleFormat); int srcBytes = len * SAMPLE_SIZE(mSampleFormat); read = (int)file.Read(buffer, (size_t)srcBytes); read /= SAMPLE_SIZE(mSampleFormat); CopySamples(buffer, mSampleFormat, (samplePtr)data, format, read); DeleteSamples(buffer); } file.Close(); return read; } }
sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track, longSampleCount *pos, float *queue, int *queueStart, int *queueLen, Resample *SRC) { double trackRate = track->GetRate(); double initialWarp = mRate / trackRate; double t = *pos / trackRate; int sampleSize = SAMPLE_SIZE(floatSample); int i, c; sampleCount out = 0; // Find the last sample longSampleCount last = -1; WaveClipList::Node* it = track->GetClipIterator(); while (it) { longSampleCount end = it->GetData()->GetEndSample(); if (end > last) { last = end; } it = it->GetNext(); } longSampleCount max = trackRate * mT1; if (last > max) last = max; while(out < mMaxOut) { if (*queueLen < mProcessLen) { memmove(queue, &queue[*queueStart], (*queueLen)*sampleSize); *queueStart = 0; int getLen = mQueueMaxLen - *queueLen; // Constrain if (*pos + getLen > last) { getLen = last - *pos; } // Nothing to do if past end of track if (getLen <= 0) { break; } track->Get((samplePtr)&queue[*queueLen], floatSample, *pos, getLen); track->GetEnvelopeValues(mEnvValues, getLen, (*pos) / trackRate, 1.0 / trackRate); for(i=0; i<getLen; i++) queue[(*queueLen)+i] *= mEnvValues[i]; *queueLen += getLen; *pos += getLen; } sampleCount thisProcessLen = mProcessLen; if (*queueLen < mProcessLen) thisProcessLen = *queueLen; double factor = initialWarp; if (mTimeTrack) { double warpFactor = mTimeTrack->GetEnvelope()->GetValue(t); warpFactor = (mTimeTrack->GetRangeLower() * (1 - warpFactor) + warpFactor * mTimeTrack->GetRangeUpper())/100.0; factor /= warpFactor; } int input_used; bool last = (*queueLen < mProcessLen); int outgen = SRC->Process(factor, &queue[*queueStart], thisProcessLen, last, &input_used, &mFloatBuffer[out], mMaxOut - out); if (outgen < 0) return 0; *queueStart += input_used; *queueLen -= input_used; out += outgen; t += (input_used / trackRate); if (last) break; } for(c=0; c<mNumChannels; c++) if (mApplyTrackGains) mGains[c] = track->GetChannelGain(c); else mGains[c] = 1.0; MixBuffers(mNumChannels, channelFlags, mGains, (samplePtr)mFloatBuffer, mTemp, out, mInterleaved); return out; }
// Given a project and a list of aliased files that should no // longer be external dependencies (selected by the user), replace // all of those alias block files with disk block files. void RemoveDependencies(AudacityProject *project, AliasedFileArray *aliasedFiles) { DirManager *dirManager = project->GetDirManager(); ProgressDialog *progress = new ProgressDialog(_("Removing Dependencies"), _("Copying audio data into project...")); int updateResult = eProgressSuccess; // Hash aliasedFiles based on their full paths and // count total number of bytes to process. AliasedFileHash aliasedFileHash; wxLongLong totalBytesToProcess = 0; unsigned int i; for (i = 0; i < aliasedFiles->GetCount(); i++) { totalBytesToProcess += aliasedFiles->Item(i).mByteCount; wxString fileNameStr = aliasedFiles->Item(i).mFileName.GetFullPath(); aliasedFileHash[fileNameStr] = &aliasedFiles->Item(i); } BlockArray blocks; GetAllSeqBlocks(project, &blocks); const sampleFormat format = project->GetDefaultFormat(); ReplacedBlockFileHash blockFileHash; wxLongLong completedBytes = 0; for (i = 0; i < blocks.GetCount(); i++) { BlockFile *f = blocks[i]->f; if (f->IsAlias() && (blockFileHash.count(f) == 0)) { // f is an alias block we have not yet processed. AliasBlockFile *aliasBlockFile = (AliasBlockFile *)f; wxFileName fileName = aliasBlockFile->GetAliasedFileName(); wxString fileNameStr = fileName.GetFullPath(); if (aliasedFileHash.count(fileNameStr) == 0) // This aliased file was not selected to be replaced. Skip it. continue; // Convert it from an aliased file to an actual file in the project. unsigned int len = aliasBlockFile->GetLength(); samplePtr buffer = NewSamples(len, format); f->ReadData(buffer, format, 0, len); BlockFile *newBlockFile = dirManager->NewSimpleBlockFile(buffer, len, format); DeleteSamples(buffer); // Update our hash so we know what block files we've done blockFileHash[f] = newBlockFile; // Update the progress bar completedBytes += SAMPLE_SIZE(format) * len; updateResult = progress->Update(completedBytes, totalBytesToProcess); if (updateResult != eProgressSuccess) break; } } // Above, we created a SimpleBlockFile contained in our project // to go with each AliasBlockFile that we wanted to migrate. // However, that didn't actually change any references to these // blockfiles in the Sequences, so we do that next... ReplaceBlockFiles(project, blockFileHash); // Subtract one from reference count of new block files; they're // now all referenced the proper number of times by the Sequences ReplacedBlockFileHash::iterator it; for( it = blockFileHash.begin(); it != blockFileHash.end(); ++it ) { BlockFile *f = it->second; dirManager->Deref(f); } delete progress; }
void ComputeLegacySummaryInfo(wxFileName fileName, int summaryLen, sampleFormat format, SummaryInfo *info, bool noRMS, float *min, float *max, float *rms) { int fields = 3; /* min, max, rms */ if (noRMS) fields = 2; info->fields = fields; info->format = format; info->bytesPerFrame = SAMPLE_SIZE(info->format) * fields; info->totalSummaryBytes = summaryLen; info->offset64K = 20; /* legacy header tag len */ info->frames64K = (summaryLen-20) / (info->bytesPerFrame * 256); info->offset256 = info->offset64K + (info->frames64K * info->bytesPerFrame); info->frames256 = (summaryLen - 20 - (info->frames64K * info->bytesPerFrame)) / info->bytesPerFrame; // // Compute the min, max, and RMS of the block from the // 64K summary data // float *summary = new float[info->frames64K * fields]; samplePtr data = NewSamples(info->frames64K * fields, info->format); wxFFile summaryFile; if( !summaryFile.Open(fileName.GetFullPath(), "rb") ) { delete[] data; return; } summaryFile.Seek(info->offset64K); int read = summaryFile.Read(data, info->frames64K * info->bytesPerFrame); int count = read / info->bytesPerFrame; CopySamples(data, info->format, (samplePtr)summary, floatSample, count); (*min) = FLT_MAX; (*max) = FLT_MIN; float sumsq = 0; for(int i=0; i<count; i++) { if (summary[fields*i] < (*min)) (*min) = summary[fields*i]; if (summary[fields*i+1] > (*max)) (*max) = summary[fields*i+1]; if (fields >= 3) sumsq += summary[fields*i+2]*summary[fields*i+2]; } if (fields >= 3) (*rms) = sqrt(sumsq / count); else (*rms) = 0; DeleteSamples(data); delete[] summary; }
static bool_t wv_play (InputPlayback * playback, const char * filename, VFSFile * file, int start_time, int stop_time, bool_t pause) { if (file == NULL) return FALSE; int32_t *input = NULL; void *output = NULL; int sample_rate, num_channels, bits_per_sample; unsigned num_samples; WavpackContext *ctx = NULL; VFSFile *wvc_input = NULL; bool_t error = FALSE; if (! wv_attach (filename, file, & wvc_input, & ctx, NULL, OPEN_TAGS | OPEN_WVC)) { fprintf (stderr, "Error opening Wavpack file '%s'.", filename); error = TRUE; goto error_exit; } sample_rate = WavpackGetSampleRate(ctx); num_channels = WavpackGetNumChannels(ctx); bits_per_sample = WavpackGetBitsPerSample(ctx); num_samples = WavpackGetNumSamples(ctx); if (!playback->output->open_audio(SAMPLE_FMT(bits_per_sample), sample_rate, num_channels)) { fprintf (stderr, "Error opening audio output."); error = TRUE; goto error_exit; } if (pause) playback->output->pause(TRUE); input = malloc(BUFFER_SIZE * num_channels * sizeof(uint32_t)); output = malloc(BUFFER_SIZE * num_channels * SAMPLE_SIZE(bits_per_sample)); if (input == NULL || output == NULL) goto error_exit; playback->set_gain_from_playlist(playback); pthread_mutex_lock (& mutex); playback->set_params(playback, (int) WavpackGetAverageBitrate(ctx, num_channels), sample_rate, num_channels); seek_value = (start_time > 0) ? start_time : -1; stop_flag = FALSE; playback->set_pb_ready(playback); pthread_mutex_unlock (& mutex); while (!stop_flag && (stop_time < 0 || playback->output->written_time () < stop_time)) { int ret; unsigned samples_left; /* Handle seek and pause requests */ pthread_mutex_lock (& mutex); if (seek_value >= 0) { playback->output->flush (seek_value); WavpackSeekSample (ctx, (int64_t) seek_value * sample_rate / 1000); seek_value = -1; } pthread_mutex_unlock (& mutex); /* Decode audio data */ samples_left = num_samples - WavpackGetSampleIndex(ctx); ret = WavpackUnpackSamples(ctx, input, BUFFER_SIZE); if (samples_left == 0) stop_flag = TRUE; else if (ret < 0) { fprintf (stderr, "Error decoding file.\n"); break; } else { /* Perform audio data conversion and output */ unsigned i; int32_t *rp = input; int8_t *wp = output; int16_t *wp2 = output; int32_t *wp4 = output; if (bits_per_sample == 8) { for (i = 0; i < ret * num_channels; i++, wp++, rp++) *wp = *rp & 0xff; } else if (bits_per_sample == 16) { for (i = 0; i < ret * num_channels; i++, wp2++, rp++) *wp2 = *rp & 0xffff; } else if (bits_per_sample == 24 || bits_per_sample == 32) { for (i = 0; i < ret * num_channels; i++, wp4++, rp++) *wp4 = *rp; } playback->output->write_audio(output, ret * num_channels * SAMPLE_SIZE(bits_per_sample)); } } error_exit: free(input); free(output); wv_deattach (wvc_input, ctx); stop_flag = TRUE; return ! error; }
void Mixer::Clear() { for (int c = 0; c < mNumBuffers; c++) memset(mBuffer[c], 0, mInterleavedBufferSize * SAMPLE_SIZE(mFormat)); }
void SimpleBlockFile::FillCache() { if (mCache.active) return; // cache is already filled // Check sample format wxFFile file(mFileName.GetFullPath(), wxT("rb")); if (!file.IsOpened()) { // Don't read into cache if file not available return; } auHeader header; if (file.Read(&header, sizeof(header)) != sizeof(header)) { // Corrupt file return; } wxUint32 encoding; if (header.magic == 0x2e736e64) encoding = header.encoding; // correct endianness else encoding = SwapUintEndianess(header.encoding); switch (encoding) { case AU_SAMPLE_FORMAT_16: mCache.format = int16Sample; break; case AU_SAMPLE_FORMAT_24: mCache.format = int24Sample; break; default: // floatSample is a safe default (we will never loose data) mCache.format = floatSample; break; } file.Close(); // Read samples into cache mCache.sampleData = new char[mLen * SAMPLE_SIZE(mCache.format)]; if (ReadData(mCache.sampleData, mCache.format, 0, mLen) != mLen) { // Could not read all samples delete mCache.sampleData; return; } // Read summary data into cache mCache.summaryData = new char[mSummaryInfo.totalSummaryBytes]; if (!ReadSummary(mCache.summaryData)) memset(mCache.summaryData, 0, mSummaryInfo.totalSummaryBytes); // Cache is active but already on disk mCache.active = true; mCache.needWrite = false; //wxLogDebug("SimpleBlockFile::FillCache(): Succesfully read simple block file into cache."); }
void Mixer::MixSameRate(int *channelFlags, WaveTrack * src, double t0, double t1) { if ((t0 - src->GetOffset()) >= src->GetNumSamples() / src->GetRate() || (t1 - src->GetOffset()) <= 0) return; int s0 = int ((t0 - src->GetOffset()) * src->GetRate() + 0.5); int s1 = int ((t1 - src->GetOffset()) * src->GetRate() + 0.5); int slen = s1 - s0; if (slen <= 0) return; if (slen > mBufferSize) slen = mBufferSize; GetSamples(src, s0, slen); double volume; if (mUseVolumeSlider) volume = mControlToolBar->GetSoundVol(); else volume = 1.0; Envelope *e = src->GetEnvelope(); e->GetValues(mEnvValues, slen, t0, 1.0 / mRate); // Mix it down to the appropriate tracks for (int c = 0; c < mNumChannels; c++) { if (!channelFlags[c]) continue; samplePtr destPtr; int skip; if (mInterleaved) { destPtr = mBuffer[0] + c*SAMPLE_SIZE(mFormat); skip = mNumChannels; } else { destPtr = mBuffer[c]; skip = 1; } // This is the mixing inner loop, which we want // as optimized as possible switch(mFormat) { case int16Sample: { short *dest = (short *)destPtr; short *temp = (short *)mTemp; for (int j = 0; j < slen; j++) { *dest += (short)rint(temp[j] * volume * mEnvValues[j]); dest += skip; } } break; case int24Sample: { int *dest = (int *)destPtr; int *temp = (int *)mTemp; for (int j = 0; j < slen; j++) { *dest += (int)rint(temp[j] * volume * mEnvValues[j]); dest += skip; } } break; case floatSample: { float *dest = (float *)destPtr; float *temp = (float *)mTemp; for (int j = 0; j < slen; j++) { *dest += temp[j] * volume * mEnvValues[j]; dest += skip; } } break; } // switch } }
/// Read the data portion of the block file using libsndfile. Convert it /// to the given format if it is not already. /// /// @param data The buffer where the data will be stored /// @param format The format the data will be stored in /// @param start The offset in this block file /// @param len The number of samples to read int SimpleBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { if (mCache.active) { //wxLogDebug("SimpleBlockFile::ReadData(): Data are already in cache."); if (len > mLen - start) len = mLen - start; CopySamples( (samplePtr)(((char*)mCache.sampleData) + start * SAMPLE_SIZE(mCache.format)), mCache.format, data, format, len); return len; } else { //wxLogDebug("SimpleBlockFile::ReadData(): Reading data from disk."); SF_INFO info; wxFile f; // will be closed when it goes out of scope SNDFILE *sf = NULL; { Maybe<wxLogNull> silence{}; if (mSilentLog) silence.create(); memset(&info, 0, sizeof(info)); if (f.Open(mFileName.GetFullPath())) { // Even though there is an sf_open() that takes a filename, use the one that // takes a file descriptor since wxWidgets can open a file with a Unicode name and // libsndfile can't (under Windows). sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE); } if (!sf) { memset(data, 0, SAMPLE_SIZE(format)*len); mSilentLog = TRUE; return len; } } mSilentLog=FALSE; sf_seek(sf, start, SEEK_SET); SampleBuffer buffer(len, floatSample); int framesRead = 0; // If both the src and dest formats are integer formats, // read integers from the file (otherwise we would be // converting to float and back, which is unneccesary) if (format == int16Sample && sf_subtype_is_integer(info.format)) { framesRead = sf_readf_short(sf, (short *)data, len); } else if (format == int24Sample && sf_subtype_is_integer(info.format)) { framesRead = sf_readf_int(sf, (int *)data, len); // libsndfile gave us the 3 byte sample in the 3 most // significant bytes -- we want it in the 3 least // significant bytes. int *intPtr = (int *)data; for( int i = 0; i < framesRead; i++ ) intPtr[i] = intPtr[i] >> 8; } else {
///returns the number of samples filled in from start. //also updates data and len to reflect NEW unfilled area - start is unmodified. int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleFormat outFormat, sampleCount &start, sampleCount& len, unsigned int channel) { if(mDecodeCache.size() <= 0) return 0; int samplesFilled=0; //do a search for the best position to start at. //Guess that the array is evenly spaced from end to end - (dictionary sort) //assumes the array is sorted. //all we need for this to work is a location in the cache array //that has a start time of less than our start sample, but try to get closer with binary search int searchStart = 0; int searchEnd = mDecodeCache.size(); int guess; if(searchEnd>kODFFmpegSearchThreshold) { //first just guess that the cache is contiguous and we can just use math to figure it out like a dictionary //by guessing where our hit will be. while(searchStart+1<searchEnd) { guess = (searchStart+searchEnd)/2;//find a midpoint. //searchStart+ (searchEnd-searchStart)* ((float)start - mDecodeCache[searchStart]->start )/mDecodeCache[searchEnd]->start; //we want guess to point at the first index that hits even if there are duplicate start times (which can happen) if(mDecodeCache[guess]->start+mDecodeCache[guess]->len >= start) searchEnd = --guess; else searchStart = guess; } } //this is a sorted array for(int i=searchStart; i < (int)mDecodeCache.size(); i++) { //check for a cache hit - be careful to include the first/last sample an nothing more. //we only accept cache hits that touch either end - no piecing out of the middle. //this way the amount to be decoded remains set. if(start < mDecodeCache[i]->start+mDecodeCache[i]->len && start + len > mDecodeCache[i]->start) { uint8_t* outBuf; outBuf = (uint8_t*)data; //reject buffers that would split us into two pieces because we don't have //a method of dealing with this yet, and it won't happen very often. if(start<mDecodeCache[i]->start && start+len > mDecodeCache[i]->start+mDecodeCache[i]->len) continue; int samplesHit; int hitStartInCache; int hitStartInRequest; int nChannels = mDecodeCache[i]->numChannels; samplesHit = FFMIN(start+len,mDecodeCache[i]->start+mDecodeCache[i]->len) - FFMAX(mDecodeCache[i]->start,start); //find the start of the hit relative to the cache buffer start. hitStartInCache = FFMAX(0,start-mDecodeCache[i]->start); //we also need to find out which end was hit - if it is the tail only we need to update from a later index. hitStartInRequest = start <mDecodeCache[i]->start?len - samplesHit: 0; sampleCount outIndex,inIndex; for(int j=0;j<samplesHit;j++) { outIndex = hitStartInRequest + j; inIndex = (hitStartInCache + j) * nChannels + channel; switch (mDecodeCache[i]->samplefmt) { case SAMPLE_FMT_U8: //printf("u8 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); ((int16_t *)outBuf)[outIndex] = (int16_t) (((uint8_t*)mDecodeCache[i]->samplePtr)[inIndex] - 0x80) << 8; break; case SAMPLE_FMT_S16: //printf("u16 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); ((int16_t *)outBuf)[outIndex] = ((int16_t*)mDecodeCache[i]->samplePtr)[inIndex]; break; case SAMPLE_FMT_S32: //printf("s32 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); ((float *)outBuf)[outIndex] = (float) ((int32_t*)mDecodeCache[i]->samplePtr)[inIndex] * (1.0 / (1 << 31)); break; case SAMPLE_FMT_FLT: //printf("f in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); ((float *)outBuf)[outIndex] = (float) ((float*)mDecodeCache[i]->samplePtr)[inIndex]; break; case SAMPLE_FMT_DBL: //printf("dbl in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); ((float *)outBuf)[outIndex] = (float) ((double*)mDecodeCache[i]->samplePtr)[inIndex]; break; default: printf("ODDecodeFFMPEG TASK unrecognized sample format\n"); return 1; break; } } //update the cursor samplesFilled += samplesHit; //update the input start/len params - if the end was hit we can take off just len. //otherwise, we can assume only the front of the request buffer was hit since we don't allow it to be split. if(start < mDecodeCache[i]->start) len-=samplesHit; else { //we update data pointer too- but it is a typedef'd char* so be careful with the pointer math data+= samplesHit * (SAMPLE_SIZE(outFormat) / sizeof(*data)); start+=samplesHit; len -=samplesHit; } } //if we've had our fill, leave. if we've passed the point which can have hits, leave. if(len<=0 || mDecodeCache[i]->start > start+len) break; } return samplesFilled; }
bool Sequence::Append(samplePtr buffer, sampleFormat format, sampleCount len) { samplePtr temp = NULL; if (format != mSampleFormat) { temp = NewSamples(mMaxSamples, mSampleFormat); wxASSERT(temp); } // If the last block is not full, we need to add samples to it int numBlocks = mBlock->Count(); if (numBlocks > 0 && mBlock->Item(numBlocks - 1)->len < mMinSamples) { SeqBlock *lastBlock = mBlock->Item(numBlocks - 1); sampleCount addLen; if (lastBlock->len + len < mMaxSamples) addLen = len; else addLen = GetIdealBlockSize() - lastBlock->len; SeqBlock *newLastBlock = NewInitedSeqBlock(); samplePtr buffer2 = NewSamples((lastBlock->len + addLen), mSampleFormat); Read(buffer2, mSampleFormat, lastBlock, 0, lastBlock->len); if (format == mSampleFormat) memcpy(buffer2 + lastBlock->len * SAMPLE_SIZE(format), buffer, addLen * SAMPLE_SIZE(format)); else { CopySamples(buffer, format, temp, mSampleFormat, addLen); memcpy(buffer2 + lastBlock->len * SAMPLE_SIZE(format), temp, addLen * SAMPLE_SIZE(format)); } newLastBlock->start = lastBlock->start; newLastBlock->len = lastBlock->len + addLen; FirstWrite(buffer2, newLastBlock, lastBlock->len + addLen); DeleteSamples(buffer2); mDirManager->Deref(lastBlock->f); delete lastBlock; mBlock->Item(numBlocks - 1) = newLastBlock; len -= addLen; mNumSamples += addLen; buffer += addLen * SAMPLE_SIZE(format); } // Append the rest as new blocks while (len) { sampleCount idealSamples = GetIdealBlockSize(); sampleCount l = (len > idealSamples ? idealSamples : len); SeqBlock *w = new SeqBlock(); w->f = mDirManager->NewBlockFile(mSummary->totalSummaryBytes); w->start = mNumSamples; w->len = l; if (format == mSampleFormat) FirstWrite(buffer, w, l); else { CopySamples(buffer, format, temp, mSampleFormat, l); FirstWrite(temp, w, l); } mBlock->Add(w); buffer += l * SAMPLE_SIZE(format); mNumSamples += l; len -= l; } if (format != mSampleFormat) DeleteSamples(temp); ConsistencyCheck("Append"); return true; }
bool Sequence::Append(samplePtr buffer, sampleFormat format, sampleCount len, XMLWriter* blockFileLog /*=NULL*/) { // Quick check to make sure that it doesn't overflow if (((double)mNumSamples) + ((double)len) > wxLL(9223372036854775807)) return false; samplePtr temp = NULL; if (format != mSampleFormat) { temp = NewSamples(mMaxSamples, mSampleFormat); wxASSERT(temp); } // If the last block is not full, we need to add samples to it int numBlocks = mBlock->Count(); if (numBlocks > 0 && mBlock->Item(numBlocks - 1)->f->GetLength() < mMinSamples) { SeqBlock *lastBlock = mBlock->Item(numBlocks - 1); sampleCount addLen; if (lastBlock->f->GetLength() + len < mMaxSamples) addLen = len; else addLen = GetIdealBlockSize() - lastBlock->f->GetLength(); SeqBlock *newLastBlock = new SeqBlock(); samplePtr buffer2 = NewSamples((lastBlock->f->GetLength() + addLen), mSampleFormat); Read(buffer2, mSampleFormat, lastBlock, 0, lastBlock->f->GetLength()); CopySamples(buffer, format, buffer2 + lastBlock->f->GetLength() * SAMPLE_SIZE(mSampleFormat), mSampleFormat, addLen); newLastBlock->start = lastBlock->start; int newLastBlockLen = lastBlock->f->GetLength() + addLen; newLastBlock->f = mDirManager->NewSimpleBlockFile(buffer2, newLastBlockLen, mSampleFormat, blockFileLog != NULL); if (blockFileLog) ((SimpleBlockFile*)newLastBlock->f)->SaveXML(*blockFileLog); DeleteSamples(buffer2); mDirManager->Deref(lastBlock->f); delete lastBlock; mBlock->Item(numBlocks - 1) = newLastBlock; len -= addLen; mNumSamples += addLen; buffer += addLen * SAMPLE_SIZE(format); } // Append the rest as new blocks while (len) { sampleCount idealSamples = GetIdealBlockSize(); sampleCount l = (len > idealSamples ? idealSamples : len); SeqBlock *w = new SeqBlock(); w->start = mNumSamples; if (format == mSampleFormat) { w->f = mDirManager->NewSimpleBlockFile(buffer, l, mSampleFormat, blockFileLog != NULL); } else { CopySamples(buffer, format, temp, mSampleFormat, l); w->f = mDirManager->NewSimpleBlockFile(temp, l, mSampleFormat, blockFileLog != NULL); } if (blockFileLog) ((SimpleBlockFile*)w->f)->SaveXML(*blockFileLog); mBlock->Add(w); buffer += l * SAMPLE_SIZE(format); mNumSamples += l; len -= l; } if (format != mSampleFormat) DeleteSamples(temp); // JKC: During generate we use Append again and again. // If generating a long sequence this test would give O(n^2) // performance - not good! #ifdef VERY_SLOW_CHECKING ConsistencyCheck(wxT("Append")); #endif return true; }
AUDACITY_DLL_API samplePtr NewSamples(int count, sampleFormat format) { return (samplePtr)malloc(count * SAMPLE_SIZE(format)); }
bool Sequence::Paste(sampleCount s, const Sequence *src) { if (s < 0) s = 0; if (s >= mNumSamples) s = mNumSamples; // Quick check to make sure that it doesn't overflow if (((double)mNumSamples) + ((double)src->mNumSamples) > wxLL(9223372036854775807)) return false; BlockArray *srcBlock = src->mBlock; sampleCount addedLen = src->mNumSamples; unsigned int srcNumBlocks = srcBlock->Count(); int sampleSize = SAMPLE_SIZE(mSampleFormat); if (addedLen == 0 || srcNumBlocks == 0) return true; unsigned int b = FindBlock(s); unsigned int numBlocks = mBlock->Count(); if (numBlocks == 0 || (s == mNumSamples && mBlock->Item(numBlocks-1)->f->GetLength() >= mMinSamples)) { // Special case: this track is currently empty, or it's safe to append // onto the end because the current last block is longer than the // minimum size for (unsigned int i = 0; i < srcNumBlocks; i++) AppendBlock(srcBlock->Item(i)); return ConsistencyCheck(wxT("Paste branch one")); } if (b >= 0 && b < numBlocks && mBlock->Item(b)->f->GetLength() + addedLen < mMaxSamples) { // Special case: we can fit all of the new samples inside of // one block! samplePtr buffer = NewSamples(mMaxSamples, mSampleFormat); int splitPoint = s - mBlock->Item(b)->start; Read(buffer, mSampleFormat, mBlock->Item(b), 0, splitPoint); src->Get(buffer + splitPoint*sampleSize, mSampleFormat, 0, addedLen); Read(buffer + (splitPoint + addedLen)*sampleSize, mSampleFormat, mBlock->Item(b), splitPoint, mBlock->Item(b)->f->GetLength() - splitPoint); SeqBlock *largerBlock = new SeqBlock(); largerBlock->start = mBlock->Item(b)->start; int largerBlockLen = mBlock->Item(b)->f->GetLength() + addedLen; if (largerBlockLen > mMaxSamples) largerBlockLen = mMaxSamples; // Prevent overruns, per NGS report for UmixIt. largerBlock->f = mDirManager->NewSimpleBlockFile(buffer, largerBlockLen, mSampleFormat); mDirManager->Deref(mBlock->Item(b)->f); delete mBlock->Item(b); mBlock->Item(b) = largerBlock; for (unsigned int i = b + 1; i < numBlocks; i++) mBlock->Item(i)->start += addedLen; mNumSamples += addedLen; DeleteSamples(buffer); return ConsistencyCheck(wxT("Paste branch two")); } // Case two: if we are inserting four or fewer blocks, // it's simplest to just lump all the data together // into one big block along with the split block, // then resplit it all unsigned int i; BlockArray *newBlock = new BlockArray(); newBlock->Alloc(numBlocks + srcNumBlocks + 2); int newNumBlocks = 0; for (i = 0; i < b; i++) { newBlock->Add(mBlock->Item(i)); newNumBlocks++; } SeqBlock *splitBlock = mBlock->Item(b); sampleCount splitLen = mBlock->Item(b)->f->GetLength(); int splitPoint = s - splitBlock->start; if (srcNumBlocks <= 4) { sampleCount sum = splitLen + addedLen; samplePtr sumBuffer = NewSamples(sum, mSampleFormat); Read(sumBuffer, mSampleFormat, splitBlock, 0, splitPoint); src->Get(sumBuffer + splitPoint * sampleSize, mSampleFormat, 0, addedLen); Read(sumBuffer + (splitPoint + addedLen) * sampleSize, mSampleFormat, splitBlock, splitPoint, splitBlock->f->GetLength() - splitPoint); BlockArray *split = Blockify(sumBuffer, sum); for (i = 0; i < split->Count(); i++) { split->Item(i)->start += splitBlock->start; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; DeleteSamples(sumBuffer); } else { // The final case is that we're inserting at least five blocks. // We divide these into three groups: the first two get merged // with the first half of the split block, the middle ones get // copied in as is, and the last two get merged with the last // half of the split block. sampleCount srcFirstTwoLen = srcBlock->Item(0)->f->GetLength() + srcBlock->Item(1)->f->GetLength(); sampleCount leftLen = splitPoint + srcFirstTwoLen; samplePtr leftBuffer = NewSamples(leftLen, mSampleFormat); Read(leftBuffer, mSampleFormat, splitBlock, 0, splitPoint); src->Get(leftBuffer + splitPoint*sampleSize, mSampleFormat, 0, srcFirstTwoLen); BlockArray *split = Blockify(leftBuffer, leftLen); for (i = 0; i < split->Count(); i++) { split->Item(i)->start += splitBlock->start; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; DeleteSamples(leftBuffer); for (i = 2; i < srcNumBlocks - 2; i++) { SeqBlock *insertBlock = new SeqBlock(); insertBlock->start = srcBlock->Item(i)->start + s; insertBlock->f = mDirManager->CopyBlockFile(srcBlock->Item(i)->f); if (!insertBlock->f) { // TODO error: Could not paste! (Out of disk space?) return false; } newBlock->Add(insertBlock); newNumBlocks++; } sampleCount srcLastTwoLen = srcBlock->Item(srcNumBlocks - 2)->f->GetLength() + srcBlock->Item(srcNumBlocks - 1)->f->GetLength(); sampleCount rightSplit = splitBlock->f->GetLength() - splitPoint; sampleCount rightLen = rightSplit + srcLastTwoLen; samplePtr rightBuffer = NewSamples(rightLen, mSampleFormat); sampleCount lastStart = srcBlock->Item(srcNumBlocks - 2)->start; src->Get(rightBuffer, mSampleFormat, lastStart, srcLastTwoLen); Read(rightBuffer + srcLastTwoLen * sampleSize, mSampleFormat, splitBlock, splitPoint, rightSplit); sampleCount pos = s + lastStart; split = Blockify(rightBuffer, rightLen); for (i = 0; i < split->Count(); i++) { split->Item(i)->start += pos; newBlock->Add(split->Item(i)); newNumBlocks++; } delete split; DeleteSamples(rightBuffer); } mDirManager->Deref(splitBlock->f); delete splitBlock; // Copy remaining blocks to new block array and // swap the new block array in for the old for (i = b + 1; i < numBlocks; i++) { mBlock->Item(i)->start += addedLen; newBlock->Add(mBlock->Item(i)); newNumBlocks++; } delete mBlock; mBlock = newBlock; mNumSamples += addedLen; return ConsistencyCheck(wxT("Paste branch three")); }
// TODO: Risky? Assumes 0.0f is represented by 0x00000000; void ClearSamples(samplePtr src, sampleFormat format, int start, int len) { int size = SAMPLE_SIZE(format); memset(src + start*size, 0, len*size); }
void ComputeLegacySummaryInfo(wxFileName fileName, int summaryLen, sampleFormat format, SummaryInfo *info, bool noRMS,bool Silent, float *min, float *max, float *rms) { int fields = 3; /* min, max, rms */ if (noRMS) fields = 2; info->fields = fields; info->format = format; info->bytesPerFrame = SAMPLE_SIZE(info->format) * fields; info->totalSummaryBytes = summaryLen; info->offset64K = 20; /* legacy header tag len */ info->frames64K = (summaryLen-20) / (info->bytesPerFrame * 256); info->offset256 = info->offset64K + (info->frames64K * info->bytesPerFrame); info->frames256 = (summaryLen - 20 - (info->frames64K * info->bytesPerFrame)) / info->bytesPerFrame; // // Compute the min, max, and RMS of the block from the // 64K summary data // float *summary = new float[info->frames64K * fields]; SampleBuffer data(info->frames64K * fields, info->format); int read; { Maybe<wxLogNull> silence{}; wxFFile summaryFile(fileName.GetFullPath(), wxT("rb")); if (Silent) silence.create(); if (!summaryFile.IsOpened()) { wxLogWarning(wxT("Unable to access summary file %s; substituting silence for remainder of session"), fileName.GetFullPath().c_str()); read = info->frames64K * info->bytesPerFrame; memset(data.ptr(), 0, read); } else{ summaryFile.Seek(info->offset64K); read = summaryFile.Read(data.ptr(), info->frames64K * info->bytesPerFrame); } } int count = read / info->bytesPerFrame; CopySamples(data.ptr(), info->format, (samplePtr)summary, floatSample, count); (*min) = FLT_MAX; (*max) = FLT_MIN; float sumsq = 0; for(int i=0; i<count; i++) { if (summary[fields*i] < (*min)) (*min) = summary[fields*i]; if (summary[fields*i+1] > (*max)) (*max) = summary[fields*i+1]; if (fields >= 3) sumsq += summary[fields*i+2]*summary[fields*i+2]; } if (fields >= 3) (*rms) = sqrt(sumsq / count); else (*rms) = 0; delete[] summary; }
/// Reads the specified data from the aliased file, using libsndfile, /// and converts it to the given sample format. /// /// @param data The buffer to read the sample data into. /// @param format The format to convert the data into /// @param start The offset within the block to begin reading /// @param len The number of samples to read int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { SF_INFO info; if(!mAliasedFileName.IsOk()){ // intentionally silenced memset(data,0,SAMPLE_SIZE(format)*len); return len; } wxLogNull *silence=0; if(mSilentAliasLog)silence= new wxLogNull(); memset(&info, 0, sizeof(info)); SNDFILE *sf=sf_open(OSFILENAME(mAliasedFileName.GetFullPath()), SFM_READ, &info); if (!sf){ memset(data,0,SAMPLE_SIZE(format)*len); if(silence) delete silence; mSilentAliasLog=TRUE; return len; } if(silence) delete silence; mSilentAliasLog=FALSE; sf_seek(sf, mAliasStart + start, SEEK_SET); samplePtr buffer = NewSamples(len * info.channels, floatSample); int framesRead = 0; if (format == int16Sample && !sf_subtype_more_than_16_bits(info.format)) { // Special case: if the file is in 16-bit (or less) format, // and the calling method wants 16-bit data, go ahead and // read 16-bit data directly. This is a pretty common // case, as most audio files are 16-bit. framesRead = sf_readf_short(sf, (short *)buffer, len); for (int i = 0; i < framesRead; i++) ((short *)data)[i] = ((short *)buffer)[(info.channels * i) + mAliasChannel]; } else { // Otherwise, let libsndfile handle the conversion and // scaling, and pass us normalized data as floats. We can // then convert to whatever format we want. framesRead = sf_readf_float(sf, (float *)buffer, len); float *bufferPtr = &((float *)buffer)[mAliasChannel]; CopySamples((samplePtr)bufferPtr, floatSample, (samplePtr)data, format, framesRead, true, info.channels); } DeleteSamples(buffer); sf_close(sf); return framesRead; }
/// Reads the specified data from the aliased file, using libsndfile, /// and converts it to the given sample format. /// Copied from PCMAliasBlockFIle but wxLog calls taken out for thread safety /// /// @param data The buffer to read the sample data into. /// @param format The format to convert the data into /// @param start The offset within the block to begin reading /// @param len The number of samples to read int ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { mReadDataMutex.Lock(); SF_INFO info; if(!mAliasedFileName.IsOk()){ // intentionally silenced memset(data,0,SAMPLE_SIZE(format)*len); mReadDataMutex.Unlock(); return len; } memset(&info, 0, sizeof(info)); wxString aliasPath = mAliasedFileName.GetFullPath(); //there are thread-unsafe crashes here - not sure why. sf_open may be called on the same file //from different threads, but this seems okay, unless it is implemented strangely.. static ODLock sfMutex; wxFile f; // will be closed when it goes out of scope SNDFILE *sf = NULL; if (f.Open(aliasPath)) { // Even though there is an sf_open() that takes a filename, use the one that // takes a file descriptor since wxWidgets can open a file with a Unicode name and // libsndfile can't (under Windows). ODManager::LockLibSndFileMutex(); sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE); ODManager::UnlockLibSndFileMutex(); } if (!sf){ memset(data,0,SAMPLE_SIZE(format)*len); mSilentAliasLog=TRUE; mReadDataMutex.Unlock(); return len; } mSilentAliasLog=FALSE; ODManager::LockLibSndFileMutex(); sf_seek(sf, mAliasStart + start, SEEK_SET); ODManager::UnlockLibSndFileMutex(); samplePtr buffer = NewSamples(len * info.channels, floatSample); int framesRead = 0; if (format == int16Sample && !sf_subtype_more_than_16_bits(info.format)) { // Special case: if the file is in 16-bit (or less) format, // and the calling method wants 16-bit data, go ahead and // read 16-bit data directly. This is a pretty common // case, as most audio files are 16-bit. ODManager::LockLibSndFileMutex(); framesRead = sf_readf_short(sf, (short *)buffer, len); ODManager::UnlockLibSndFileMutex(); for (int i = 0; i < framesRead; i++) ((short *)data)[i] = ((short *)buffer)[(info.channels * i) + mAliasChannel]; } else { // Otherwise, let libsndfile handle the conversion and // scaling, and pass us normalized data as floats. We can // then convert to whatever format we want. ODManager::LockLibSndFileMutex(); framesRead = sf_readf_float(sf, (float *)buffer, len); ODManager::UnlockLibSndFileMutex(); float *bufferPtr = &((float *)buffer)[mAliasChannel]; CopySamples((samplePtr)bufferPtr, floatSample, (samplePtr)data, format, framesRead, true, info.channels); } DeleteSamples(buffer); ODManager::LockLibSndFileMutex(); sf_close(sf); ODManager::UnlockLibSndFileMutex(); mReadDataMutex.Unlock(); return framesRead; }
bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, sampleCount *where, int numPixels, double t0, double pixelsPerSecond, bool &isLoadingOD) { mWaveCacheMutex.Lock(); if (mWaveCache && mWaveCache->dirty == mDirty && mWaveCache->start == t0 && mWaveCache->len >= numPixels && mWaveCache->pps == pixelsPerSecond) { //check for invalid regions, and make the bottom if an else if. //invalid regions are kept in a sorted array. for(int i=0;i<mWaveCache->GetNumInvalidRegions();i++) { int invStart; invStart = mWaveCache->GetInvalidRegionStart(i); int invEnd; invEnd = mWaveCache->GetInvalidRegionEnd(i); int regionODPixels; regionODPixels =0; int regionODPixelsAfter; regionODPixelsAfter =0; //before check number of ODPixels for(int j=invStart;j<invEnd;j++) { if(mWaveCache->bl[j]<0) regionODPixels++; } mSequence->GetWaveDisplay(&mWaveCache->min[invStart], &mWaveCache->max[invStart], &mWaveCache->rms[invStart], &mWaveCache->bl[invStart], invEnd-invStart, &mWaveCache->where[invStart], mRate / pixelsPerSecond); //after check number of ODPixels for(int j=invStart;j<invEnd;j++) { if(mWaveCache->bl[j]<0) regionODPixelsAfter++; } //decrement the number of od pixels. mWaveCache->numODPixels -= (regionODPixels - regionODPixelsAfter); } mWaveCache->ClearInvalidRegions(); memcpy(min, mWaveCache->min, numPixels*sizeof(float)); memcpy(max, mWaveCache->max, numPixels*sizeof(float)); memcpy(rms, mWaveCache->rms, numPixels*sizeof(float)); memcpy(bl, mWaveCache->bl, numPixels*sizeof(int)); memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount)); isLoadingOD = mWaveCache->numODPixels>0; mWaveCacheMutex.Unlock(); return true; } WaveCache *oldCache = mWaveCache; mWaveCache = new WaveCache(numPixels); mWaveCache->pps = pixelsPerSecond; mWaveCache->rate = mRate; mWaveCache->start = t0; double tstep = 1.0 / pixelsPerSecond; sampleCount x; for (x = 0; x < mWaveCache->len + 1; x++) { mWaveCache->where[x] = (sampleCount) floor(t0 * mRate + ((double) x) * mRate * tstep + 0.5); } //mchinen: I think s0 - s1 represents the range of samples that we will need to look up. likewise p0-p1 the number of pixels. sampleCount s0 = mWaveCache->where[0]; sampleCount s1 = mWaveCache->where[mWaveCache->len]; int p0 = 0; int p1 = mWaveCache->len; // Optimization: if the old cache is good and overlaps // with the current one, re-use as much of the cache as // possible if (oldCache->dirty == mDirty && oldCache->pps == pixelsPerSecond && oldCache->where[0] < mWaveCache->where[mWaveCache->len] && oldCache->where[oldCache->len] > mWaveCache->where[0]) { //now we are assuming the entire range is covered by the old cache and reducing s1/s0 as we find out otherwise. s0 = mWaveCache->where[mWaveCache->len]; //mchinen:s0 is the min sample covered up to by the wave cache. will shrink if old doen't overlap s1 = mWaveCache->where[0]; //mchinen - same, but the maximum sample covered. p0 = mWaveCache->len; p1 = 0; //check for invalid regions, and make the bottom if an else if. //invalid regions are keep in a sorted array. //TODO:integrate into below for loop so that we only load inval regions if //necessary. (usually is the case, so no rush.) //also, we should be updating the NEW cache, but here we are patching the old one up. for(int i=0;i<oldCache->GetNumInvalidRegions();i++) { int invStart; invStart = oldCache->GetInvalidRegionStart(i); int invEnd; invEnd = oldCache->GetInvalidRegionEnd(i); mSequence->GetWaveDisplay(&oldCache->min[invStart], &oldCache->max[invStart], &oldCache->rms[invStart], &oldCache->bl[invStart], invEnd-invStart, &oldCache->where[invStart], mRate / pixelsPerSecond); } oldCache->ClearInvalidRegions(); for (x = 0; x < mWaveCache->len; x++) { //below is regular cache access. if (mWaveCache->where[x] >= oldCache->where[0] && mWaveCache->where[x] <= oldCache->where[oldCache->len - 1]) { //if we hit an invalid region, load it up. int ox = int ((double (oldCache->len) * (mWaveCache->where[x] - oldCache->where[0])) /(oldCache->where[oldCache->len] - oldCache->where[0]) + 0.5); mWaveCache->min[x] = oldCache->min[ox]; mWaveCache->max[x] = oldCache->max[ox]; mWaveCache->rms[x] = oldCache->rms[ox]; mWaveCache->bl[x] = oldCache->bl[ox]; } else { if (mWaveCache->where[x] < s0) { s0 = mWaveCache->where[x]; p0 = x; } if (mWaveCache->where[x + 1] > s1) { s1 = mWaveCache->where[x + 1]; p1 = x + 1; } } } } if (p1 > p0) { /* handle values in the append buffer */ int numSamples = mSequence->GetNumSamples(); int a; for(a=p0; a<p1; a++) if (mWaveCache->where[a+1] > numSamples) break; //compute the values that are outside the overlap from scratch. if (a < p1) { int i; sampleFormat seqFormat = mSequence->GetSampleFormat(); bool didUpdate = false; for(i=a; i<p1; i++) { sampleCount left; left = mWaveCache->where[i] - numSamples; sampleCount right; right = mWaveCache->where[i+1] - numSamples; //wxCriticalSectionLocker locker(mAppendCriticalSection); if (left < 0) left = 0; if (right > mAppendBufferLen) right = mAppendBufferLen; if (right > left) { float *b; sampleCount len = right-left; sampleCount j; if (seqFormat == floatSample) b = &((float *)mAppendBuffer)[left]; else { b = new float[len]; CopySamples(mAppendBuffer + left*SAMPLE_SIZE(seqFormat), seqFormat, (samplePtr)b, floatSample, len); } float max = b[0]; float min = b[0]; float sumsq = b[0] * b[0]; for(j=1; j<len; j++) { if (b[j] > max) max = b[j]; if (b[j] < min) min = b[j]; sumsq += b[j]*b[j]; } mWaveCache->min[i] = min; mWaveCache->max[i] = max; mWaveCache->rms[i] = (float)sqrt(sumsq / len); mWaveCache->bl[i] = 1; //for now just fake it. if (seqFormat != floatSample) delete[] b; didUpdate=true; } } // So that the sequence doesn't try to write any // of these values //mchinen: but only do this if we've updated pixels in the cache. if(didUpdate) p1 = a; } if (p1 > p0) { if (!mSequence->GetWaveDisplay(&mWaveCache->min[p0], &mWaveCache->max[p0], &mWaveCache->rms[p0], &mWaveCache->bl[p0], p1-p0, &mWaveCache->where[p0], mRate / pixelsPerSecond)) { isLoadingOD=false; mWaveCacheMutex.Unlock(); return false; } } } mWaveCache->dirty = mDirty; delete oldCache; memcpy(min, mWaveCache->min, numPixels*sizeof(float)); memcpy(max, mWaveCache->max, numPixels*sizeof(float)); memcpy(rms, mWaveCache->rms, numPixels*sizeof(float)); memcpy(bl, mWaveCache->bl, numPixels*sizeof(int)); memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount)); //find the number of OD pixels - the only way to do this is by recounting since we've lost some old cache. mWaveCache->numODPixels = 0; for(int j=0;j<mWaveCache->len;j++) if(mWaveCache->bl[j]<0) mWaveCache->numODPixels++; isLoadingOD = mWaveCache->numODPixels>0; mWaveCacheMutex.Unlock(); return true; }