📄 simpleblockfile.cpp
字号:
/********************************************************************** Audacity: A Digital Audio Editor SimpleBlockFile.cpp Joshua Haberman**********************************************************************/#include <wx/filefn.h>#include <wx/ffile.h>#include <wx/utils.h>#include <wx/log.h>#include "SimpleBlockFile.h"#include "../FileFormats.h"#include "sndfile.h"#include "../Internat.h"// The AU formats we care aboutenum { AU_SAMPLE_FORMAT_16 = 3, AU_SAMPLE_FORMAT_24 = 4, AU_SAMPLE_FORMAT_FLOAT = 6,};typedef struct { wxUint32 magic; // magic number wxUint32 dataOffset; // byte offset to start of audio data wxUint32 dataSize; // data length, in bytes (optional) wxUint32 encoding; // data encoding enumeration wxUint32 sampleRate; // samples per second wxUint32 channels; // number of interleaved channels} auHeader;/// Constructs a SimpleBlockFile based on sample data and writes/// it to disk.////// @param baseFileName The filename to use, but without an extension./// This constructor will add the appropriate/// extension (.au in this case)./// @param sampleData The sample data to be written to this block./// @param sampleLen The number of samples to be written to this block./// @param format The format of the given samples.SimpleBlockFile::SimpleBlockFile(wxFileName baseFileName, samplePtr sampleData, sampleCount sampleLen, sampleFormat format): BlockFile(wxFileName(baseFileName.GetFullPath() + wxT(".au")), sampleLen){ // Now checked in the DirManager //wxASSERT( !wxFileExists(FILENAME(mFileName.GetFullPath())) ); // Open and write the file wxFFile file(FILENAME(mFileName.GetFullPath()).c_str(), wxT("wb")); if( !file.IsOpened() ){ // Can't do anything else. return; } 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 void *summaryData = BlockFile::CalcSummary(sampleData, sampleLen, format); file.Write(&header, sizeof(header)); file.Write(summaryData, mSummaryInfo.totalSummaryBytes); 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++ )#if wxBYTE_ORDER == wxBIG_ENDIAN file.Write((char*)&int24sampleData[i] + 1, 3);#else file.Write((char*)&int24sampleData[i], 3);#endif } else { // for all other sample formats we can write straight from the buffer // to disk file.Write(sampleData, sampleLen * SAMPLE_SIZE(format)); }}/// Construct a SimpleBlockFile memory structure that will point to an/// existing block file. This file must exist and be a valid block file.////// @param existingFile The disk file this SimpleBlockFile should use.SimpleBlockFile::SimpleBlockFile(wxFileName existingFile, sampleCount len, float min, float max, float rms): BlockFile(existingFile, len){ mMin = min; mMax = max; mRMS = rms;}SimpleBlockFile::~SimpleBlockFile(){}/// Read the summary section of the disk file.////// @param *data The buffer to write the data to. It must be at least/// mSummaryinfo.totalSummaryBytes long.bool SimpleBlockFile::ReadSummary(void *data){ wxFFile file(FILENAME(mFileName.GetFullPath()).c_str(), wxT("rb")); wxLogNull *silence=0; if(mSilentLog)silence= new wxLogNull(); if(!file.IsOpened() ){ memset(data,0,(size_t)mSummaryInfo.totalSummaryBytes); if(silence) delete silence; mSilentLog=TRUE; return true; } if(silence) delete silence; mSilentLog=FALSE; // The offset is just past the au header if( !file.Seek(sizeof(auHeader)) ) return false; int read = (int)file.Read(data, (size_t)mSummaryInfo.totalSummaryBytes); FixSummary(data); return (read == mSummaryInfo.totalSummaryBytes);}/// 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 readint SimpleBlockFile::ReadData(samplePtr data, sampleFormat format, sampleCount start, sampleCount len){ SF_INFO info; wxLogNull *silence=0; if(mSilentLog)silence= new wxLogNull(); memset(&info, 0, sizeof(info)); #ifdef _UNICODE /* sf_open doesn't handle fn_Str() in Unicode build. May or may not actually work. */ SNDFILE *sf=sf_open(FILENAME(mFileName.GetFullPath()).mb_str(), SFM_READ, &info); #else // ANSI SNDFILE *sf=sf_open(FILENAME(mFileName.GetFullPath()).fn_str(), SFM_READ, &info); #endif // Unicode/ANSI if (!sf){ memset(data,0,SAMPLE_SIZE(format)*len); if(silence) delete silence; mSilentLog=TRUE; return len; } if(silence) delete silence; mSilentLog=FALSE; sf_seek(sf, start, SEEK_SET); samplePtr buffer = NewSamples(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 { // 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); CopySamples(buffer, floatSample, (samplePtr)data, format, framesRead); } DeleteSamples(buffer); sf_close(sf); return framesRead;}void SimpleBlockFile::SaveXML(int depth, wxFFile &xmlFile){ for(int i = 0; i < depth; i++) xmlFile.Write(wxT("\t")); xmlFile.Write(wxT("<simpleblockfile ")); xmlFile.Write(wxString::Format(wxT("filename='%s' "), XMLTagHandler::XMLEsc(mFileName.GetFullName()).c_str())); xmlFile.Write(wxString::Format(wxT("len='%d' "), mLen)); xmlFile.Write(wxString::Format(wxT("min='%s' "), Internat::ToString(mMin).c_str())); xmlFile.Write(wxString::Format(wxT("max='%s' "), Internat::ToString(mMax).c_str())); xmlFile.Write(wxString::Format(wxT("rms='%s'"), Internat::ToString(mRMS).c_str())); xmlFile.Write(wxT("/>\n"));}/// staticBlockFile *SimpleBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs){ wxFileName fileName; float min=0, max=0, rms=0; sampleCount len = 0; while(*attrs) { const wxChar *attr = *attrs++; const wxChar *value = *attrs++; if( !wxStrcmp(attr, wxT("filename")) ) dm.AssignFile(fileName,value,FALSE); if( !wxStrcmp(attr, wxT("len")) ) len = wxAtoi(value); if( !wxStrcmp(attr, wxT("min")) ) min = Internat::CompatibleToDouble(value); if( !wxStrcmp(attr, wxT("max")) ) max = Internat::CompatibleToDouble(value); if( !wxStrcmp(attr, wxT("rms")) ) rms = Internat::CompatibleToDouble(value); } return new SimpleBlockFile(fileName, len, min, max, rms);}/// Create a copy of this BlockFile, but using a different disk file.////// @param newFileName The name of the new file to use.BlockFile *SimpleBlockFile::Copy(wxFileName newFileName){ BlockFile *newBlockFile = new SimpleBlockFile(newFileName, mLen, mMin, mMax, mRMS); return newBlockFile;}int SimpleBlockFile::GetSpaceUsage(){ wxFFile dataFile(mFileName.GetFullPath()); return dataFile.Length();}void SimpleBlockFile::Recover(){ wxFFile file(FILENAME(mFileName.GetFullPath()).c_str(), wxT("wb")); int i; if( !file.IsOpened() ){ // Can't do anything else. return; } auHeader header; header.magic = 0x2e736e64; header.dataOffset = sizeof(auHeader) + mSummaryInfo.totalSummaryBytes; // dataSize is optional, and we opt out header.dataSize = 0xffffffff; header.encoding = AU_SAMPLE_FORMAT_16; header.sampleRate = 44100; header.channels = 1; file.Write(&header, sizeof(header)); for(i=0;i<mSummaryInfo.totalSummaryBytes;i++) file.Write(wxT("\0"),1); for(i=0;i<mLen*2;i++) file.Write(wxT("\0"),1);}// Indentation settings for Vim and Emacs and unique identifier for Arch, a// version control system. Please do not modify past this point.//// Local Variables:// c-basic-offset: 3// indent-tabs-mode: nil// End://// vim: et sts=3 sw=3// arch-tag: 606f2b69-1fda-40a3-88e6-06ff91d708c2
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -