1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-09-23 07:29:46 +02:00

Exception safety in: subclasses of ExportPlugin; and more error checking

This commit is contained in:
Paul Licameli 2016-12-24 08:57:27 -05:00
parent 48459404a5
commit 0c8bedc59a
8 changed files with 156 additions and 95 deletions

View File

@ -26,6 +26,8 @@ class FileIO
public: public:
FileIO(const wxString & name, FileIOMode mode); FileIO(const wxString & name, FileIOMode mode);
// Calls Close()
~FileIO(); ~FileIO();
bool IsOpened(); bool IsOpened();

View File

@ -354,14 +354,19 @@ ProgressResult ExportCL::Export(AudacityProject *project,
// Kick off the command // Kick off the command
ExportCLProcess process(&output); ExportCLProcess process(&output);
rc = wxExecute(cmd, wxEXEC_ASYNC, &process);
{
#if defined(__WXMSW__) #if defined(__WXMSW__)
if (!opath.IsEmpty()) { auto cleanup = finally( [&] {
wxSetEnv(wxT("PATH"),opath.c_str()); if (!opath.IsEmpty()) {
} wxSetEnv(wxT("PATH"),opath.c_str());
}
} );
#endif #endif
rc = wxExecute(cmd, wxEXEC_ASYNC, &process);
}
if (!rc) { if (!rc) {
wxMessageBox(wxString::Format(_("Cannot export audio to %s"), wxMessageBox(wxString::Format(_("Cannot export audio to %s"),
fName.c_str())); fName.c_str()));
@ -435,6 +440,11 @@ ProgressResult ExportCL::Export(AudacityProject *project,
auto updateResult = ProgressResult::Success; auto updateResult = ProgressResult::Success;
{ {
auto closeIt = finally ( [&] {
// Should make the process die, before propagating any exception
process.CloseOutput();
} );
// Prepare the progress display // Prepare the progress display
ProgressDialog progress(_("Export"), ProgressDialog progress(_("Export"),
selectionOnly ? selectionOnly ?
@ -475,6 +485,7 @@ ProgressResult ExportCL::Export(AudacityProject *project,
while (bytes > 0) { while (bytes > 0) {
os->Write(mixed, bytes); os->Write(mixed, bytes);
if (!os->IsOk()) { if (!os->IsOk()) {
updateResult = ProgressResult::Cancelled;
break; break;
} }
bytes -= os->LastWrite(); bytes -= os->LastWrite();
@ -487,9 +498,6 @@ ProgressResult ExportCL::Export(AudacityProject *project,
// Done with the progress display // Done with the progress display
} }
// Should make the process die
process.CloseOutput();
// Wait for process to terminate // Wait for process to terminate
while (process.IsActive()) { while (process.IsActive()) {
wxMilliSleep(10); wxMilliSleep(10);

View File

@ -165,7 +165,7 @@ private:
unsigned mChannels{}; unsigned mChannels{};
bool mSupportsUTF8{}; bool mSupportsUTF8{};
// Smart pointer fields, their order is the reverse in which they are reset in Finalize(): // Smart pointer fields, their order is the reverse in which they are reset in FreeResources():
AVFifoBufferHolder mEncAudioFifo; // FIFO to write incoming audio samples into AVFifoBufferHolder mEncAudioFifo; // FIFO to write incoming audio samples into
AVMallocHolder<int16_t> mEncAudioFifoOutBuf; // buffer to read _out_ of the FIFO into AVMallocHolder<int16_t> mEncAudioFifoOutBuf; // buffer to read _out_ of the FIFO into
AVFormatContextHolder mEncFormatCtx; // libavformat's context for our output file AVFormatContextHolder mEncFormatCtx; // libavformat's context for our output file
@ -357,7 +357,7 @@ bool ExportFFmpeg::Init(const char *shortname, AudacityProject *project, const T
return false; return false;
} }
// Only now, we can keep all the resources until Finalize(). // Only now, we can keep all the resources until after Finalize().
// Cancel the local cleanup. // Cancel the local cleanup.
cleanup.release(); cleanup.release();
@ -737,7 +737,6 @@ bool ExportFFmpeg::Finalize()
// Write any file trailers. // Write any file trailers.
av_write_trailer(mEncFormatCtx.get()); av_write_trailer(mEncFormatCtx.get());
FreeResources();
return true; return true;
} }
@ -774,7 +773,8 @@ bool ExportFFmpeg::EncodeAudioFrame(int16_t *pFrame, size_t frameSize)
// Put the raw audio samples into the FIFO. // Put the raw audio samples into the FIFO.
ret = av_fifo_generic_write(mEncAudioFifo.get(), pRawSamples, nBytesToWrite,NULL); ret = av_fifo_generic_write(mEncAudioFifo.get(), pRawSamples, nBytesToWrite,NULL);
wxASSERT(ret == nBytesToWrite); if(ret != nBytesToWrite)
return false;
if (nAudioFrameSizeOut > mEncAudioFifoOutBufSiz) { if (nAudioFrameSizeOut > mEncAudioFifoOutBufSiz) {
wxMessageBox(wxString::Format(_("FFmpeg : ERROR - nAudioFrameSizeOut too large.")), wxMessageBox(wxString::Format(_("FFmpeg : ERROR - nAudioFrameSizeOut too large.")),
@ -853,11 +853,13 @@ ProgressResult ExportFFmpeg::Export(AudacityProject *project,
if (mSubFormat == FMT_OTHER) if (mSubFormat == FMT_OTHER)
shortname = gPrefs->Read(wxT("/FileFormats/FFmpegFormat"),wxT("matroska")); shortname = gPrefs->Read(wxT("/FileFormats/FFmpegFormat"),wxT("matroska"));
ret = Init(shortname.mb_str(),project, metadata, subformat); ret = Init(shortname.mb_str(),project, metadata, subformat);
auto cleanup = finally ( [&] { FreeResources(); } );
if (!ret) if (!ret)
return ProgressResult::Cancelled; return ProgressResult::Cancelled;
size_t pcmBufferSize = 1024; size_t pcmBufferSize = 1024;
const WaveTrackConstArray waveTracks = const WaveTrackConstArray waveTracks =
tracks->GetWaveTrackConstArray(selectionOnly, false); tracks->GetWaveTrackConstArray(selectionOnly, false);
auto mixer = CreateMixer(waveTracks, auto mixer = CreateMixer(waveTracks,
@ -881,13 +883,19 @@ ProgressResult ExportFFmpeg::Export(AudacityProject *project,
short *pcmBuffer = (short *)mixer->GetBuffer(); short *pcmBuffer = (short *)mixer->GetBuffer();
EncodeAudioFrame(pcmBuffer, (pcmNumSamples)*sizeof(int16_t)*mChannels); if (!EncodeAudioFrame(
pcmBuffer, (pcmNumSamples)*sizeof(int16_t)*mChannels)) {
updateResult = ProgressResult::Cancelled;
break;
}
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0); updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
} }
} }
Finalize(); if ( updateResult != ProgressResult::Cancelled )
if ( !Finalize() )
return ProgressResult::Cancelled;
return updateResult; return updateResult;
} }

View File

@ -204,6 +204,7 @@ private:
bool GetMetadata(AudacityProject *project, const Tags *tags); bool GetMetadata(AudacityProject *project, const Tags *tags);
// Should this be a stack variable instead in Export?
FLAC__StreamMetadataHandle mMetadata; FLAC__StreamMetadataHandle mMetadata;
}; };
@ -262,6 +263,10 @@ ProgressResult ExportFLAC::Export(AudacityProject *project,
encoder.set_metadata(&p, 1); encoder.set_metadata(&p, 1);
} }
auto cleanup1 = finally( [&] {
mMetadata.reset(); // need this?
} );
sampleFormat format; sampleFormat format;
if (bitDepthPref == wxT("24")) { if (bitDepthPref == wxT("24")) {
format = int24Sample; format = int24Sample;
@ -312,6 +317,13 @@ ProgressResult ExportFLAC::Export(AudacityProject *project,
mMetadata.reset(); mMetadata.reset();
auto cleanup2 = finally( [&] {
#ifndef LEGACY_FLAC
f.Detach(); // libflac closes the file
#endif
encoder.finish();
} );
const WaveTrackConstArray waveTracks = const WaveTrackConstArray waveTracks =
tracks->GetWaveTrackConstArray(selectionOnly, false); tracks->GetWaveTrackConstArray(selectionOnly, false);
auto mixer = CreateMixer(waveTracks, auto mixer = CreateMixer(waveTracks,
@ -322,38 +334,40 @@ ProgressResult ExportFLAC::Export(AudacityProject *project,
ArraysOf<FLAC__int32> tmpsmplbuf{ numChannels, SAMPLES_PER_RUN, true }; ArraysOf<FLAC__int32> tmpsmplbuf{ numChannels, SAMPLES_PER_RUN, true };
{ ProgressDialog progress(wxFileName(fName).GetName(),
ProgressDialog progress(wxFileName(fName).GetName(), selectionOnly ?
selectionOnly ? _("Exporting the selected audio as FLAC") :
_("Exporting the selected audio as FLAC") : _("Exporting the entire project as FLAC"));
_("Exporting the entire project as FLAC"));
while (updateResult == ProgressResult::Success) { while (updateResult == ProgressResult::Success) {
auto samplesThisRun = mixer->Process(SAMPLES_PER_RUN); auto samplesThisRun = mixer->Process(SAMPLES_PER_RUN);
if (samplesThisRun == 0) { //stop encoding if (samplesThisRun == 0) { //stop encoding
break; break;
} }
else { else {
for (size_t i = 0; i < numChannels; i++) { for (size_t i = 0; i < numChannels; i++) {
samplePtr mixed = mixer->GetBuffer(i); samplePtr mixed = mixer->GetBuffer(i);
if (format == int24Sample) { if (format == int24Sample) {
for (decltype(samplesThisRun) j = 0; j < samplesThisRun; j++) { for (decltype(samplesThisRun) j = 0; j < samplesThisRun; j++) {
tmpsmplbuf[i][j] = ((int *)mixed)[j]; tmpsmplbuf[i][j] = ((int *)mixed)[j];
} }
} }
else { else {
for (decltype(samplesThisRun) j = 0; j < samplesThisRun; j++) { for (decltype(samplesThisRun) j = 0; j < samplesThisRun; j++) {
tmpsmplbuf[i][j] = ((short *)mixed)[j]; tmpsmplbuf[i][j] = ((short *)mixed)[j];
}
} }
} }
encoder.process(reinterpret_cast<const FLAC__int32**>(tmpsmplbuf.get()), samplesThisRun);
} }
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0); if (! encoder.process(
reinterpret_cast<FLAC__int32**>( tmpsmplbuf.get() ),
samplesThisRun) ) {
updateResult = ProgressResult::Cancelled;
break;
}
} }
f.Detach(); // libflac closes the file
encoder.finish();
} }
if (updateResult == ProgressResult::Success)
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
return updateResult; return updateResult;
} }

View File

@ -217,6 +217,7 @@ ProgressResult ExportMP2::Export(AudacityProject *project,
twolame_options *encodeOptions; twolame_options *encodeOptions;
encodeOptions = twolame_init(); encodeOptions = twolame_init();
auto cleanup = finally( [&] { twolame_close(&encodeOptions); } );
twolame_set_in_samplerate(encodeOptions, (int)(rate + 0.5)); twolame_set_in_samplerate(encodeOptions, (int)(rate + 0.5));
twolame_set_out_samplerate(encodeOptions, (int)(rate + 0.5)); twolame_set_out_samplerate(encodeOptions, (int)(rate + 0.5));
@ -227,7 +228,6 @@ ProgressResult ExportMP2::Export(AudacityProject *project,
{ {
wxMessageBox(_("Cannot export MP2 with this sample rate and bit rate"), wxMessageBox(_("Cannot export MP2 with this sample rate and bit rate"),
_("Error"), wxICON_STOP); _("Error"), wxICON_STOP);
twolame_close(&encodeOptions);
return ProgressResult::Cancelled; return ProgressResult::Cancelled;
} }
@ -238,7 +238,6 @@ ProgressResult ExportMP2::Export(AudacityProject *project,
FileIO outFile(fName, FileIO::Output); FileIO outFile(fName, FileIO::Output);
if (!outFile.IsOpened()) { if (!outFile.IsOpened()) {
wxMessageBox(_("Unable to open target file for writing")); wxMessageBox(_("Unable to open target file for writing"));
twolame_close(&encodeOptions);
return ProgressResult::Cancelled; return ProgressResult::Cancelled;
} }
@ -288,6 +287,11 @@ ProgressResult ExportMP2::Export(AudacityProject *project,
mp2Buffer.get(), mp2Buffer.get(),
mp2BufferSize); mp2BufferSize);
if (mp2BufferNumBytes < 0) {
updateResult = ProgressResult::Cancelled;
break;
}
outFile.Write(mp2Buffer.get(), mp2BufferNumBytes); outFile.Write(mp2Buffer.get(), mp2BufferNumBytes);
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0); updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
@ -302,15 +306,11 @@ ProgressResult ExportMP2::Export(AudacityProject *project,
if (mp2BufferNumBytes > 0) if (mp2BufferNumBytes > 0)
outFile.Write(mp2Buffer.get(), mp2BufferNumBytes); outFile.Write(mp2Buffer.get(), mp2BufferNumBytes);
twolame_close(&encodeOptions);
/* Write ID3 tag if it was supposed to be at the end of the file */ /* Write ID3 tag if it was supposed to be at the end of the file */
if (id3len && endOfFile) if (id3len && endOfFile)
outFile.Write(id3buffer.get(), id3len); outFile.Write(id3buffer.get(), id3len);
/* Close file */
outFile.Close(); outFile.Close();
return updateResult; return updateResult;
@ -328,7 +328,9 @@ struct id3_tag_deleter {
using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>; using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
// returns buffer len; caller frees // returns buffer len; caller frees
int ExportMP2::AddTags(AudacityProject * WXUNUSED(project), ArrayOf<char> &buffer, bool *endOfFile, const Tags *tags) int ExportMP2::AddTags(
AudacityProject * WXUNUSED(project), ArrayOf< char > &buffer,
bool *endOfFile, const Tags *tags)
{ {
#ifdef USE_LIBID3TAG #ifdef USE_LIBID3TAG
id3_tag_holder tp { id3_tag_new() }; id3_tag_holder tp { id3_tag_new() };

View File

@ -1809,6 +1809,9 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
long bytes; long bytes;
size_t bufferSize = std::max(0, exporter.GetOutBufferSize()); size_t bufferSize = std::max(0, exporter.GetOutBufferSize());
if (bufferSize == 0)
return ProgressResult::Cancelled;
ArrayOf<unsigned char> buffer{ bufferSize }; ArrayOf<unsigned char> buffer{ bufferSize };
wxASSERT(buffer); wxASSERT(buffer);
@ -1873,6 +1876,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
wxString msg; wxString msg;
msg.Printf(_("Error %ld returned from MP3 encoder"), bytes); msg.Printf(_("Error %ld returned from MP3 encoder"), bytes);
wxMessageBox(msg); wxMessageBox(msg);
updateResult = ProgressResult::Cancelled;
break; break;
} }
@ -1882,28 +1886,29 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
} }
} }
bytes = exporter.FinishStream(buffer.get()); if ( updateResult != ProgressResult::Cancelled ) {
bytes = exporter.FinishStream(buffer.get());
if (bytes) { if (bytes > 0) {
outFile.Write(buffer.get(), bytes); outFile.Write(buffer.get(), bytes);
}
// Write ID3 tag if it was supposed to be at the end of the file
if (id3len > 0 && endOfFile) {
outFile.Write(id3buffer.get(), id3len);
}
// Always write the info (Xing/Lame) tag. Until we stop supporting Lame
// versions before 3.98, we must do this after the MP3 file has been
// closed.
//
// Also, if beWriteInfoTag() is used, mGF will no longer be valid after
// this call, so do not use it.
exporter.PutInfoTag(outFile, pos);
outFile.Close();
} }
// Write ID3 tag if it was supposed to be at the end of the file
if (id3len && endOfFile) {
outFile.Write(id3buffer.get(), id3len);
}
// Always write the info (Xing/Lame) tag. Until we stop supporting Lame
// versions before 3.98, we must do this after the MP3 file has been
// closed.
//
// Also, if beWriteInfoTag() is used, mGF will no longer be valid after
// this call, so do not use it.
exporter.PutInfoTag(outFile, pos);
// Close the file
outFile.Close();
return updateResult; return updateResult;
} }

View File

@ -193,6 +193,15 @@ ProgressResult ExportOGG::Export(AudacityProject *project,
vorbis_dsp_state dsp; vorbis_dsp_state dsp;
vorbis_block block; vorbis_block block;
auto cleanup = finally( [&] {
ogg_stream_clear(&stream);
vorbis_block_clear(&block);
vorbis_dsp_clear(&dsp);
vorbis_info_clear(&info);
vorbis_comment_clear(&comment);
} );
// Encoding setup // Encoding setup
vorbis_info_init(&info); vorbis_info_init(&info);
vorbis_encode_init_vbr(&info, numChannels, (int)(rate + 0.5), quality); vorbis_encode_init_vbr(&info, numChannels, (int)(rate + 0.5), quality);
@ -257,9 +266,10 @@ ProgressResult ExportOGG::Export(AudacityProject *project,
float **vorbis_buffer = vorbis_analysis_buffer(&dsp, SAMPLES_PER_RUN); float **vorbis_buffer = vorbis_analysis_buffer(&dsp, SAMPLES_PER_RUN);
auto samplesThisRun = mixer->Process(SAMPLES_PER_RUN); auto samplesThisRun = mixer->Process(SAMPLES_PER_RUN);
int err;
if (samplesThisRun == 0) { if (samplesThisRun == 0) {
// Tell the library that we wrote 0 bytes - signalling the end. // Tell the library that we wrote 0 bytes - signalling the end.
vorbis_analysis_wrote(&dsp, 0); err = vorbis_analysis_wrote(&dsp, 0);
} }
else { else {
@ -269,7 +279,7 @@ ProgressResult ExportOGG::Export(AudacityProject *project,
} }
// tell the encoder how many samples we have // tell the encoder how many samples we have
vorbis_analysis_wrote(&dsp, samplesThisRun); err = vorbis_analysis_wrote(&dsp, samplesThisRun);
} }
// I don't understand what this call does, so here is the comment // I don't understand what this call does, so here is the comment
@ -278,22 +288,23 @@ ProgressResult ExportOGG::Export(AudacityProject *project,
// vorbis does some data preanalysis, then divvies up blocks // vorbis does some data preanalysis, then divvies up blocks
// for more involved (potentially parallel) processing. Get // for more involved (potentially parallel) processing. Get
// a single block for encoding now // a single block for encoding now
while (vorbis_analysis_blockout(&dsp, &block) == 1) { while (!err && vorbis_analysis_blockout(&dsp, &block) == 1) {
// analysis, assume we want to use bitrate management // analysis, assume we want to use bitrate management
vorbis_analysis(&block, NULL); err = vorbis_analysis(&block, NULL);
vorbis_bitrate_addblock(&block); if (!err)
err = vorbis_bitrate_addblock(&block);
while (vorbis_bitrate_flushpacket(&dsp, &packet)) { while (!err && vorbis_bitrate_flushpacket(&dsp, &packet)) {
// add the packet to the bitstream // add the packet to the bitstream
ogg_stream_packetin(&stream, &packet); err = ogg_stream_packetin(&stream, &packet);
// From vorbis-tools-1.0/oggenc/encode.c: // From vorbis-tools-1.0/oggenc/encode.c:
// If we've gone over a page boundary, we can do actual output, // If we've gone over a page boundary, we can do actual output,
// so do so (for however many pages are available). // so do so (for however many pages are available).
while (!eos) { while (!err && !eos) {
int result = ogg_stream_pageout(&stream, &page); int result = ogg_stream_pageout(&stream, &page);
if (!result) { if (!result) {
break; break;
@ -309,17 +320,15 @@ ProgressResult ExportOGG::Export(AudacityProject *project,
} }
} }
if (err) {
updateResult = ProgressResult::Cancelled;
break;
}
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0); updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
} }
} }
ogg_stream_clear(&stream);
vorbis_block_clear(&block);
vorbis_dsp_clear(&dsp);
vorbis_info_clear(&info);
vorbis_comment_clear(&comment);
outFile.Close(); outFile.Close();
return updateResult; return updateResult;

View File

@ -331,7 +331,7 @@ private:
ArrayOf<char> AdjustString(const wxString & wxStr, int sf_format); ArrayOf<char> AdjustString(const wxString & wxStr, int sf_format);
bool AddStrings(AudacityProject *project, SNDFILE *sf, const Tags *tags, int sf_format); bool AddStrings(AudacityProject *project, SNDFILE *sf, const Tags *tags, int sf_format);
void AddID3Chunk(wxString fName, const Tags *tags, int sf_format); bool AddID3Chunk(wxString fName, const Tags *tags, int sf_format);
}; };
@ -516,6 +516,7 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
_("Error while writing %s file (disk full?).\nLibsndfile says \"%s\""), _("Error while writing %s file (disk full?).\nLibsndfile says \"%s\""),
formatStr.c_str(), formatStr.c_str(),
wxString::FromAscii(buffer2).c_str())); wxString::FromAscii(buffer2).c_str()));
updateResult = ProgressResult::Cancelled;
break; break;
} }
@ -534,7 +535,8 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
if (((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) || if (((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||
((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)) ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV))
AddID3Chunk(fName, metadata, sf_format); if (!AddID3Chunk(fName, metadata, sf_format) )
return ProgressResult::Cancelled;
return updateResult; return updateResult;
} }
@ -699,7 +701,7 @@ struct id3_tag_deleter {
}; };
using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>; using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
void ExportPCM::AddID3Chunk(wxString fName, const Tags *tags, int sf_format) bool ExportPCM::AddID3Chunk(wxString fName, const Tags *tags, int sf_format)
{ {
#ifdef USE_LIBID3TAG #ifdef USE_LIBID3TAG
id3_tag_holder tp { id3_tag_new() }; id3_tag_holder tp { id3_tag_new() };
@ -784,12 +786,12 @@ void ExportPCM::AddID3Chunk(wxString fName, const Tags *tags, int sf_format)
len = id3_tag_render(tp.get(), 0); len = id3_tag_render(tp.get(), 0);
if (len == 0) if (len == 0)
return; return true;
if ((len % 2) != 0) len++; // Length must be even. if ((len % 2) != 0) len++; // Length must be even.
ArrayOf<id3_byte_t> buffer { len, true }; ArrayOf<id3_byte_t> buffer { len, true };
if (buffer == NULL) if (buffer == NULL)
return; return false;
// Zero all locations, for ending odd UTF16 content // Zero all locations, for ending odd UTF16 content
// correctly, i.e., two '\0's at the end. // correctly, i.e., two '\0's at the end.
@ -797,33 +799,44 @@ void ExportPCM::AddID3Chunk(wxString fName, const Tags *tags, int sf_format)
id3_tag_render(tp.get(), buffer.get()); id3_tag_render(tp.get(), buffer.get());
wxFFile f(fName, wxT("r+b")); wxFFile f(fName, wxT("r+b"));
// FIXME: TRAP_ERR wxFFILE ops in Export PCM ID3 could fail.
if (f.IsOpened()) { if (f.IsOpened()) {
wxUint32 sz; wxUint32 sz;
sz = (wxUint32) len; sz = (wxUint32) len;
f.SeekEnd(0); if (!f.SeekEnd(0))
return false;
if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
f.Write("id3 ", 4); // Must be lower case for foobar2000. {
if (4 != f.Write("id3 ", 4))// Must be lower case for foobar2000.
return false ;
}
else { else {
f.Write("ID3 ", 4); if (4 != f.Write("ID3 ", 4))
return false;
sz = wxUINT32_SWAP_ON_LE(sz); sz = wxUINT32_SWAP_ON_LE(sz);
} }
f.Write(&sz, 4); if (4 != f.Write(&sz, 4))
return false;
f.Write(buffer.get(), len); if (len != f.Write(buffer.get(), len))
return false;
sz = (wxUint32) f.Tell() - 8; sz = (wxUint32) f.Tell() - 8;
if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
sz = wxUINT32_SWAP_ON_LE(sz); sz = wxUINT32_SWAP_ON_LE(sz);
f.Seek(4); if (!f.Seek(4))
f.Write(&sz, 4); return false;
if (4 != f.Write(&sz, 4))
return false;
f.Close(); if (!f.Close())
return false;
} }
else
return false;
#endif #endif
return; return true;
} }
wxWindow *ExportPCM::OptionsCreate(wxWindow *parent, int format) wxWindow *ExportPCM::OptionsCreate(wxWindow *parent, int format)