diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 4b1d0e785..ea90e8cf9 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -3384,7 +3384,7 @@ void AudioIO::FillBuffers() { // Append captured samples to the end of the WaveTracks. // The WaveTracks have their own buffering for efficiency. - XMLStringWriter blockFileLog; + AutoSaveFile blockFileLog; int numChannels = mCaptureTracks.GetCount(); for( i = 0; (int)i < numChannels; i++ ) @@ -3392,7 +3392,7 @@ void AudioIO::FillBuffers() int avail = commonlyAvail; sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat(); - XMLStringWriter appendLog; + AutoSaveFile appendLog; if( mFactor == 1.0 ) { @@ -3423,6 +3423,7 @@ void AudioIO::FillBuffers() if (!appendLog.IsEmpty()) { blockFileLog.StartTag(wxT("recordingrecovery")); + blockFileLog.WriteAttr(wxT("id"), mCaptureTracks[i]->GetAutoSaveIdent()); blockFileLog.WriteAttr(wxT("channel"), (int)i); blockFileLog.WriteAttr(wxT("numchannels"), numChannels); blockFileLog.WriteSubTree(appendLog); diff --git a/src/AudioIOListener.h b/src/AudioIOListener.h index 14998ba52..db15ac4e4 100644 --- a/src/AudioIOListener.h +++ b/src/AudioIOListener.h @@ -15,6 +15,8 @@ #include +#include "AutoRecovery.h" + class AUDACITY_DLL_API AudioIOListener { public: AudioIOListener() {} @@ -23,7 +25,7 @@ public: virtual void OnAudioIORate(int rate) = 0; virtual void OnAudioIOStartRecording() = 0; virtual void OnAudioIOStopRecording() = 0; - virtual void OnAudioIONewBlockFiles(const wxString& blockFileLog) = 0; + virtual void OnAudioIONewBlockFiles(const AutoSaveFile & blockFileLog) = 0; }; #endif diff --git a/src/AutoRecovery.cpp b/src/AutoRecovery.cpp index 8d1bfa870..2315b78dc 100644 --- a/src/AutoRecovery.cpp +++ b/src/AutoRecovery.cpp @@ -274,13 +274,29 @@ bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag, // We need to find the track and sequence where the blockfile belongs WaveTrackArray tracks = mProject->GetTracks()->GetWaveTrackArray(false); - int index = tracks.GetCount() - mNumChannels + mChannel; - if (index < 0 || index >= (int)tracks.GetCount()) + size_t index; + if (mAutoSaveIdent) + { + for (index = 0; index < tracks.GetCount(); index++) + { + if (tracks[index]->GetAutoSaveIdent() == mAutoSaveIdent) + { + break; + } + } + } + else + { + index = tracks.GetCount() - mNumChannels + mChannel; + } + + if (index < 0 || index >= tracks.GetCount()) { // This should only happen if there is a bug wxASSERT(false); return false; } + WaveTrack* track = tracks.Item(index); WaveClip* clip = track->NewestOrNewClip(); Sequence* seq = clip->GetSequence(); @@ -302,6 +318,8 @@ bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag, } else if (wxStrcmp(tag, wxT("recordingrecovery")) == 0) { + mAutoSaveIdent = 0; + // loop through attrs, which is a null-terminated list of // attribute-value pairs long nValue; @@ -331,6 +349,13 @@ bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag, return false; mNumChannels = nValue; } + else if (wxStrcmp(attr, wxT("id")) == 0) + { + if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&nValue) || + (nValue < 1)) + return false; + mAutoSaveIdent = nValue; + } } } @@ -345,3 +370,511 @@ XMLTagHandler* RecordingRecoveryHandler::HandleXMLChild(const wxChar *tag) return NULL; } + +/// +/// AutoSaveFile class +/// + +// Simple "binary xml" format used exclusively for autosave files. +// +// It is not intended to transport these files across platform architectures, +// so endianness is not a concern. +// +// It is not intended that the user view or modify the file. +// +// It IS intended that as little work be done during auto save, so numbers +// and strings are written in their native format. They will be converted +// during recovery. +// +// The file has 3 main sections: +// +// ident literal "" +// name dictionary dictionary of all names used in the document +// data fields the "encoded" XML document +// +// If a subtree is added, it will be preceeded with FT_Push tell the decoder +// to preserve the active dictionary. The decoder when then restore the +// dictionary when an FT_Pop is encountered. Nesting is unlimited. +// +// To save space, each name (attribute or element) encountered is stored in +// the name dictionary and replaced with the assigned 2-byte identifier. +// +// All strings are in native unicode format, 2-byte or 4-byte. +// +// All "lengths" are 2-byte signed, so are limited to 32767 bytes long/ + +enum FieldTypes +{ + FT_StartTag, // type, ID, name + FT_EndTag, // type, ID, name + FT_String, // type, ID, name, string length, string + FT_Int, // type, ID, value + FT_Bool, // type, ID, value + FT_Long, // type, ID, value + FT_LongLong, // type, ID, value + FT_SizeT, // type, ID, value + FT_Float, // type, ID, value + FT_Double, // type, ID, value + FT_Data, // type, string length, string + FT_Raw, // type, string length, string + FT_Push, // type only + FT_Pop, // type only + FT_Name // type, name length, name +}; + +#include +WX_DEFINE_OBJARRAY(IdMapArray); + +AutoSaveFile::AutoSaveFile(size_t allocSize) +{ + mAllocSize = allocSize; +} + +AutoSaveFile::~AutoSaveFile() +{ +} + +void AutoSaveFile::StartTag(const wxString & name) +{ + mBuffer.PutC(FT_StartTag); + WriteName(name); +} + +void AutoSaveFile::EndTag(const wxString & name) +{ + mBuffer.PutC(FT_EndTag); + WriteName(name); +} + +void AutoSaveFile::WriteAttr(const wxString & name, const wxChar *value) +{ + WriteAttr(name, wxString(value)); +} + +void AutoSaveFile::WriteAttr(const wxString & name, const wxString & value) +{ + mBuffer.PutC(FT_String); + WriteName(name); + + short len = value.Length() * sizeof(wxChar); + + mBuffer.Write(&len, sizeof(len)); + mBuffer.Write(value.c_str(), len); +} + +void AutoSaveFile::WriteAttr(const wxString & name, int value) +{ + mBuffer.PutC(FT_Int); + WriteName(name); + + mBuffer.Write(&value, sizeof(value)); +} + +void AutoSaveFile::WriteAttr(const wxString & name, bool value) +{ + mBuffer.PutC(FT_Bool); + WriteName(name); + + mBuffer.Write(&value, sizeof(value)); +} + +void AutoSaveFile::WriteAttr(const wxString & name, long value) +{ + mBuffer.PutC(FT_Long); + WriteName(name); + + mBuffer.Write(&value, sizeof(value)); +} + +void AutoSaveFile::WriteAttr(const wxString & name, long long value) +{ + mBuffer.PutC(FT_LongLong); + WriteName(name); + + mBuffer.Write(&value, sizeof(value)); +} + +void AutoSaveFile::WriteAttr(const wxString & name, size_t value) +{ + mBuffer.PutC(FT_SizeT); + WriteName(name); + + mBuffer.Write(&value, sizeof(value)); +} + +void AutoSaveFile::WriteAttr(const wxString & name, float value, int digits) +{ + mBuffer.PutC(FT_Float); + WriteName(name); + + mBuffer.Write(&value, sizeof(value)); + mBuffer.Write(&digits, sizeof(digits)); +} + +void AutoSaveFile::WriteAttr(const wxString & name, double value, int digits) +{ + mBuffer.PutC(FT_Double); + WriteName(name); + + mBuffer.Write(&value, sizeof(value)); + mBuffer.Write(&digits, sizeof(digits)); +} + +void AutoSaveFile::WriteData(const wxString & value) +{ + mBuffer.PutC(FT_Data); + + short len = value.Length() * sizeof(wxChar); + + mBuffer.Write(&len, sizeof(len)); + mBuffer.Write(value.c_str(), len); +} + +void AutoSaveFile::Write(const wxString & value) +{ + mBuffer.PutC(FT_Raw); + + short len = value.Length() * sizeof(wxChar); + + mBuffer.Write(&len, sizeof(len)); + mBuffer.Write(value.c_str(), len); +} + +void AutoSaveFile::WriteSubTree(const AutoSaveFile & value) +{ + mBuffer.PutC(FT_Push); + + wxStreamBuffer *buf = value.mDict.GetOutputStreamBuffer(); + mBuffer.Write(buf->GetBufferStart(), buf->GetIntPosition()); + + buf = value.mBuffer.GetOutputStreamBuffer(); + mBuffer.Write(buf->GetBufferStart(), buf->GetIntPosition()); + + mBuffer.PutC(FT_Pop); +} + +bool AutoSaveFile::Write(wxFFile & file) const +{ + bool success = file.Write(AutoSaveIdent, strlen(AutoSaveIdent)) == strlen(AutoSaveIdent); + if (success) + { + success = Append(file); + } + + return success; +} + +bool AutoSaveFile::Append(wxFFile & file) const +{ + wxStreamBuffer *buf = mDict.GetOutputStreamBuffer(); + + bool success = file.Write(buf->GetBufferStart(), buf->GetIntPosition()) == buf->GetIntPosition(); + if (success) + { + buf = mBuffer.GetOutputStreamBuffer(); + success = file.Write(buf->GetBufferStart(), buf->GetIntPosition()) == buf->GetIntPosition(); + } + + return success; +} + +void AutoSaveFile::CheckSpace(wxMemoryOutputStream & os) +{ + wxStreamBuffer *buf = os.GetOutputStreamBuffer(); + size_t left = buf->GetBytesLeft(); + if (left == 0) + { + size_t origPos = buf->GetIntPosition(); + char *temp = new char[mAllocSize]; + buf->Write(temp, mAllocSize); + delete temp; + buf->SetIntPosition(origPos); + } +} + +void AutoSaveFile::WriteName(const wxString & name) +{ + short len = name.Length() * sizeof(wxChar); + short id; + + if (mNames.count(name)) + { + id = mNames[name]; + } + else + { + id = mNames.size(); + mNames[name] = id; + + CheckSpace(mDict); + mDict.PutC(FT_Name); + mDict.Write(&id, sizeof(id)); + mDict.Write(&len, sizeof(len)); + mDict.Write(name.c_str(), len); + } + + CheckSpace(mBuffer); + mBuffer.Write(&id, sizeof(id)); +} + +bool AutoSaveFile::IsEmpty() const +{ + return mBuffer.GetLength() == 0; +} + +bool AutoSaveFile::Decode(const wxString & fileName) +{ + char ident[sizeof(AutoSaveIdent)]; + size_t len = strlen(AutoSaveIdent); + + wxFileName fn(fileName); + wxFFile file; + + if (!file.Open(fn.GetFullPath(), wxT("rb"))) + { + return false; + } + + if (file.Read(&ident, len) != len || strncmp(ident, AutoSaveIdent, len) != 0) + { + // Not something we recognize. Could be decoded already. Let the caller + // deal with it. + file.Close(); + + return true; + } + + len = file.Length() - len; + char *buf = new char[len]; + + if (file.Read(buf, len) != len) + { + delete buf; + file.Close(); + return false; + } + + wxMemoryInputStream in(buf, len); + + file.Close(); + + // Decode to a temporary file to preserve the orignal. + wxString tempName = fn.CreateTempFileName(fn.GetPath(true)); + + XMLFileWriter out; + + out.Open(tempName, wxT("wb")); + if (!out.IsOpened()) + { + delete buf; + + wxRemoveFile(tempName); + + return false; + } + + mIds.clear(); + + while (!in.Eof() && !out.Error()) + { + short id; + + switch (in.GetC()) + { + case FT_Push: + { + mIdStack.Add(mIds); + mIds.clear(); + } + break; + + case FT_Pop: + { + mIds = mIdStack[mIdStack.GetCount() - 1]; + mIdStack.RemoveAt(mIdStack.GetCount() - 1); + } + break; + + case FT_Name: + { + short len; + + in.Read(&id, sizeof(id)); + in.Read(&len, sizeof(len)); + wxChar *name = new wxChar[len / sizeof(wxChar)]; + in.Read(name, len); + + mIds[id] = wxString(name, len / sizeof(wxChar)); + delete name; + } + break; + + case FT_StartTag: + { + in.Read(&id, sizeof(id)); + + out.StartTag(mIds[id]); + } + break; + + case FT_EndTag: + { + in.Read(&id, sizeof(id)); + + out.EndTag(mIds[id]); + } + break; + + case FT_String: + { + short len; + + in.Read(&id, sizeof(id)); + in.Read(&len, sizeof(len)); + wxChar *val = new wxChar[len / sizeof(wxChar)]; + in.Read(val, len); + + out.WriteAttr(mIds[id], wxString(val, len / sizeof(wxChar))); + delete val; + } + break; + + case FT_Float: + { + float val; + int dig; + + in.Read(&id, sizeof(id)); + in.Read(&val, sizeof(val)); + in.Read(&dig, sizeof(dig)); + + out.WriteAttr(mIds[id], val, dig); + } + break; + + case FT_Double: + { + double val; + int dig; + + in.Read(&id, sizeof(id)); + in.Read(&val, sizeof(val)); + in.Read(&dig, sizeof(dig)); + + out.WriteAttr(mIds[id], val, dig); + } + break; + + case FT_Int: + { + int val; + + in.Read(&id, sizeof(id)); + in.Read(&val, sizeof(val)); + + out.WriteAttr(mIds[id], val); + } + break; + + case FT_Bool: + { + bool val; + + in.Read(&id, sizeof(id)); + in.Read(&val, sizeof(val)); + + out.WriteAttr(mIds[id], val); + } + break; + + case FT_Long: + { + long val; + + in.Read(&id, sizeof(id)); + in.Read(&val, sizeof(val)); + + out.WriteAttr(mIds[id], val); + } + break; + + case FT_LongLong: + { + long long val; + + in.Read(&id, sizeof(id)); + in.Read(&val, sizeof(val)); + + out.WriteAttr(mIds[id], val); + } + break; + + case FT_SizeT: + { + size_t val; + + in.Read(&id, sizeof(id)); + in.Read(&val, sizeof(val)); + + out.WriteAttr(mIds[id], val); + } + break; + + case FT_Data: + { + short len; + + in.Read(&len, sizeof(len)); + wxChar *val = new wxChar[len / sizeof(wxChar)]; + in.Read(val, len); + + out.WriteData(wxString(val, len / sizeof(wxChar))); + delete val; + } + break; + + case FT_Raw: + { + short len; + + in.Read(&len, sizeof(len)); + wxChar *val = new wxChar[len / sizeof(wxChar)]; + in.Read(val, len); + + out.Write(wxString(val, len / sizeof(wxChar))); + delete val; + } + break; + + default: + wxASSERT(true); + break; + } + } + + delete buf; + + bool error = out.Error(); + + out.Close(); + + // Bail if decoding failed. + if (error) + { + // File successfully decoded + wxRemoveFile(tempName); + + return false; + } + + // Decoding was successful, so remove the original file and replace with decoded one. + if (wxRemoveFile(fileName)) + { + if (!wxRenameFile(tempName, fileName)) + { + return false; + } + } + + return true; +} diff --git a/src/AutoRecovery.h b/src/AutoRecovery.h index ffed7cfac..0833ca0ef 100644 --- a/src/AutoRecovery.h +++ b/src/AutoRecovery.h @@ -12,9 +12,15 @@ #define __AUDACITY_AUTORECOVERY__ #include "Project.h" + #include "xml/XMLTagHandler.h" +#include "xml/XMLWriter.h" #include +#include +#include +#include +#include // // Show auto recovery dialog if there are projects to recover. Should be @@ -49,6 +55,65 @@ private: AudacityProject* mProject; int mChannel; int mNumChannels; + int mAutoSaveIdent; }; +/// +/// AutoSaveFile +/// + +// Should be plain ASCII +#define AutoSaveIdent "" + +WX_DECLARE_STRING_HASH_MAP_WITH_DECL(short, NameMap, class AUDACITY_DLL_API); +WX_DECLARE_HASH_MAP(short, wxString, wxIntegerHash, wxIntegerEqual, IdMap); +WX_DECLARE_OBJARRAY(IdMap, IdMapArray); + +class AUDACITY_DLL_API AutoSaveFile : public XMLWriter +{ +public: + + AutoSaveFile(size_t allocSize = 1024 * 1024); + virtual ~AutoSaveFile(); + + virtual void StartTag(const wxString & name); + virtual void EndTag(const wxString & name); + + virtual void WriteAttr(const wxString & name, const wxString &value); + virtual void WriteAttr(const wxString & name, const wxChar *value); + + virtual void WriteAttr(const wxString & name, int value); + virtual void WriteAttr(const wxString & name, bool value); + virtual void WriteAttr(const wxString & name, long value); + virtual void WriteAttr(const wxString & name, long long value); + virtual void WriteAttr(const wxString & name, size_t value); + virtual void WriteAttr(const wxString & name, float value, int digits = -1); + virtual void WriteAttr(const wxString & name, double value, int digits = -1); + + virtual void WriteData(const wxString & value); + + virtual void WriteSubTree(const AutoSaveFile & value); + + virtual void Write(const wxString & data); + virtual bool Write(wxFFile & file) const; + virtual bool Append(wxFFile & file) const; + + virtual bool IsEmpty() const; + + virtual bool Decode(const wxString & fileName); + +private: + void WriteName(const wxString & name); + void CheckSpace(wxMemoryOutputStream & buf); + +private: + wxMemoryOutputStream mBuffer; + wxMemoryOutputStream mDict; + NameMap mNames; + IdMap mIds; + IdMapArray mIdStack; + size_t mAllocSize; +}; + + #endif diff --git a/src/Project.cpp b/src/Project.cpp index c33da5c3c..bceca619d 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -2478,13 +2478,6 @@ void AudacityProject::OpenFile(wxString fileName, bool addtohistory) return; } - // We want to open projects using wxTextFile, but if it's NOT a project - // file (but actually a WAV file, for example), then wxTextFile will spin - // for a long time searching for line breaks. So, we look for our - // signature at the beginning of the file first: - - wxString firstLine = wxT("AudacityProject"); - if (!::wxFileExists(fileName)) { wxMessageBox(_("Could not open file: ") + fileName, _("Error Opening File"), @@ -2492,6 +2485,11 @@ void AudacityProject::OpenFile(wxString fileName, bool addtohistory) return; } + // We want to open projects using wxTextFile, but if it's NOT a project + // file (but actually a WAV file, for example), then wxTextFile will spin + // for a long time searching for line breaks. So, we look for our + // signature at the beginning of the file first: + wxFFile *ff = new wxFFile(fileName, wxT("rb")); if (!ff->IsOpened()) { wxMessageBox(_("Could not open file: ") + fileName, @@ -2504,9 +2502,9 @@ void AudacityProject::OpenFile(wxString fileName, bool addtohistory) wxMessageBox(wxString::Format(_("File may be invalid or corrupted: \n%s"), (const wxChar*)fileName), _("Error Opening File or Project"), wxOK | wxCENTRE, this); - ff->Close(); - delete ff; - return; + ff->Close(); + delete ff; + return; } buf[15] = 0; ff->Close(); @@ -2558,31 +2556,13 @@ void AudacityProject::OpenFile(wxString fileName, bool addtohistory) if (mFileName.Length() >= autoSaveExt.Length() && mFileName.Right(autoSaveExt.Length()) == autoSaveExt) { - // This is an auto-save file, add tag, if necessary - wxFile f(fileName, wxFile::read_write); - if (f.IsOpened()) + AutoSaveFile asf; + if (!asf.Decode(fileName)) { - // Read the last 16 bytes of the file and check if they contain - // "" somewhere. - const int bufsize = 16; - char buf[bufsize]; - bool seekOk, readOk; - seekOk = f.SeekEnd(-bufsize) != wxInvalidOffset; - if (seekOk) - readOk = (f.Read(buf, bufsize) == bufsize); - else - readOk = false; - if (readOk && !strstr(buf, "")) - { - // End of file does not contain closing tag, so add it - if (f.Seek(0, wxFromEnd) != wxInvalidOffset) - { - strcpy(buf, "\n"); - f.Write(buf, strlen(buf)); - } - } - - f.Close(); + wxMessageBox(_("Could not decode file: ") + fileName, + _("Error decoding file"), + wxOK | wxCENTRE, this); + return; } } @@ -3187,8 +3167,18 @@ void AudacityProject::WriteXML(XMLWriter &xmlFile) if (t->GetLinked()) t = iter.Next(); } - else + else if (t->GetKind() == Track::Wave && mAutoSaving) + { + pWaveTrack = (WaveTrack*)t; + pWaveTrack->SetAutoSaveIdent(++ndx); t->WriteXML(xmlFile); + } + else + { + pWaveTrack = (WaveTrack*)t; + pWaveTrack->SetAutoSaveIdent(0); + t->WriteXML(xmlFile); + } t = iter.Next(); } @@ -4653,20 +4643,16 @@ void AudacityProject::AutoSave() try { - XMLStringWriter buffer(1024 * 1024); VarSetter setter(&mAutoSaving, true, false); + + AutoSaveFile buffer; WriteXMLHeader(buffer); WriteXML(buffer); - XMLFileWriter saveFile; + wxFFile saveFile; saveFile.Open(fn + wxT(".tmp"), wxT("wb")); - saveFile.WriteSubTree(buffer); - - // JKC Calling XMLFileWriter::Close will close the scope. - // We certainly don't want to do that, if we're doing recordingrecovery, - // because the recordingrecovery tags need to be inside . - // So instead we do not call Close() but CloseWithoutEndingTags(). - saveFile.CloseWithoutEndingTags(); + buffer.Write(saveFile); + saveFile.Close(); } catch (XMLFileWriterException* pException) { @@ -4748,13 +4734,7 @@ void AudacityProject::OnAudioIOStartRecording() { // Before recording is started, auto-save the file. The file will have // empty tracks at the bottom where the recording will be put into - // - // When block files are cached, auto recovery is disabled while recording, - // since no block files are written during recording that could be - // recovered. - // - if (!GetCacheBlockFiles()) - AutoSave(); + AutoSave(); } // This is called after recording has stopped and all tracks have flushed. @@ -4785,30 +4765,19 @@ void AudacityProject::OnAudioIOStopRecording() AutoSave(); } -void AudacityProject::OnAudioIONewBlockFiles(const wxString& blockFileLog) +void AudacityProject::OnAudioIONewBlockFiles(const AutoSaveFile & blockFileLog) { // New blockfiles have been created, so add them to the auto-save file - if (!GetCacheBlockFiles() && - !mAutoSaveFileName.IsEmpty()) + if (!mAutoSaveFileName.IsEmpty()) { - wxFFile f(mAutoSaveFileName, wxT("at")); + wxFFile f(mAutoSaveFileName, wxT("ab")); if (!f.IsOpened()) return; // Keep recording going, there's not much we can do here - f.Write(blockFileLog); + blockFileLog.Append(f); f.Close(); } } -bool AudacityProject::GetCacheBlockFiles() -{ - bool cacheBlockFiles = false; -#ifdef DEPRECATED_AUDIO_CACHE - // See http://bugzilla.audacityteam.org/show_bug.cgi?id=545. - gPrefs->Read(wxT("/Directories/CacheBlockFiles"), &cacheBlockFiles); -#endif - return cacheBlockFiles; -} - void AudacityProject::SetSnapTo(int snap) { AS_SetSnapTo(snap); diff --git a/src/Project.h b/src/Project.h index b8166838d..88e681051 100644 --- a/src/Project.h +++ b/src/Project.h @@ -436,7 +436,7 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, virtual void OnAudioIORate(int rate); virtual void OnAudioIOStartRecording(); virtual void OnAudioIOStopRecording(); - virtual void OnAudioIONewBlockFiles(const wxString& blockFileLog); + virtual void OnAudioIONewBlockFiles(const AutoSaveFile & blockFileLog); // Command Handling bool TryToMakeActionAllowed( wxUint32 & flags, wxUint32 flagsRqd, wxUint32 mask ); @@ -464,8 +464,6 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, void AutoSave(); void DeleteCurrentAutoSaveFile(); - static bool GetCacheBlockFiles(); - public: bool IsSoloSimple() { return mSoloPref == wxT("Simple"); } bool IsSoloNone() { return mSoloPref == wxT("None"); } diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index 07324e147..436485b30 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -1482,6 +1482,9 @@ bool WaveTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs) else if (!wxStrcmp(attr, wxT("linked")) && XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) SetLinked(nValue != 0); + else if (!wxStrcmp(attr, wxT("autosaveid")) && + XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) + mAutoSaveIdent = (int) nValue; } // while #ifdef EXPERIMENTAL_OUTPUT_DISPLAY @@ -1539,6 +1542,10 @@ XMLTagHandler *WaveTrack::HandleXMLChild(const wxChar *tag) void WaveTrack::WriteXML(XMLWriter &xmlFile) { xmlFile.StartTag(wxT("wavetrack")); + if (mAutoSaveIdent) + { + xmlFile.WriteAttr(wxT("autosaveid"), mAutoSaveIdent); + } xmlFile.WriteAttr(wxT("name"), mName); xmlFile.WriteAttr(wxT("channel"), mChannel); xmlFile.WriteAttr(wxT("linked"), mLinked); @@ -2376,3 +2383,13 @@ void WaveTrack::AddInvalidRegion(sampleCount startSample, sampleCount endSample) for (WaveClipList::compatibility_iterator it=GetClipIterator(); it; it=it->GetNext()) it->GetData()->AddInvalidRegion(startSample,endSample); } + +int WaveTrack::GetAutoSaveIdent() +{ + return mAutoSaveIdent; +} + +void WaveTrack::SetAutoSaveIdent(int ident) +{ + mAutoSaveIdent = ident; +} diff --git a/src/WaveTrack.h b/src/WaveTrack.h index ed27006b7..b256a9277 100644 --- a/src/WaveTrack.h +++ b/src/WaveTrack.h @@ -390,6 +390,14 @@ class AUDACITY_DLL_API WaveTrack: public Track { // Resample track (i.e. all clips in the track) bool Resample(int rate, ProgressDialog *progress = NULL); + // + // AutoSave related + // + // Retrieve the unique autosave ID + int GetAutoSaveIdent(); + // Set the unique autosave ID + void SetAutoSaveIdent(int id); + // // The following code will eventually become part of a GUIWaveTrack // and will be taken out of the WaveTrack class: @@ -454,7 +462,7 @@ class AUDACITY_DLL_API WaveTrack: public Track { wxCriticalSection mFlushCriticalSection; wxCriticalSection mAppendCriticalSection; double mLegacyProjectFileOffset; - + int mAutoSaveIdent; }; #endif // __AUDACITY_WAVETRACK__ diff --git a/src/xml/XMLWriter.cpp b/src/xml/XMLWriter.cpp index d3bcc5a40..bc8940c2a 100644 --- a/src/xml/XMLWriter.cpp +++ b/src/xml/XMLWriter.cpp @@ -33,7 +33,6 @@ the general functionality for creating XML in UTF8 encoding. #include "../Internat.h" #include "XMLWriter.h" -#include "XMLTagHandler.h" //table for xml encoding compatibility with expat decoding //see wxWidgets-2.8.12/src/expat/lib/xmltok_impl.h diff --git a/src/xml/XMLWriter.h b/src/xml/XMLWriter.h index e28aa26e9..6c1282a9f 100644 --- a/src/xml/XMLWriter.h +++ b/src/xml/XMLWriter.h @@ -10,8 +10,9 @@ #ifndef __AUDACITY_XML_XML_FILE_WRITER__ #define __AUDACITY_XML_XML_FILE_WRITER__ -#include +#include #include +#include /// /// XMLWriter @@ -23,23 +24,23 @@ class AUDACITY_DLL_API XMLWriter { XMLWriter(); virtual ~XMLWriter(); - void StartTag(const wxString &name); - void EndTag(const wxString &name); + virtual void StartTag(const wxString &name); + virtual void EndTag(const wxString &name); - void WriteAttr(const wxString &name, const wxString &value); - void WriteAttr(const wxString &name, const wxChar *value); + virtual void WriteAttr(const wxString &name, const wxString &value); + virtual void WriteAttr(const wxString &name, const wxChar *value); - void WriteAttr(const wxString &name, int value); - void WriteAttr(const wxString &name, bool value); - void WriteAttr(const wxString &name, long value); - void WriteAttr(const wxString &name, long long value); - void WriteAttr(const wxString &name, size_t value); - void WriteAttr(const wxString &name, float value, int digits = -1); - void WriteAttr(const wxString &name, double value, int digits = -1); + virtual void WriteAttr(const wxString &name, int value); + virtual void WriteAttr(const wxString &name, bool value); + virtual void WriteAttr(const wxString &name, long value); + virtual void WriteAttr(const wxString &name, long long value); + virtual void WriteAttr(const wxString &name, size_t value); + virtual void WriteAttr(const wxString &name, float value, int digits = -1); + virtual void WriteAttr(const wxString &name, double value, int digits = -1); - void WriteData(const wxString &value); + virtual void WriteData(const wxString &value); - void WriteSubTree(const wxString &value); + virtual void WriteSubTree(const wxString &value); virtual void Write(const wxString &data) = 0;