mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-02 08:39:46 +02:00
Merge branch 'master' into scrubbing
This commit is contained in:
commit
4323ad60f6
@ -49,6 +49,7 @@
|
||||
#include "AudacityApp.h"
|
||||
#include "AudioIO.h"
|
||||
#include "BlockFile.h"
|
||||
#include "Diags.h"
|
||||
#include "DirManager.h"
|
||||
#include "Envelope.h"
|
||||
#include "FFT.h"
|
||||
|
@ -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);
|
||||
|
@ -15,6 +15,8 @@
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#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
|
||||
|
@ -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 "<?xml autosave>"
|
||||
// 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/arrimpl.cpp>
|
||||
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;
|
||||
}
|
||||
|
@ -12,9 +12,15 @@
|
||||
#define __AUDACITY_AUTORECOVERY__
|
||||
|
||||
#include "Project.h"
|
||||
|
||||
#include "xml/XMLTagHandler.h"
|
||||
#include "xml/XMLWriter.h"
|
||||
|
||||
#include <wx/debug.h>
|
||||
#include <wx/dynarray.h>
|
||||
#include <wx/ffile.h>
|
||||
#include <wx/hashmap.h>
|
||||
#include <wx/mstream.h>
|
||||
|
||||
//
|
||||
// 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 "<?xml autosave>"
|
||||
|
||||
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
|
||||
|
98
src/Diags.cpp
Normal file
98
src/Diags.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
Diags.cpp
|
||||
|
||||
James Crook
|
||||
|
||||
|
||||
********************************************************************//**
|
||||
|
||||
\class Diags
|
||||
\brief Processing of the macros for recording bad events or performance
|
||||
monitoring.
|
||||
|
||||
The idea of these macros is that we can include them in release
|
||||
code at no risk. They
|
||||
|
||||
a) have almost zero performance impact.
|
||||
b) will not flood the log with events.
|
||||
|
||||
This is achieved by a countdown which stops recording in the log
|
||||
when the countdown is finished. The countdwon continues to
|
||||
count down so that we track how many times the event happens.
|
||||
|
||||
|
||||
*//********************************************************************/
|
||||
|
||||
|
||||
#include "Audacity.h"
|
||||
#include <wx/defs.h>
|
||||
#include <wx/hash.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <wx/stopwatch.h>
|
||||
#include "Diags.h"
|
||||
#include "Experimental.h"
|
||||
|
||||
static wxStopWatch MasterWatch;
|
||||
static bool bStopWatchStarted = false;
|
||||
|
||||
void diagnostics_do_diag( t_diag_struct * pDiag ){
|
||||
wxLog * pLog = wxLog::SetActiveTarget(NULL);
|
||||
wxLogDebug( wxT("%s"), pDiag->pMessage );
|
||||
wxLog::SetActiveTarget(pLog);
|
||||
}
|
||||
void diagnostics_do_diag_mem( t_diag_struct * pDiag, long amount ){
|
||||
wxLog * pLog = wxLog::SetActiveTarget(NULL);
|
||||
wxLogDebug( wxT("%s %l"), pDiag->pMessage, amount );
|
||||
wxLog::SetActiveTarget(pLog);
|
||||
pDiag->total += amount;
|
||||
pDiag->most_recent = amount;
|
||||
if( pDiag->countdown == (pDiag->initial_count -1 )){
|
||||
pDiag->most = amount;
|
||||
pDiag->least = amount;
|
||||
}
|
||||
else if( amount > pDiag->most )
|
||||
pDiag->most = amount;
|
||||
else if( amount < pDiag->least )
|
||||
pDiag->least = amount;
|
||||
}
|
||||
|
||||
void diagnostics_do_perfmon_start( t_diag_struct * pDiag, t_diag_struct ** pRememberMe ){
|
||||
if( *pRememberMe == NULL ){
|
||||
*pRememberMe = pDiag;
|
||||
if( !bStopWatchStarted ){
|
||||
bStopWatchStarted = true;
|
||||
MasterWatch.Start();
|
||||
}
|
||||
}
|
||||
pDiag->most_recent = MasterWatch.Time();
|
||||
}
|
||||
|
||||
void diagnostics_do_perfmon_stop( t_diag_struct ** ppDiag ){
|
||||
t_diag_struct * pDiag = *ppDiag;
|
||||
*ppDiag = NULL;
|
||||
long amount = MasterWatch.Time() - pDiag->most_recent;
|
||||
pDiag->total += amount;
|
||||
pDiag->most_recent = amount;
|
||||
if( pDiag->countdown == (pDiag->initial_count -1 )){
|
||||
pDiag->most = amount;
|
||||
pDiag->least = amount;
|
||||
}
|
||||
else if( amount > pDiag->most )
|
||||
pDiag->most = amount;
|
||||
else if( amount < pDiag->least )
|
||||
pDiag->least = amount;
|
||||
wxLog * pLog = wxLog::SetActiveTarget(NULL);
|
||||
wxLogDebug( wxT("%s %f seconds"), pDiag->pMessage, ((float)amount)/1000.0f );
|
||||
wxLog::SetActiveTarget(pLog);
|
||||
}
|
||||
|
||||
|
||||
void diag_sample_test(){
|
||||
DIAG("Flip counter");// Flip counter will show in log ten times, then just count.
|
||||
}
|
83
src/Diags.h
Normal file
83
src/Diags.h
Normal file
@ -0,0 +1,83 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
Diags.h
|
||||
|
||||
James Crook
|
||||
|
||||
Provides Macros for recording bad events and performance monitoring.
|
||||
These macros have such low cost that they can be used in release code.
|
||||
They will take miniscule processing time after the first ten times.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef __AUDACITY_DIAGS__
|
||||
#define __AUDACITY_DIAGS__
|
||||
|
||||
typedef long t_diag_timer;
|
||||
|
||||
struct t_diag_struct {
|
||||
long countdown;
|
||||
long initial_count;
|
||||
long total;
|
||||
long most_recent;
|
||||
long least;
|
||||
long most;
|
||||
const wchar_t * pMessage;
|
||||
};
|
||||
|
||||
|
||||
extern void diagnostics_do_diag( t_diag_struct * pDiag );
|
||||
extern void diagnostics_do_diag_mem( t_diag_struct * pDiag, long amount );
|
||||
extern void diagnostics_do_perfmon_start( t_diag_struct * pDiag, t_diag_struct ** ppRememberMe );
|
||||
extern void diagnostics_do_perfmon_stop( t_diag_struct ** ppDiag);
|
||||
|
||||
// A constant that sets the maximum number of times we log the message.
|
||||
#define DEFAULT_LOG_COUNT (10)
|
||||
|
||||
// USAGE:
|
||||
// Each of these will do something the first ten times, then just count.
|
||||
// They can be reactivated by a GUI.
|
||||
//
|
||||
// Use DIAG for a simple message. Usually for something bad like an overrun.
|
||||
// Use TRACK_MEM to track hungry memory usage, RAM or disk.
|
||||
// Use TIMER_START and STOP to time an interval.
|
||||
// For the above two, you will need a MAKE_TIMER( timername ) first.
|
||||
|
||||
// The 'timername' created here is almost free.
|
||||
// It's a pointer that allows both START and STOP to use the same struct.
|
||||
#define MAKE_TIMER( timername ) \
|
||||
static t_diag_struct * timername = NULL;
|
||||
|
||||
// Note that in all three macros:
|
||||
// The {} ensure diag name is not visible outside
|
||||
// static ensures struct is initialised just once.
|
||||
// No function is called after the countdown is counted out.
|
||||
#define DIAG( message ) { \
|
||||
static t_diag_struct diag{ DEFAULT_LOG_COUNT, DEFAULT_LOG_COUNT, 0,0,0,0,wxT(message)};\
|
||||
if( --diag.countdown >=0 )\
|
||||
diagnostics_do_diag( &diag );\
|
||||
}
|
||||
|
||||
#define TRACK_MEM( message, amount ) { \
|
||||
static t_diag_struct diag{ DEFAULT_LOG_COUNT, DEFAULT_LOG_COUNT, 0,0,0,0,wxT(message)};\
|
||||
if( --diag.countdown >=0 )\
|
||||
diagnostics_do_diag_mem( &diag, amount );\
|
||||
}
|
||||
|
||||
#define TIMER_START( message, timername )\
|
||||
MAKE_TIMER( timername ); { \
|
||||
static t_diag_struct diag{ DEFAULT_LOG_COUNT, DEFAULT_LOG_COUNT, 0,0,0,0,wxT(message)};\
|
||||
if( --diag.countdown >=0 )\
|
||||
diagnostics_do_perfmon_start( &diag, &timername );\
|
||||
}
|
||||
|
||||
#define TIMER_STOP( timername ){ \
|
||||
if( timername != NULL )\
|
||||
diagnostics_do_perfmon_stop( &timername );\
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -122,6 +122,8 @@ audacity_SOURCES = \
|
||||
DeviceChange.h \
|
||||
DeviceManager.cpp \
|
||||
DeviceManager.h \
|
||||
Diags.cpp \
|
||||
Diags.h \
|
||||
Envelope.cpp \
|
||||
Envelope.h \
|
||||
Experimental.h \
|
||||
|
105
src/Project.cpp
105
src/Project.cpp
@ -98,6 +98,7 @@ scroll information. It also has some status flags.
|
||||
#include "AColor.h"
|
||||
#include "AudioIO.h"
|
||||
#include "Dependencies.h"
|
||||
#include "Diags.h"
|
||||
#include "HistoryWindow.h"
|
||||
#include "Lyrics.h"
|
||||
#include "LyricsWindow.h"
|
||||
@ -2477,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"),
|
||||
@ -2491,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,
|
||||
@ -2503,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();
|
||||
@ -2557,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 </project> 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
|
||||
// "</project>" 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, "</project>"))
|
||||
{
|
||||
// End of file does not contain closing </project> tag, so add it
|
||||
if (f.Seek(0, wxFromEnd) != wxInvalidOffset)
|
||||
{
|
||||
strcpy(buf, "</project>\n");
|
||||
f.Write(buf, strlen(buf));
|
||||
}
|
||||
}
|
||||
|
||||
f.Close();
|
||||
wxMessageBox(_("Could not decode file: ") + fileName,
|
||||
_("Error decoding file"),
|
||||
wxOK | wxCENTRE, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3106,6 +3087,7 @@ void AudacityProject::WriteXMLHeader(XMLWriter &xmlFile)
|
||||
|
||||
void AudacityProject::WriteXML(XMLWriter &xmlFile)
|
||||
{
|
||||
TIMER_START( "AudacityProject::WriteXML", xml_writer_timer );
|
||||
// Warning: This block of code is duplicated in Save, for now...
|
||||
wxString project = mFileName;
|
||||
if (project.Len() > 4 && project.Mid(project.Len() - 4) == wxT(".aup"))
|
||||
@ -3185,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();
|
||||
}
|
||||
@ -3198,6 +3190,8 @@ void AudacityProject::WriteXML(XMLWriter &xmlFile)
|
||||
// recording log data to the end of the file later
|
||||
xmlFile.EndTag(wxT("project"));
|
||||
}
|
||||
TIMER_STOP( xml_writer_timer );
|
||||
|
||||
}
|
||||
|
||||
// Lock all blocks in all tracks of the last saved version
|
||||
@ -4649,20 +4643,16 @@ void AudacityProject::AutoSave()
|
||||
|
||||
try
|
||||
{
|
||||
XMLStringWriter buffer(1024 * 1024);
|
||||
VarSetter<bool> 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 <project> scope.
|
||||
// We certainly don't want to do that, if we're doing recordingrecovery,
|
||||
// because the recordingrecovery tags need to be inside <project></project>.
|
||||
// So instead we do not call Close() but CloseWithoutEndingTags().
|
||||
saveFile.CloseWithoutEndingTags();
|
||||
buffer.Write(saveFile);
|
||||
saveFile.Close();
|
||||
}
|
||||
catch (XMLFileWriterException* pException)
|
||||
{
|
||||
@ -4744,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.
|
||||
@ -4781,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);
|
||||
|
@ -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"); }
|
||||
|
@ -30,6 +30,7 @@ UndoManager
|
||||
#include "Track.h"
|
||||
#include "WaveTrack.h" // temp
|
||||
#include "NoteTrack.h" // for Sonify* function declarations
|
||||
#include "Diags.h"
|
||||
|
||||
#include "UndoManager.h"
|
||||
|
||||
@ -50,6 +51,7 @@ UndoManager::~UndoManager()
|
||||
|
||||
void UndoManager::CalculateSpaceUsage()
|
||||
{
|
||||
TIMER_START( "CalculateSpaceUsage", space_calc );
|
||||
TrackListOfKindIterator iter(Track::Wave);
|
||||
|
||||
space.Clear();
|
||||
@ -102,6 +104,7 @@ void UndoManager::CalculateSpaceUsage()
|
||||
|
||||
delete cur;
|
||||
delete prev;
|
||||
TIMER_STOP( space_calc );
|
||||
}
|
||||
|
||||
void UndoManager::GetLongDescription(unsigned int n, wxString *desc,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__
|
||||
|
@ -29,7 +29,7 @@
|
||||
// Name Type Key Def Min Max Scale
|
||||
Param( Sequence, wxString, wxTRANSLATE("Sequence"), wxT("audacity"), wxT(""), wxT(""), wxT(""));
|
||||
Param( DutyCycle, double, wxTRANSLATE("Duty Cycle"), 55.0, 0.0, 100.0, 10.0 );
|
||||
Param( Amplitude, double, wxTRANSLATE("Amplitude"), 0.0, 0.0, 1.0, 1 );
|
||||
Param( Amplitude, double, wxTRANSLATE("Amplitude"), 0.8, 0.0, 1.0, 1 );
|
||||
|
||||
static const double kFadeInOut = 250.0; // used for fadein/out needed to remove clicking noise
|
||||
|
||||
|
@ -819,6 +819,7 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
||||
|
||||
// If SHIFT key was down, the user wants append to tracks
|
||||
int recordingChannels = 0;
|
||||
TrackList *tracksCopy = NULL;
|
||||
bool shifted = mRecord->WasShiftDown();
|
||||
if (shifted) {
|
||||
bool sel = false;
|
||||
@ -858,6 +859,16 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
||||
playbackTracks.Remove(wt);
|
||||
t1 = wt->GetEndTime();
|
||||
if (t1 < t0) {
|
||||
if (!tracksCopy) {
|
||||
tracksCopy = new TrackList();
|
||||
TrackListIterator iter(t);
|
||||
Track *trk = iter.First();
|
||||
while (trk) {
|
||||
tracksCopy->Add(trk->Duplicate());
|
||||
trk = iter.Next();
|
||||
}
|
||||
}
|
||||
|
||||
WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack();
|
||||
newTrack->InsertSilence(0.0, t0 - t1);
|
||||
newTrack->Flush();
|
||||
@ -923,10 +934,29 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
||||
if (success) {
|
||||
p->SetAudioIOToken(token);
|
||||
mBusyProject = p;
|
||||
if (shifted && tracksCopy) {
|
||||
tracksCopy->Clear(true);
|
||||
delete tracksCopy;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// msmeyer: Delete recently added tracks if opening stream fails
|
||||
if (!shifted) {
|
||||
if (shifted) {
|
||||
// Restore the tracks to remove any inserted silence
|
||||
if (tracksCopy) {
|
||||
t->Clear(true);
|
||||
TrackListIterator iter(tracksCopy);
|
||||
Track *trk = iter.First();
|
||||
while (trk)
|
||||
{
|
||||
Track *tmp = trk;
|
||||
trk = iter.RemoveCurrent();
|
||||
t->Add(tmp);
|
||||
}
|
||||
delete tracksCopy;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// msmeyer: Delete recently added tracks if opening stream fails
|
||||
for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) {
|
||||
t->Remove(newRecordingTracks[i]);
|
||||
delete newRecordingTracks[i];
|
||||
|
@ -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
|
||||
|
@ -10,8 +10,9 @@
|
||||
#ifndef __AUDACITY_XML_XML_FILE_WRITER__
|
||||
#define __AUDACITY_XML_XML_FILE_WRITER__
|
||||
|
||||
#include <wx/ffile.h>
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/dynarray.h>
|
||||
#include <wx/ffile.h>
|
||||
|
||||
///
|
||||
/// 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;
|
||||
|
||||
|
@ -246,6 +246,7 @@
|
||||
<ClCompile Include="..\..\..\src\Dependencies.cpp" />
|
||||
<ClCompile Include="..\..\..\src\DeviceChange.cpp" />
|
||||
<ClCompile Include="..\..\..\src\DeviceManager.cpp" />
|
||||
<ClCompile Include="..\..\..\src\Diags.cpp" />
|
||||
<ClCompile Include="..\..\..\src\DirManager.cpp" />
|
||||
<ClCompile Include="..\..\..\src\Dither.cpp" />
|
||||
<ClCompile Include="..\..\..\src\effects\EffectRack.cpp" />
|
||||
@ -508,6 +509,7 @@
|
||||
<ClInclude Include="..\..\..\src\AllThemeResources.h" />
|
||||
<ClInclude Include="..\..\..\src\Audacity.h" />
|
||||
<ClInclude Include="..\..\..\src\AudacityApp.h" />
|
||||
<ClInclude Include="..\..\..\src\AudacityHeaders.h" />
|
||||
<ClInclude Include="..\..\..\src\AudacityLogger.h" />
|
||||
<ClInclude Include="..\..\..\src\AudioIO.h" />
|
||||
<ClInclude Include="..\..\..\src\AudioIOListener.h" />
|
||||
@ -520,6 +522,7 @@
|
||||
<ClInclude Include="..\..\..\src\CaptureEvents.h" />
|
||||
<ClInclude Include="..\..\..\src\commands\OpenSaveCommands.h" />
|
||||
<ClInclude Include="..\..\..\src\DeviceChange.h" />
|
||||
<ClInclude Include="..\..\..\src\Diags.h" />
|
||||
<ClInclude Include="..\..\..\src\effects\EffectRack.h" />
|
||||
<ClInclude Include="..\..\..\src\effects\NoiseReduction.h" />
|
||||
<ClInclude Include="..\..\..\src\effects\Phaser.h" />
|
||||
|
@ -834,6 +834,9 @@
|
||||
<ClCompile Include="..\..\..\src\SelectedRegion.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\Diags.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\src\AboutDialog.h">
|
||||
@ -1667,6 +1670,12 @@
|
||||
<ClInclude Include="..\..\..\src\SelectedRegion.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\Diags.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\AudacityHeaders.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="..\..\audacity.ico">
|
||||
|
Loading…
x
Reference in New Issue
Block a user