Update of /cvsroot/audacity/audacity-src/src/export
In directory sc8-pr-cvs11.sourceforge.net:/tmp/cvs-serv29995
Modified Files:
ExportCL.cpp
Log Message:
Support command line exporter on Windows and Mac.
Index: ExportCL.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/export/ExportCL.cpp,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- ExportCL.cpp 15 Apr 2007 02:23:36 -0000 1.16
+++ ExportCL.cpp 21 Apr 2007 21:56:19 -0000 1.17
@@ -14,12 +14,16 @@
**********************************************************************/
#include <stdio.h>
+#include <wx/log.h>
+#include <wx/process.h>
#include <wx/progdlg.h>
+#include <wx/textctrl.h>
#include "../Project.h"
#include "../Mix.h"
#include "../Prefs.h"
#include "../Internat.h"
+#include "../float_cast.h"
/* this structure combines the RIFF header, the format chunk, and the data
* chunk header */
@@ -44,109 +48,233 @@
wxUint32 dataLen; /* length of all samples in bytes */
};
+static void Drain(wxInputStream *s, wxString *o)
+{
+ while (s->CanRead()) {
+ char buffer[4096];
+
+ s->Read(buffer, WXSIZEOF(buffer) - 1);
+ buffer[s->LastRead()] = _T('\0');
+ *o += LAT1CTOWX(buffer);
+ }
+}
+
+class MyProcess : public wxProcess
+{
+public:
+ MyProcess(wxString *output)
+ {
+ mOutput = output;
+ mActive = true;
+ mStatus = -555;
+ Redirect();
+ }
+
+ bool IsActive()
+ {
+ return mActive;
+ }
+
+ void OnTerminate(int WXUNUSED( pid ), int status)
+ {
+ Drain(GetInputStream(), mOutput);
+ Drain(GetErrorStream(), mOutput);
+
+ mStatus = status;
+ mActive = false;
+ }
+
+ int GetStatus()
+ {
+ return mStatus;
+ }
+
+private:
+ wxString *mOutput;
+ bool mActive;
+ int mStatus;
+};
+
bool ExportCL(AudacityProject *project,
int channels, wxString fName,
bool selectionOnly, double t0, double t1,
MixerSpec *mixerSpec)
{
- int rate = int(project->GetRate() + 0.5);
- TrackList *tracks = project->GetTracks();
-
- wxString command =
gPrefs->Read(wxT("/FileFormats/ExternalProgramExportCommand"), wxT("lame -
'%f'"));
- command.Replace(wxT("%f"), fName);
-
- /* establish parameters */
- unsigned long totalSamples = (unsigned long)((t1 - t0) * rate + 0.5);
- unsigned long sampleBytes = totalSamples * channels *
SAMPLE_SIZE(int16Sample);
-
- /* fill up the wav header */
- wav_header header;
- header.riffID[0] = 'R';
- header.riffID[1] = 'I';
- header.riffID[2] = 'F';
- header.riffID[3] = 'F';
- header.riffType[0] = 'W';
- header.riffType[1] = 'A';
- header.riffType[2] = 'V';
- header.riffType[3] = 'E';
- header.lenAfterRiff = sampleBytes + 32;
+ MyProcess *p;
+ wxString output;
+ wxString cmd;
+ bool show;
- header.fmtID[0] = 'f';
- header.fmtID[1] = 'm';
- header.fmtID[2] = 't';
- header.fmtID[3] = ' ';
- header.formatChunkLen = 16;
- header.formatTag = 1;
- header.channels = channels;
- header.sampleRate = rate;
- header.bitsPerSample = SAMPLE_SIZE(int16Sample) * 8;
- header.blockAlign = header.bitsPerSample * header.channels;
- header.avgBytesPerSec = header.sampleRate * header.blockAlign;
+ // Retrieve settings
+ gPrefs->Read(wxT("/FileFormats/ExternalProgramShowOutput"), &show, false);
+ cmd = gPrefs->Read(wxT("/FileFormats/ExternalProgramExportCommand"),
wxT("lame - '%f'"));
+ cmd.Replace(wxT("%f"), fName);
- header.dataID[0] = 'd';
- header.dataID[1] = 'a';
- header.dataID[2] = 't';
- header.dataID[3] = 'a';
- header.dataLen = sampleBytes;
+ // Kick off the command
+ p = new MyProcess(&output);
+ if (!wxExecute(cmd, wxEXEC_ASYNC, p)) {
+ wxMessageBox(wxString::Format(_("Cannot export audio to %s"),
+ fName.c_str()));
+ p->Detach();
+ p->CloseOutput();
+ return false;
+ }
- FILE *pipe = popen(OSFILENAME(command), "w");
+ // Turn off logging to prevent broken pipe messages
+ wxLogNull nolog;
- /* write the header */
+ // establish parameters
+ int rate = lrint(project->GetRate());
+ sampleCount maxBlockLen = 44100 * 5;
+ unsigned long totalSamples = lrint((t1 - t0) * rate);
+ unsigned long sampleBytes = totalSamples * channels *
SAMPLE_SIZE(int16Sample);
- fwrite( &header, sizeof(wav_header), 1, pipe );
+ // fill up the wav header
+ wav_header header;
+ header.riffID[0] = 'R';
+ header.riffID[1] = 'I';
+ header.riffID[2] = 'F';
+ header.riffID[3] = 'F';
+ header.riffType[0] = 'W';
+ header.riffType[1] = 'A';
+ header.riffType[2] = 'V';
+ header.riffType[3] = 'E';
+ header.lenAfterRiff = wxUINT32_SWAP_ON_BE(sampleBytes + 32);
- sampleCount maxBlockLen = 44100 * 5;
+ header.fmtID[0] = 'f';
+ header.fmtID[1] = 'm';
+ header.fmtID[2] = 't';
+ header.fmtID[3] = ' ';
+ header.formatChunkLen = wxUINT32_SWAP_ON_BE(16);
+ header.formatTag = wxUINT16_SWAP_ON_BE(1);
+ header.channels = wxUINT16_SWAP_ON_BE(channels);
+ header.sampleRate = wxUINT32_SWAP_ON_BE(rate);
+ header.bitsPerSample = wxUINT16_SWAP_ON_BE(SAMPLE_SIZE(int16Sample) * 8);
+ header.blockAlign = wxUINT16_SWAP_ON_BE(header.bitsPerSample *
header.channels);
+ header.avgBytesPerSec = wxUINT32_SWAP_ON_BE(header.sampleRate *
header.blockAlign);
+ header.dataID[0] = 'd';
+ header.dataID[1] = 'a';
+ header.dataID[2] = 't';
+ header.dataID[3] = 'a';
+ header.dataLen = wxUINT32_SWAP_ON_BE(sampleBytes);
- bool cancelling = false;
+ // write the header
+ wxOutputStream *os = p->GetOutputStream();
+ os->Write(&header, sizeof(wav_header));
+ // Mix 'em up
int numWaveTracks;
WaveTrack **waveTracks;
+ TrackList *tracks = project->GetTracks();
tracks->GetWaveTracks(selectionOnly, &numWaveTracks, &waveTracks);
- Mixer *mixer = new Mixer(numWaveTracks, waveTracks,
+ Mixer *mixer = new Mixer(numWaveTracks,
+ waveTracks,
tracks->GetTimeTrack(),
- t0, t1,
- channels, maxBlockLen, true,
- rate, int16Sample, true, mixerSpec);
+ t0,
+ t1,
+ channels,
+ maxBlockLen,
+ true,
+ rate,
+ int16Sample,
+ true,
+ mixerSpec);
- GetActiveProject()->ProgressShow(_("E&xport"),
+ size_t numBytes = 0;
+ samplePtr mixed;
+ bool cancelling = false;
+
+ // Prepare the progress display
+ GetActiveProject()->ProgressShow(_("Export"),
selectionOnly ?
_("Exporting the selected audio using command-line encoder") :
_("Exporting the entire project using command-line encoder"));
- while(!cancelling) {
- sampleCount numSamples = mixer->Process(maxBlockLen);
-
- if (numSamples == 0)
- break;
-
- samplePtr mixed = mixer->GetBuffer();
+ // Starting piping the mixed data to the command
+ while (!cancelling && p->IsActive() && os->IsOk()) {
+ // Capture any stdout and stderr from the command
+ Drain(p->GetInputStream(), &output);
+ Drain(p->GetErrorStream(), &output);
- char *buffer = new char[numSamples * SAMPLE_SIZE(int16Sample) *
channels];
- wxASSERT(buffer);
+ // Need to mix another block
+ if (numBytes == 0) {
+ sampleCount numSamples = mixer->Process(maxBlockLen);
+ if (numSamples == 0) {
+ break;
+ }
+
+ mixed = mixer->GetBuffer();
+ numBytes = numSamples * channels;
- // Byte-swapping is neccesary on big-endian machines, since
- // WAV files are little-endian
+ // Byte-swapping is neccesary on big-endian machines, since
+ // WAV files are little-endian
#if wxBYTE_ORDER == wxBIG_ENDIAN
- {
- short *buffer = (short*)mixed;
- for( int i = 0; i < numSamples; i++ )
- buffer[i] = wxINT16_SWAP_ON_BE(buffer[i]);
- }
+ wxUint16 *buffer = (wxUint16 *) mixed;
+ for (int i = 0; i < numBytes; i++) {
+ buffer[i] = wxUINT16_SWAP_ON_BE(buffer[i]);
+ }
#endif
+ numBytes *= SAMPLE_SIZE(int16Sample);
+ }
- fwrite( mixed, numSamples * channels * SAMPLE_SIZE(int16Sample), 1, pipe
);
+ // Don't write too much at once...pipes may not be able to handle it
+ size_t bytes = wxMin(numBytes, 4096);
+ numBytes -= bytes;
- int progressvalue = int (1000 * ((mixer->MixGetCurrentTime()-t0) /
+ while (bytes > 0) {
+ os->Write(mixed, bytes);
+ if (!os->IsOk()) {
+ break;
+ }
+ bytes -= os->LastWrite();
+ mixed += os->LastWrite();
+ }
+
+ // Update the progress display
+ int progressvalue = lrint(1000 * ((mixer->MixGetCurrentTime()-t0) /
(t1-t0)));
cancelling = !GetActiveProject()->ProgressUpdate(progressvalue);
-
- delete[]buffer;
}
+
+ // Done with the progress display
GetActiveProject()->ProgressHide();
- delete mixer;
+ // Should make the process die
+ p->CloseOutput();
- pclose( pipe );
+ // Wait for process to terminate
+ while (p->IsActive()) {
+ wxMilliSleep(10);
+ wxYield();
+ }
+
+ // Display output on error or if the user wants to see it
+ if (p->GetStatus() != 0 || show) {
+ wxDialog dlg(NULL,
+ wxID_ANY,
+ wxString(_("Command Output")),
+ wxDefaultPosition,
+ wxSize(600, 400),
+ wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
+
+ ShuttleGui S(&dlg, eIsCreating);
+ wxTextCtrl *tc = S.AddTextWindow(output);
+ S.StartHorizontalLay(wxALIGN_CENTER, false);
+ {
+ S.Id(wxID_OK).AddButton(_("&OK"))->SetDefault();
+ }
+ dlg.GetSizer()->AddSpacer(5);
+ dlg.Layout();
+ dlg.SetMinSize(dlg.GetSize());
+ dlg.Center();
+
+ dlg.ShowModal();
+ }
+
+ // Clean up
+ delete mixer;
+ delete[] waveTracks;
+ delete p;
return true;
}
@@ -179,10 +307,16 @@
S.SetStretchyCol(1);
S.TieTextBox(_("Command:"),
wxT("/FileFormats/ExternalProgramExportCommand"),
- wxT("lame - '%f'"),
+ wxT("lame - \"%f\""),
64);
+ S.AddFixedText(wxT(""));
+ S.TieCheckBox(_("Show output"),
+ wxT("/FileFormats/ExternalProgramShowOutput"),
+ false);
}
S.EndMultiColumn();
+
+
S.AddFixedText(_("Include \"%f\" where the output filename should
be substituted."));
}
S.EndStatic();
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Audacity-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/audacity-cvs