bool ExportOGG(AudacityProject *project, bool stereo, wxString fName, bool selectionOnly, double t0, double t1) { double rate = project->GetRate(); wxWindow *parent = project; TrackList *tracks = project->GetTracks(); double quality = (gPrefs->Read("/FileFormats/OggExportQuality", 50)/(float)100.0); wxLogNull logNo; // temporarily disable wxWindows error messages bool cancelling = false; wxFFile outFile(fName, "wb"); if(!outFile.IsOpened()) { wxMessageBox(_("Unable to open target file for writing")); return false; } // All the Ogg and Vorbis encoding data ogg_stream_state stream; ogg_page page; ogg_packet packet; vorbis_info info; vorbis_comment comment; vorbis_dsp_state dsp; vorbis_block block; // Encoding setup vorbis_info_init(&info); vorbis_encode_init_vbr(&info, stereo ? 2 : 1, int(rate + 0.5), quality); vorbis_comment_init(&comment); // If we wanted to add comments, we would do it here // Set up analysis state and auxiliary encoding storage vorbis_analysis_init(&dsp, &info); vorbis_block_init(&dsp, &block); // Set up packet->stream encoder. According to encoder example, // a random serial number makes it more likely that you can make // chained streams with concatenation. srand(time(NULL)); ogg_stream_init(&stream, rand()); // First we need to write the required headers: // 1. The Ogg bitstream header, which contains codec setup params // 2. The Vorbis comment header // 3. The bitstream codebook. // // After we create those our responsibility is complete, libvorbis will // take care of any other ogg bistream constraints (again, according // to the example encoder source) ogg_packet bitstream_header; ogg_packet comment_header; ogg_packet codebook_header; vorbis_analysis_headerout(&dsp, &comment, &bitstream_header, &comment_header, &codebook_header); // Place these headers into the stream ogg_stream_packetin(&stream, &bitstream_header); ogg_stream_packetin(&stream, &comment_header); ogg_stream_packetin(&stream, &codebook_header); // Flushing these headers now guarentees that audio data will // start on a new page, which apparently makes streaming easier ogg_stream_flush(&stream, &page); outFile.Write(page.header, page.header_len); outFile.Write(page.body, page.body_len); double t = t0; bool done = false; wxProgressDialog *progress = NULL; wxYield(); wxStartTimer(); while(!done && !cancelling){ float deltat = (float)SAMPLES_PER_RUN / rate; sampleCount samplesThisRun = SAMPLES_PER_RUN; Mixer *mixer = new Mixer(stereo ? 2 : 1, SAMPLES_PER_RUN, /* interleaved = */ false, rate, floatSample); if(t + deltat > t1) { done = true; deltat = t1 - t; samplesThisRun = int(deltat * rate + 0.5); } mixer->Clear(); TrackListIterator iter(tracks); Track *tr = iter.First(); while (tr) { if (tr->GetKind() == Track::Wave) { if (tr->GetSelected() || !selectionOnly) { if (tr->GetChannel() == Track::MonoChannel) mixer->MixMono((WaveTrack *) tr, t, t + deltat); else if (tr->GetChannel() == Track::LeftChannel) mixer->MixLeft((WaveTrack *) tr, t, t + deltat); else if (tr->GetChannel() == Track::RightChannel) mixer->MixRight((WaveTrack *) tr, t, t + deltat); } } tr = iter.Next(); } float **vorbis_buffer = vorbis_analysis_buffer(&dsp, SAMPLES_PER_RUN); float *left = (float *)mixer->GetBuffer(0); memcpy(vorbis_buffer[0], left, sizeof(float)*SAMPLES_PER_RUN); if(stereo) { float *right = (float *)mixer->GetBuffer(1); memcpy(vorbis_buffer[1], right, sizeof(float)*SAMPLES_PER_RUN); } // tell the encoder how many samples we have vorbis_analysis_wrote(&dsp, samplesThisRun); // I don't understand what this call does, so here is the comment // from the example, verbatim: // // vorbis does some data preanalysis, then divvies up blocks // for more involved (potentially parallel) processing. Get // a single block for encoding now while(vorbis_analysis_blockout(&dsp, &block) == 1) { // analysis, assume we want to use bitrate management vorbis_analysis(&block, NULL); vorbis_bitrate_addblock(&block); while(vorbis_bitrate_flushpacket(&dsp, &packet)) { // add the packet to the bitstream ogg_stream_packetin(&stream, &packet); int result = ogg_stream_pageout(&stream, &page); if(result != 0) { outFile.Write(page.header, page.header_len); outFile.Write(page.body, page.body_len); } } } if(progress) cancelling = !progress->Update(int (((t - t0) * 1000) / (t1 - t0) + 0.5)); else if(wxGetElapsedTime(false) > 500) { wxString message = selectionOnly ? _("Exporting the selected audio as Ogg Vorbis") : _("Exporting the entire project as Ogg Vorbis"); progress = new wxProgressDialog( _("Export"), message, 1000, parent, wxPD_CAN_ABORT | wxPD_REMAINING_TIME | wxPD_AUTO_HIDE); } delete mixer; t += deltat; } outFile.Close(); if(progress) delete progress; return true; }
bool QuickMix(TrackList *tracks, DirManager *dirManager, double rate, sampleFormat format) { WaveTrack **waveArray; VTrack *t; int numWaves = 0; int numLeft = 0; int numRight = 0; int numMono = 0; bool mono = false; int w; TrackListIterator iter(tracks); t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == VTrack::Wave) { numWaves++; switch (t->GetChannel()) { case VTrack::MonoChannel: numLeft++; numRight++; numMono++; break; case VTrack::LeftChannel: numLeft++; break; case VTrack::RightChannel: numRight++; break; } } t = iter.Next(); } if (numMono == numWaves || numLeft == numWaves || numRight == numWaves) mono = true; double totalTime = 0.0; waveArray = new WaveTrack *[numWaves]; w = 0; t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == VTrack::Wave) { waveArray[w++] = (WaveTrack *) t; if (t->GetMaxLen() > totalTime) totalTime = t->GetMaxLen(); } t = iter.Next(); } WaveTrack *mixLeft = new WaveTrack(dirManager); mixLeft->SetSampleFormat(format); mixLeft->SetRate(rate); mixLeft->SetChannel(VTrack::MonoChannel); mixLeft->SetName(_("Mix")); WaveTrack *mixRight = 0; if (!mono) { mixRight = new WaveTrack(dirManager); mixRight->SetSampleFormat(format); mixRight->SetRate(rate); mixRight->SetName(_("Mix")); mixLeft->SetChannel(VTrack::LeftChannel); mixRight->SetChannel(VTrack::RightChannel); mixLeft->SetLinked(true); } int maxBlockLen = mixLeft->GetIdealBlockSize(); double maxBlockTime = maxBlockLen / mixLeft->GetRate(); Mixer *mixer = new Mixer(mono ? 1 : 2, maxBlockLen, false, rate, format); wxProgressDialog *progress = NULL; wxYield(); wxStartTimer(); wxBusyCursor busy; double tt = 0.0; while (tt < totalTime) { double blockTime = maxBlockTime; if (tt + blockTime > totalTime) blockTime = totalTime - tt; int blockLen = int (blockTime * mixLeft->GetRate()); mixer->Clear(); for (int i = 0; i < numWaves; i++) { if (mono) mixer->MixMono(waveArray[i], tt, tt + blockTime); else { switch (waveArray[i]->GetChannel()) { case VTrack::LeftChannel: mixer->MixLeft(waveArray[i], tt, tt + blockTime); break; case VTrack::RightChannel: mixer->MixRight(waveArray[i], tt, tt + blockTime); break; case VTrack::MonoChannel: mixer->MixMono(waveArray[i], tt, tt + blockTime); break; } } } if (mono) { samplePtr buffer = mixer->GetBuffer(); mixLeft->Append(buffer, format, blockLen); } else { samplePtr buffer; buffer = mixer->GetBuffer(0); mixLeft->Append(buffer, format, blockLen); buffer = mixer->GetBuffer(1); mixRight->Append(buffer, format, blockLen); } tt += blockTime; if (!progress && wxGetElapsedTime(false) > 500) { progress = new wxProgressDialog(_("Quick Mix"), _("Mixing tracks"), 1000); } if (progress) { int progressvalue = int (1000 * (tt / totalTime)); progress->Update(progressvalue); } } tracks->Add(mixLeft); if (!mono) tracks->Add(mixRight); delete progress; int elapsedMS = wxGetElapsedTime(); double elapsedTime = elapsedMS * 0.001; double maxTracks = totalTime / (elapsedTime / numWaves); #ifdef __WXGTK__ printf(_(" Tracks: %d\n"), numWaves); printf(_(" Mix length: %f sec\n"), totalTime); printf(_("Elapsed time: %f sec\n"), elapsedTime); printf(_("Max number of tracks to mix in real time: %f\n"), maxTracks); #endif delete waveArray; delete mixer; return true; }
bool ExportPCM(AudacityProject *project, wxString format, bool stereo, wxString fName, bool selectionOnly, double t0, double t1) { wxMessageBox("In process of being rewritten, sorry..."); #if 0 double rate = project->GetRate(); wxWindow *parent = project; TrackList *tracks = project->GetTracks(); int header = SND_HEAD_NONE; #ifdef __WXMAC__ bool trackMarkers = false; #endif if (format == "WAV") header = SND_HEAD_WAVE; else if (format == "AIFF") header = SND_HEAD_AIFF; else if (format == "IRCAM") header = SND_HEAD_IRCAM; else if (format == "AU") header = SND_HEAD_NEXT; #ifdef __WXMAC__ else if (format == "AIFF with track markers") { header = SND_HEAD_AIFF; trackMarkers = true; } #endif // Use snd library to export file snd_node sndfile; snd_node sndbuffer; sndfile.device = SND_DEVICE_FILE; sndfile.write_flag = SND_WRITE; strcpy(sndfile.u.file.filename, (const char *) fName); sndfile.u.file.file = 0; sndfile.u.file.header = header; sndfile.u.file.byte_offset = 0; sndfile.u.file.end_offset = 0; sndfile.u.file.swap = 0; sndfile.format.channels = stereo ? 2 : 1; sndfile.format.mode = SND_MODE_PCM; // SND_MODE_FLOAT sndfile.format.bits = 16; sndfile.format.srate = int (rate + 0.5); int err; long flags = 0; err = snd_open(&sndfile, &flags); if (err) { wxMessageBox("Could not write to file."); return false; } sndbuffer.device = SND_DEVICE_MEM; sndbuffer.write_flag = SND_READ; sndbuffer.u.mem.buffer_max = 0; sndbuffer.u.mem.buffer = 0; sndbuffer.u.mem.buffer_len = 0; sndbuffer.u.mem.buffer_pos = 0; sndbuffer.format.channels = stereo ? 2 : 1; sndbuffer.format.mode = SND_MODE_PCM; // SND_MODE_FLOAT sndbuffer.format.bits = 16; sndbuffer.format.srate = int (rate + 0.5); double timeStep = 10.0; // write in blocks of 10 secs wxProgressDialog *progress = NULL; wxYield(); wxStartTimer(); wxBusyCursor busy; bool cancelling = false; double t = t0; while (t < t1 && !cancelling) { double deltat = timeStep; if (t + deltat > t1) deltat = t1 - t; sampleCount numSamples = int (deltat * rate + 0.5); Mixer *mixer = new Mixer(stereo ? 2 : 1, numSamples, true, rate); wxASSERT(mixer); mixer->Clear(); char *buffer = new char[numSamples * 2 * sndbuffer.format.channels]; wxASSERT(buffer); TrackListIterator iter(tracks); VTrack *tr = iter.First(); while (tr) { if (tr->GetKind() == VTrack::Wave) { if (tr->selected || !selectionOnly) { if (tr->channel == VTrack::MonoChannel) mixer->MixMono((WaveTrack *) tr, t, t + deltat); if (tr->channel == VTrack::LeftChannel) mixer->MixLeft((WaveTrack *) tr, t, t + deltat); if (tr->channel == VTrack::RightChannel) mixer->MixRight((WaveTrack *) tr, t, t + deltat); } } tr = iter.Next(); } sampleType *mixed = mixer->GetBuffer(); long b2 = snd_convert(&sndfile, buffer, // to &sndbuffer, mixed, numSamples); // from snd_write(&sndfile, buffer, b2); t += deltat; if (!progress && wxGetElapsedTime(false) > 500) { wxString message; if (selectionOnly) message = wxString:: Format("Exporting the selected audio as a %s file", (const char *) format); else message = wxString:: Format("Exporting the entire project as a %s file", (const char *) format); progress = new wxProgressDialog("Export", message, 1000, parent, wxPD_CAN_ABORT | wxPD_REMAINING_TIME | wxPD_AUTO_HIDE); } if (progress) { cancelling = !progress->Update(int (((t - t0) * 1000) / (t1 - t0) + 0.5)); } delete mixer; delete[]buffer; } snd_close(&sndfile); #ifdef __WXMAC__ FSSpec spec; wxMacFilename2FSSpec(fName, &spec); if (trackMarkers) { // Export the label track as "CD Spin Doctor" files LabelTrack *labels = NULL; TrackListIterator iter(tracks); VTrack *t = iter.First(); while (t && !labels) { if (t->GetKind() == VTrack::Label) labels = (LabelTrack *) t; t = iter.Next(); } if (labels) { FSpCreateResFile(&spec, 'AIFF', AUDACITY_CREATOR, 0); int resFile = FSpOpenResFile(&spec, fsWrPerm); if (resFile == -1) { int x = ResError(); } if (resFile != -1) { UseResFile(resFile); int numLabels = labels->mLabels.Count(); for (int i = 0; i < numLabels; i++) { int startBlock = (int) (labels->mLabels[i]->t * 75); int lenBlock; if (i < numLabels - 1) lenBlock = (int) ((labels->mLabels[i + 1]->t - labels->mLabels[i]->t) * 75); else lenBlock = (int) ((tracks->GetMaxLen() - labels->mLabels[i]->t) * 75); int startSample = startBlock * 1176 + 54; int lenSample = lenBlock * 1176 + 54; Handle theHandle = NewHandle(50); HLock(theHandle); char *data = (char *) (*theHandle); *(int *) &data[0] = startSample; *(int *) &data[4] = lenSample; *(int *) &data[8] = startBlock; *(int *) &data[12] = lenBlock; *(short *) &data[16] = i + 1; wxString title = labels->mLabels[i]->title; if (title.Length() > 31) title = title.Left(31); data[18] = title.Length(); strcpy(&data[19], (const char *) title); HUnlock(theHandle); AddResource(theHandle, 'SdCv', 128 + i, "\p"); } CloseResFile(resFile); wxMessageBox("Saved track information with file."); } } } FInfo finfo; if (FSpGetFInfo(&spec, &finfo) == noErr) { switch (header) { case SND_HEAD_AIFF: finfo.fdType = 'AIFF'; break; case SND_HEAD_IRCAM: finfo.fdType = 'IRCA'; break; case SND_HEAD_NEXT: finfo.fdType = 'AU '; break; case SND_HEAD_WAVE: finfo.fdType = 'WAVE'; break; } finfo.fdCreator = AUDACITY_CREATOR; FSpSetFInfo(&spec, &finfo); } #endif if (progress) delete progress; return true; #endif return false; }
bool ExportCL(AudacityProject *project, bool stereo, wxString fName, bool selectionOnly, double t0, double t1) { int rate = int(project->GetRate() + 0.5); wxWindow *parent = project; TrackList *tracks = project->GetTracks(); wxString command = gPrefs->Read("/FileFormats/ExternalProgramExportCommand", "lame - '%f'"); command.Replace("%f", fName); /* establish parameters */ int channels = stereo ? 2 : 1; unsigned long totalSamples = (unsigned long)((t1 - t0) * rate + 0.5); unsigned long sampleBytes = totalSamples * channels * SAMPLE_SIZE(int16Sample); double timeStep = 10.0; // write in blocks of 10 secs /* 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; 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; header.dataID[0] = 'd'; header.dataID[1] = 'a'; header.dataID[2] = 't'; header.dataID[3] = 'a'; header.dataLen = sampleBytes; FILE *pipe = popen(command.c_str(), "w"); /* write the header */ fwrite( &header, sizeof(wav_header), 1, pipe ); //sampleCount maxSamples = int (timeStep * rate + 0.5); wxProgressDialog *progress = NULL; wxYield(); wxStartTimer(); wxBusyCursor busy; bool cancelling = false; double t = t0; while (t < t1 && !cancelling) { double deltat = timeStep; if (t + deltat > t1) deltat = t1 - t; sampleCount numSamples = int (deltat * rate + 0.5); Mixer *mixer = new Mixer(channels, numSamples, true, rate, int16Sample); wxASSERT(mixer); mixer->Clear(); char *buffer = new char[numSamples * SAMPLE_SIZE(int16Sample) * channels]; wxASSERT(buffer); TrackListIterator iter(tracks); VTrack *tr = iter.First(); while (tr) { if (tr->GetKind() == VTrack::Wave) { if (tr->GetSelected() || !selectionOnly) { if (tr->GetChannel() == VTrack::MonoChannel) mixer->MixMono((WaveTrack *) tr, t, t + deltat); if (tr->GetChannel() == VTrack::LeftChannel) mixer->MixLeft((WaveTrack *) tr, t, t + deltat); if (tr->GetChannel() == VTrack::RightChannel) mixer->MixRight((WaveTrack *) tr, t, t + deltat); } } tr = iter.Next(); } samplePtr mixed = mixer->GetBuffer(); // 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]); } #endif fwrite( mixed, numSamples * channels * SAMPLE_SIZE(int16Sample), 1, pipe ); t += deltat; if (!progress && wxGetElapsedTime(false) > 500) { wxString message; if (selectionOnly) message = "Exporting the selected audio using command-line encoder"; else message = "Exporting the entire project using command-line encoder"; progress = new wxProgressDialog("Export", message, 1000, parent, wxPD_CAN_ABORT | wxPD_REMAINING_TIME | wxPD_AUTO_HIDE); } if (progress) { cancelling = !progress->Update(int (((t - t0) * 1000) / (t1 - t0) + 0.5)); } delete mixer; delete[]buffer; } pclose( pipe ); if(progress) delete progress; return true; }
bool ExportMP3(AudacityProject *project, bool stereo, wxString fName, bool selectionOnly, double t0, double t1) { double rate = project->GetRate(); wxWindow *parent = project; TrackList *tracks = project->GetTracks(); wxLogNull logNo; /* temporarily disable wxWindows error messages */ bool success = GetMP3Exporter()->FindLibrary(parent); if (!success) return false; success = GetMP3Exporter()->LoadLibrary(); if (!success) { wxMessageBox(_("Could not open MP3 encoding library!")); gPrefs->Write("/MP3/MP3LibPath", wxString("")); return false; } if(!GetMP3Exporter()->ValidLibraryLoaded()) { wxMessageBox(_("Not a valid or supported MP3 encoding library!")); gPrefs->Write("/MP3/MP3LibPath", wxString("")); return false; } /* Open file for writing */ wxFFile outFile(fName, "wb"); if (!outFile.IsOpened()) { wxMessageBox(_("Unable to open target file for writing")); return false; } /* Put ID3 tags at beginning of file */ Tags *tags = project->GetTags(); if (!tags->ShowEditDialog(project, _("Edit the ID3 tags for the MP3 file"))) return false; // used selected "cancel" char *id3buffer; int id3len; bool endOfFile; id3len = tags->ExportID3(&id3buffer, &endOfFile); if (!endOfFile) outFile.Write(id3buffer, id3len); /* Export MP3 using DLL */ long bitrate = gPrefs->Read("/FileFormats/MP3Bitrate", 128); GetMP3Exporter()->SetBitrate(bitrate); sampleCount inSamples = GetMP3Exporter()->InitializeStream(stereo ? 2 : 1, int(rate + 0.5)); double timeStep = (double)inSamples / rate; double t = t0; wxProgressDialog *progress = NULL; wxYield(); wxStartTimer(); wxBusyCursor busy; bool cancelling = false; long bytes; int bufferSize = GetMP3Exporter()->GetOutBufferSize(); unsigned char *buffer = new unsigned char[bufferSize]; wxASSERT(buffer); while (t < t1 && !cancelling) { double deltat = timeStep; bool lastFrame = false; sampleCount numSamples = inSamples; if (t + deltat > t1) { lastFrame = true; deltat = t1 - t; numSamples = int(deltat * rate + 0.5); } Mixer *mixer = new Mixer(stereo ? 2 : 1, numSamples, true, rate, int16Sample); wxASSERT(mixer); mixer->Clear(); TrackListIterator iter(tracks); VTrack *tr = iter.First(); while (tr) { if (tr->GetKind() == VTrack::Wave) { if (tr->GetSelected() || !selectionOnly) { if (tr->GetChannel() == VTrack::MonoChannel) mixer->MixMono((WaveTrack *) tr, t, t + deltat); else if (tr->GetChannel() == VTrack::LeftChannel) mixer->MixLeft((WaveTrack *) tr, t, t + deltat); else if (tr->GetChannel() == VTrack::RightChannel) mixer->MixRight((WaveTrack *) tr, t, t + deltat); } } tr = iter.Next(); } short *mixed = (short *)mixer->GetBuffer(); if(lastFrame) bytes = GetMP3Exporter()->EncodeRemainder(mixed, numSamples, buffer); else bytes = GetMP3Exporter()->EncodeBuffer(mixed, buffer); outFile.Write(buffer, bytes); t += deltat; if (!progress && wxGetElapsedTime(false) > 500) { wxString message; if (selectionOnly) message = wxString::Format(_("Exporting the selected audio as an mp3")); else message = wxString::Format(_("Exporting the entire project as an mp3")); progress = new wxProgressDialog(_("Export"), message, 1000, parent, wxPD_CAN_ABORT | wxPD_REMAINING_TIME | wxPD_AUTO_HIDE); } if (progress) { cancelling = !progress->Update(int (((t - t0) * 1000) / (t1 - t0) + 0.5)); } delete mixer; } bytes = GetMP3Exporter()->FinishStream(buffer); if (bytes) outFile.Write(buffer, bytes); /* Write ID3 tag if it was supposed to be at the end of the file */ if (endOfFile) outFile.Write(id3buffer, id3len); delete[] id3buffer; /* Close file */ outFile.Close(); /* MacOS: set the file type/creator so that the OS knows it's an MP3 file which was created by Audacity */ #ifdef __WXMAC__ FSSpec spec; wxMacFilename2FSSpec(fName, &spec); FInfo finfo; if (FSpGetFInfo(&spec, &finfo) == noErr) { finfo.fdType = 'MP3 '; finfo.fdCreator = AUDACITY_CREATOR; FSpSetFInfo(&spec, &finfo); } #endif if (progress) delete progress; delete[]buffer; return true; }
bool ExportPCM(AudacityProject *project, bool stereo, wxString fName, bool selectionOnly, double t0, double t1) { double rate = project->GetRate(); wxWindow *parent = project; TrackList *tracks = project->GetTracks(); int format = ReadExportFormatPref(); int formatBits = ReadExportFormatBitsPref(); wxString formatStr; SF_INFO info; SNDFILE *sf; int err; formatStr = sf_header_name(format & SF_FORMAT_TYPEMASK); // Use libsndfile to export file info.samplerate = (unsigned int)(rate + 0.5); info.samples = (unsigned int)((t1 - t0)*rate + 0.5); info.channels = stereo? 2: 1; info.pcmbitwidth = formatBits; info.format = format; info.sections = 1; info.seekable = 0; // If we can't export exactly the format they requested, // try the default format for that header type, and try // 16-bit samples. if (!sf_format_check(&info)) info.format = (info.format & SF_FORMAT_TYPEMASK); if (!sf_format_check(&info)) info.pcmbitwidth = 16; if (!sf_format_check(&info)) { wxMessageBox(_("Cannot export audio in this format.")); return false; } sf = sf_open_write((const char *)fName, &info); if (!sf) { wxMessageBox(wxString::Format(_("Cannot export audio to %s"), (const char *)fName)); return false; } double timeStep = 10.0; // write in blocks of 10 secs wxProgressDialog *progress = NULL; wxYield(); wxStartTimer(); wxBusyCursor busy; bool cancelling = false; double t = t0; while (t < t1 && !cancelling) { double deltat = timeStep; if (t + deltat > t1) deltat = t1 - t; sampleCount numSamples = int (deltat * rate + 0.5); Mixer *mixer = new Mixer(stereo ? 2 : 1, numSamples, true, rate); wxASSERT(mixer); mixer->Clear(); TrackListIterator iter(tracks); VTrack *tr = iter.First(); while (tr) { if (tr->GetKind() == VTrack::Wave) { if (tr->GetSelected() || !selectionOnly) { if (tr->GetChannel() == VTrack::MonoChannel) mixer->MixMono((WaveTrack *) tr, t, t + deltat); if (tr->GetChannel() == VTrack::LeftChannel) mixer->MixLeft((WaveTrack *) tr, t, t + deltat); if (tr->GetChannel() == VTrack::RightChannel) mixer->MixRight((WaveTrack *) tr, t, t + deltat); } } tr = iter.Next(); } sampleType *mixed = mixer->GetBuffer(); sf_writef_short(sf, mixed, numSamples); t += deltat; if (!progress && wxGetElapsedTime(false) > 500) { wxString message; if (selectionOnly) message = wxString:: Format(_("Exporting the selected audio as a %s file"), (const char *) formatStr); else message = wxString:: Format(_("Exporting the entire project as a %s file"), (const char *) formatStr); progress = new wxProgressDialog(_("Export"), message, 1000, parent, wxPD_CAN_ABORT | wxPD_REMAINING_TIME | wxPD_AUTO_HIDE); } if (progress) { cancelling = !progress->Update(int (((t - t0) * 1000) / (t1 - t0) + 0.5)); } delete mixer; } err = sf_close(sf); if (err) { char buffer[1000]; sf_error_str(sf, buffer, 1000); wxMessageBox(wxString::Format (_("Error (file may not have been written): %s"), buffer)); } #ifdef __WXMAC__ FSSpec spec; wxMacFilename2FSSpec(fName, &spec); FInfo finfo; if (FSpGetFInfo(&spec, &finfo) == noErr) { finfo.fdType = sf_header_mactype(format & SF_FORMAT_TYPEMASK); finfo.fdCreator = AUDACITY_CREATOR; FSpSetFInfo(&spec, &finfo); } #endif if (progress) delete progress; return true; }
void AudioIO::FillBuffers() { unsigned int numEmpty = 0; unsigned int i; // Playback buffers for(i=0; i<mNumOutBuffers; i++) { if (mOutBuffer[i].ID == 0) numEmpty++; } if (numEmpty > (mNumOutBuffers/2)) { sampleCount block = numEmpty * mBufferSize; double deltat = block / mRate; if (mT + deltat > mT1) { deltat = mT1 - mT; if(deltat < 0.0) return; block = (sampleCount)(deltat * mRate + 0.5); } Mixer *mixer = new Mixer(mNumOutChannels, block, true, mRate, mFormat); mixer->UseVolumeSlider(mProject->GetControlToolBar()); mixer->Clear(); TrackListIterator iter2(mTracks); int numSolo = 0; Track *vt = iter2.First(); while (vt) { if (vt->GetKind() == Track::Wave && vt->GetSolo()) numSolo++; vt = iter2.Next(); } TrackListIterator iter(mTracks); vt = iter.First(); while (vt) { if (vt->GetKind() == Track::Wave) { Track *mt = vt; // We want to extract mute and solo information from // the top of the two tracks if they're linked // (i.e. a stereo pair only has one set of mute/solo buttons) Track *partner = mTracks->GetLink(vt); if (partner && !vt->GetLinked()) mt = partner; else mt = vt; // Cut if somebody else is soloing if (numSolo>0 && !mt->GetSolo()) { vt = iter.Next(); continue; } // Cut if we're muted (unless we're soloing) if (mt->GetMute() && !mt->GetSolo()) { vt = iter.Next(); continue; } WaveTrack *t = (WaveTrack *) vt; switch (t->GetChannel()) { case Track::LeftChannel: mixer->MixLeft(t, mT, mT + deltat); break; case Track::RightChannel: mixer->MixRight(t, mT, mT + deltat); break; case Track::MonoChannel: mixer->MixMono(t, mT, mT + deltat); break; } } vt = iter.Next(); } // Copy the mixed samples into the buffers samplePtr outbytes = mixer->GetBuffer(); for(i=0; i<mNumOutBuffers && block>0; i++) if (mOutBuffer[i].ID == 0) { sampleCount count; if (block > mBufferSize) count = mBufferSize; else count = block; memcpy(mOutBuffer[i].data, outbytes, count*mNumOutChannels*SAMPLE_SIZE(mFormat)); block -= count; outbytes += (count*mNumOutChannels*SAMPLE_SIZE(mFormat)); mOutBuffer[i].len = count; mOutBuffer[i].ID = mOutID; mOutID++; } delete mixer; mT += deltat; } // Recording buffers unsigned int numFull = 0; unsigned int f, c; // loop counters sampleCount flatLen; for(i=0; i<mNumInBuffers; i++) { if (mInBuffer[i].ID != 0) numFull++; } if (numFull > 8) { samplePtr *flat = new samplePtr[mNumInChannels]; for(i=0; i<mNumInChannels; i++) flat[i] = NewSamples(numFull * mBufferSize, mFormat); flatLen = 0; for(f=0; f<numFull; f++) { int minID = mInID+1; int minIndex = 0; for(i=0; i<mNumInBuffers; i++) if (mInBuffer[i].ID > 0 && mInBuffer[i].ID < minID) { minIndex = i; minID = mInBuffer[i].ID; } switch(mFormat) { case floatSample: int j; for(j=0; j<mInBuffer[minIndex].len; j++) for(c=0; c<mNumInChannels; c++) { ((float *)flat[c])[flatLen+j] = ((float *)mInBuffer[minIndex].data)[j*mNumInChannels + c]; } break; default: wxASSERT(0); } flatLen += mInBuffer[minIndex].len; mInBuffer[minIndex].ID = 0; } for(i=0; i<mNumInChannels; i++) mInTracks[i]->Append(flat[i], mFormat, flatLen); for(i=0; i<mNumInChannels; i++) DeleteSamples(flat[i]); delete[] flat; mProject->RedrawProject(); } }