/// Writes the xml as a SimpleBlockFile if we can (if we have a summary file) /// Otherwise writes XML as a subset of attributes with 'odpcmaliasblockfile as the start tag. /// Most notably, the summaryfile attribute refers to a file that does not yet, so when the project file is read back in /// and this object reconstructed, it needs to avoid trying to open it as well as schedule itself for OD loading void ODDecodeBlockFile::SaveXML(XMLWriter &xmlFile) { LockRead(); if(IsSummaryAvailable()) { SimpleBlockFile::SaveXML(xmlFile); } else { xmlFile.StartTag(wxT("oddecodeblockfile")); //unlock to prevent deadlock and resume lock after. UnlockRead(); mFileNameMutex.Lock(); xmlFile.WriteAttr(wxT("summaryfile"), mFileName.GetFullName()); mFileNameMutex.Unlock(); LockRead(); xmlFile.WriteAttr(wxT("audiofile"), mAudioFileName.GetFullPath()); xmlFile.WriteAttr(wxT("aliasstart"), mAliasStart); xmlFile.WriteAttr(wxT("aliaslen"), mLen); xmlFile.WriteAttr(wxT("aliaschannel"), mAliasChannel); xmlFile.WriteAttr(wxT("decodetype"), (size_t)mType); xmlFile.EndTag(wxT("oddecodeblockfile")); } UnlockRead(); }
/// Writes the xml as a PCMAliasBlockFile if we can (if we have a summary file) /// Otherwise writes XML as a subset of attributes with 'odpcmaliasblockfile as the start tag. /// Most notably, the summaryfile attribute refers to a file that does not yet, so when the project file is read back in /// and this object reconstructed, it needs to avoid trying to open it as well as schedule itself for OD loading void ODPCMAliasBlockFile::SaveXML(XMLWriter &xmlFile) { //we lock this so that mAliasedFileName doesn't change. LockRead(); if(IsSummaryAvailable()) { PCMAliasBlockFile::SaveXML(xmlFile); mHasBeenSaved = true; } else { xmlFile.StartTag(wxT("odpcmaliasblockfile")); //unlock to prevent deadlock and resume lock after. UnlockRead(); mFileNameMutex.Lock(); xmlFile.WriteAttr(wxT("summaryfile"), mFileName.GetFullName()); mFileNameMutex.Unlock(); LockRead(); xmlFile.WriteAttr(wxT("aliasfile"), mAliasedFileName.GetFullPath()); xmlFile.WriteAttr(wxT("aliasstart"), mAliasStart); xmlFile.WriteAttr(wxT("aliaslen"), mLen); xmlFile.WriteAttr(wxT("aliaschannel"), mAliasChannel); xmlFile.EndTag(wxT("odpcmaliasblockfile")); } UnlockRead(); }
/// If the summary has been computed, /// Construct a new PCMAliasBlockFile based on this one. /// otherwise construct an ODPCMAliasBlockFile that still needs to be computed. /// @param newFileName The filename to copy the summary data to. BlockFile *ODDecodeBlockFile::Copy(wxFileName newFileName) { BlockFile *newBlockFile; //mAliasedFile can change so we lock readdatamutex, which is responsible for it. LockRead(); if(IsSummaryAvailable()) { //create a simpleblockfile, because once it has the summary it is a simpleblockfile for all intents an purposes newBlockFile = SimpleBlockFile::Copy(newFileName) ; } else { //Summary File might exist in this case, but it probably (99.999% of the time) won't. newBlockFile = new ODDecodeBlockFile(newFileName, mAudioFileName, mAliasStart, mLen, mAliasChannel, mType, mMin, mMax, mRMS,IsSummaryAvailable()); //The client code will need to schedule this blockfile for OD decoding if it is going to a new track. //It can do this by checking for IsDataAvailable()==false. } UnlockRead(); return newBlockFile; }
/// If the summary has been computed, /// Construct a NEW PCMAliasBlockFile based on this one. /// otherwise construct an ODPCMAliasBlockFile that still needs to be computed. /// @param newFileName The filename to copy the summary data to. BlockFile *ODPCMAliasBlockFile::Copy(wxFileName newFileName) { BlockFile *newBlockFile; //mAliasedFile can change so we lock readdatamutex, which is responsible for it. LockRead(); //If the file has been written AND it has been saved, we create a PCM alias blockfile because for //all intents and purposes, it is the same. //However, if it hasn't been saved yet, we shouldn't create one because the default behavior of the //PCMAliasBlockFile is to lock on exit, and this will cause orphaned blockfiles.. if(IsSummaryAvailable() && mHasBeenSaved) { newBlockFile = new PCMAliasBlockFile(newFileName, mAliasedFileName, mAliasStart, mLen, mAliasChannel, mMin, mMax, mRMS); } else { //Summary File might exist in this case, but it might not. newBlockFile = new ODPCMAliasBlockFile(newFileName, mAliasedFileName, mAliasStart, mLen, mAliasChannel, mMin, mMax, mRMS,IsSummaryAvailable()); //The client code will need to schedule this blockfile for OD summarizing if it is going to a NEW track. } UnlockRead(); return newBlockFile; }
/// 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 ODDecodeBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len) { int ret; LockRead(); if(IsSummaryAvailable()) ret= SimpleBlockFile::ReadData(data,format,start,len); else { //we should do an ODRequest to start processing the data here, and wait till it finishes. and just do a SimpleBlockFIle //ReadData. ClearSamples(data, format, 0, len); ret= len; } UnlockRead(); return ret; }
/// 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) { LockRead(); SF_INFO info; if(!mAliasedFileName.IsOk()){ // intentionally silenced memset(data,0,SAMPLE_SIZE(format)*len); UnlockRead(); 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.Exists(aliasPath) && 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; // Set a marker to display an error message if (!wxGetApp().ShouldShowMissingAliasedFileWarning()) wxGetApp().MarkAliasedFilesMissingWarning(this); UnlockRead(); 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(); UnlockRead(); 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 size_t ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, size_t start, size_t len) const { LockRead(); SF_INFO info; if(!mAliasedFileName.IsOk()){ // intentionally silenced memset(data,0,SAMPLE_SIZE(format)*len); UnlockRead(); return len; } memset(&info, 0, sizeof(info)); wxString aliasPath = mAliasedFileName.GetFullPath(); wxFile f; // will be closed when it goes out of scope SFFile sf; if (f.Exists(aliasPath) && 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). sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, FALSE)); } // FIXME: TRAP_ERR failure of wxFile open incompletely handled in ODPCMAliasBlockFile::ReadData. if (!sf) { memset(data,0,SAMPLE_SIZE(format)*len); mSilentAliasLog = TRUE; // Set a marker to display an error message if (!wxGetApp().ShouldShowMissingAliasedFileWarning()) wxGetApp().MarkAliasedFilesMissingWarning(this); UnlockRead(); return len; } mSilentAliasLog=FALSE; // Third party library has its own type alias, check it static_assert(sizeof(sampleCount::type) <= sizeof(sf_count_t), "Type sf_count_t is too narrow to hold a sampleCount"); SFCall<sf_count_t>(sf_seek, sf.get(), ( mAliasStart + start ).as_long_long(), SEEK_SET); wxASSERT(info.channels >= 0); SampleBuffer buffer(len * info.channels, floatSample); size_t 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 = SFCall<sf_count_t>(sf_readf_short, sf.get(), (short *)buffer.ptr(), len); 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. framesRead = SFCall<sf_count_t>(sf_readf_float, sf.get(), (float *)buffer.ptr(), len); float *bufferPtr = &((float *)buffer.ptr())[mAliasChannel]; CopySamples((samplePtr)bufferPtr, floatSample, (samplePtr)data, format, framesRead, true, info.channels); } UnlockRead(); return framesRead; }