1
0
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:
Paul-Licameli 2015-04-17 18:25:52 -04:00
commit 4323ad60f6
19 changed files with 919 additions and 93 deletions

View File

@ -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"

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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
View 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
View 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

View File

@ -122,6 +122,8 @@ audacity_SOURCES = \
DeviceChange.h \
DeviceManager.cpp \
DeviceManager.h \
Diags.cpp \
Diags.h \
Envelope.cpp \
Envelope.h \
Experimental.h \

View File

@ -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);

View File

@ -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"); }

View File

@ -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,

View File

@ -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;
}

View File

@ -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__

View File

@ -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

View File

@ -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];

View File

@ -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

View File

@ -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;

View File

@ -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" />

View File

@ -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">