int ExportFLAC::Export(AudacityProject *project, int numChannels, const wxString &fName, bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata, int WXUNUSED(subformat)) { double rate = project->GetRate(); TrackList *tracks = project->GetTracks(); wxLogNull logNo; // temporarily disable wxWidgets error messages int updateResult = eProgressSuccess; int levelPref; gPrefs->Read(wxT("/FileFormats/FLACLevel"), &levelPref, 5); wxString bitDepthPref = gPrefs->Read(wxT("/FileFormats/FLACBitDepth"), wxT("16")); FLAC::Encoder::File encoder; #ifdef LEGACY_FLAC encoder.set_filename(OSOUTPUT(fName)); #endif encoder.set_channels(numChannels); encoder.set_sample_rate(lrint(rate)); // See note in GetMetadata() about a bug in libflac++ 1.1.2 if (!GetMetadata(project, metadata)) { return false; } if (mMetadata) { encoder.set_metadata(&mMetadata, 1); } sampleFormat format; if (bitDepthPref == wxT("24")) { format = int24Sample; encoder.set_bits_per_sample(24); } else { //convert float to 16 bits format = int16Sample; encoder.set_bits_per_sample(16); } // Duplicate the flac command line compression levels if (levelPref < 0 || levelPref > 8) { levelPref = 5; } encoder.set_do_exhaustive_model_search(flacLevels[levelPref].do_exhaustive_model_search); encoder.set_do_escape_coding(flacLevels[levelPref].do_escape_coding); if (numChannels != 2) { encoder.set_do_mid_side_stereo(false); encoder.set_loose_mid_side_stereo(false); } else { encoder.set_do_mid_side_stereo(flacLevels[levelPref].do_mid_side_stereo); encoder.set_loose_mid_side_stereo(flacLevels[levelPref].loose_mid_side_stereo); } encoder.set_qlp_coeff_precision(flacLevels[levelPref].qlp_coeff_precision); encoder.set_min_residual_partition_order(flacLevels[levelPref].min_residual_partition_order); encoder.set_max_residual_partition_order(flacLevels[levelPref].max_residual_partition_order); encoder.set_rice_parameter_search_dist(flacLevels[levelPref].rice_parameter_search_dist); encoder.set_max_lpc_order(flacLevels[levelPref].max_lpc_order); #ifdef LEGACY_FLAC encoder.init(); #else wxFFile f; // will be closed when it goes out of scope if (!f.Open(fName, wxT("w+b"))) { wxMessageBox(wxString::Format(_("FLAC export couldn't open %s"), fName.c_str())); return false; } // Even though there is an init() method that takes a filename, use the one that // takes a file handle because wxWidgets can open a file with a Unicode name and // libflac can't (under Windows). int status = encoder.init(f.fp()); if (status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) { wxMessageBox(wxString::Format(_("FLAC encoder failed to initialize\nStatus: %d"), status)); return false; } #endif if (mMetadata) { ::FLAC__metadata_object_delete(mMetadata); } int numWaveTracks; WaveTrack **waveTracks; tracks->GetWaveTracks(selectionOnly, &numWaveTracks, &waveTracks); Mixer *mixer = CreateMixer(numWaveTracks, waveTracks, tracks->GetTimeTrack(), t0, t1, numChannels, SAMPLES_PER_RUN, false, rate, format, true, mixerSpec); delete [] waveTracks; int i, j; FLAC__int32 **tmpsmplbuf = new FLAC__int32*[numChannels]; for (i = 0; i < numChannels; i++) { tmpsmplbuf[i] = (FLAC__int32 *) calloc(SAMPLES_PER_RUN, sizeof(FLAC__int32)); } { ProgressDialog progress(wxFileName(fName).GetName(), selectionOnly ? _("Exporting the selected audio as FLAC") : _("Exporting the entire project as FLAC")); while (updateResult == eProgressSuccess) { sampleCount samplesThisRun = mixer->Process(SAMPLES_PER_RUN); if (samplesThisRun == 0) { //stop encoding break; } else { for (i = 0; i < numChannels; i++) { samplePtr mixed = mixer->GetBuffer(i); if (format == int24Sample) { for (j = 0; j < samplesThisRun; j++) { tmpsmplbuf[i][j] = ((int *)mixed)[j]; } } else { for (j = 0; j < samplesThisRun; j++) { tmpsmplbuf[i][j] = ((short *)mixed)[j]; } } } encoder.process(tmpsmplbuf, samplesThisRun); } updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0); } f.Detach(); // libflac closes the file encoder.finish(); } for (i = 0; i < numChannels; i++) { free(tmpsmplbuf[i]); } delete mixer; delete[] tmpsmplbuf; return updateResult; }
int ExportOGG::Export(AudacityProject *project, int numChannels, const wxString &fName, bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata, int WXUNUSED(subformat)) { double rate = project->GetRate(); const TrackList *tracks = project->GetTracks(); double quality = (gPrefs->Read(wxT("/FileFormats/OggExportQuality"), 50)/(float)100.0); wxLogNull logNo; // temporarily disable wxWidgets error messages int updateResult = eProgressSuccess; int eos = 0; FileIO outFile(fName, FileIO::Output); 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, numChannels, int(rate + 0.5), quality); // Retrieve tags if (!FillComment(project, &comment, metadata)) { return false; } // 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 while (ogg_stream_flush(&stream, &page)) { outFile.Write(page.header, page.header_len); outFile.Write(page.body, page.body_len); } const WaveTrackConstArray waveTracks = tracks->GetWaveTrackConstArray(selectionOnly, false); { auto mixer = CreateMixer(waveTracks, tracks->GetTimeTrack(), t0, t1, numChannels, SAMPLES_PER_RUN, false, rate, floatSample, true, mixerSpec); ProgressDialog progress(wxFileName(fName).GetName(), selectionOnly ? _("Exporting the selected audio as Ogg Vorbis") : _("Exporting the entire project as Ogg Vorbis")); while (updateResult == eProgressSuccess && !eos) { float **vorbis_buffer = vorbis_analysis_buffer(&dsp, SAMPLES_PER_RUN); sampleCount samplesThisRun = mixer->Process(SAMPLES_PER_RUN); if (samplesThisRun == 0) { // Tell the library that we wrote 0 bytes - signalling the end. vorbis_analysis_wrote(&dsp, 0); } else { for (int i = 0; i < numChannels; i++) { float *temp = (float *)mixer->GetBuffer(i); memcpy(vorbis_buffer[i], temp, 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); // From vorbis-tools-1.0/oggenc/encode.c: // If we've gone over a page boundary, we can do actual output, // so do so (for however many pages are available). while (!eos) { int result = ogg_stream_pageout(&stream, &page); if (!result) { break; } outFile.Write(page.header, page.header_len); outFile.Write(page.body, page.body_len); if (ogg_page_eos(&page)) { eos = 1; } } } } 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(); return updateResult; }
int ExportMP2::Export(AudacityProject *project, int channels, const wxString &fName, bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata, int WXUNUSED(subformat)) { bool stereo = (channels == 2); long bitrate = gPrefs->Read(wxT("/FileFormats/MP2Bitrate"), 160); double rate = project->GetRate(); const TrackList *tracks = project->GetTracks(); wxLogNull logNo; /* temporarily disable wxWidgets error messages */ twolame_options *encodeOptions; encodeOptions = twolame_init(); twolame_set_in_samplerate(encodeOptions, (int)(rate + 0.5)); twolame_set_out_samplerate(encodeOptions, (int)(rate + 0.5)); twolame_set_bitrate(encodeOptions, bitrate); twolame_set_num_channels(encodeOptions, stereo ? 2 : 1); if (twolame_init_params(encodeOptions) != 0) { wxMessageBox(_("Cannot export MP2 with this sample rate and bit rate"), _("Error"), wxICON_STOP); twolame_close(&encodeOptions); return false; } // Put ID3 tags at beginning of file if (metadata == NULL) metadata = project->GetTags(); FileIO outFile(fName, FileIO::Output); if (!outFile.IsOpened()) { wxMessageBox(_("Unable to open target file for writing")); twolame_close(&encodeOptions); return false; } char *id3buffer = NULL; int id3len; bool endOfFile; id3len = AddTags(project, &id3buffer, &endOfFile, metadata); if (id3len && !endOfFile) outFile.Write(id3buffer, id3len); // Values taken from the twolame simple encoder sample const int pcmBufferSize = 9216 / 2; // number of samples const int mp2BufferSize = 16384; // bytes // We allocate a buffer which is twice as big as the // input buffer, which should always be enough. // We have to multiply by 4 because one sample is 2 bytes wide! unsigned char* mp2Buffer = new unsigned char[mp2BufferSize]; const WaveTrackConstArray waveTracks = tracks->GetWaveTrackConstArray(selectionOnly, false); int updateResult = eProgressSuccess; { auto mixer = CreateMixer(waveTracks, tracks->GetTimeTrack(), t0, t1, stereo ? 2 : 1, pcmBufferSize, true, rate, int16Sample, true, mixerSpec); ProgressDialog progress(wxFileName(fName).GetName(), selectionOnly ? wxString::Format(_("Exporting selected audio at %ld kbps"), bitrate) : wxString::Format(_("Exporting entire file at %ld kbps"), bitrate)); while (updateResult == eProgressSuccess) { sampleCount pcmNumSamples = mixer->Process(pcmBufferSize); if (pcmNumSamples == 0) break; short *pcmBuffer = (short *)mixer->GetBuffer(); int mp2BufferNumBytes = twolame_encode_buffer_interleaved( encodeOptions, pcmBuffer, pcmNumSamples, mp2Buffer, mp2BufferSize); outFile.Write(mp2Buffer, mp2BufferNumBytes); updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0); } } int mp2BufferNumBytes = twolame_encode_flush( encodeOptions, mp2Buffer, mp2BufferSize); if (mp2BufferNumBytes > 0) outFile.Write(mp2Buffer, mp2BufferNumBytes); twolame_close(&encodeOptions); delete[] mp2Buffer; /* Write ID3 tag if it was supposed to be at the end of the file */ if (id3len && endOfFile) outFile.Write(id3buffer, id3len); if (id3buffer) { free(id3buffer); } /* Close file */ outFile.Close(); return updateResult; }
int ExportFFmpeg::Export(AudacityProject *project, int channels, const wxString &fName, bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata, int subformat) { if (!CheckFFmpegPresence()) return false; mChannels = channels; // subformat index may not correspond directly to fmts[] index, convert it mSubFormat = AdjustFormatIndex(subformat); if (channels > ExportFFmpegOptions::fmts[mSubFormat].maxchannels) { wxMessageBox( wxString::Format( _("Attempted to export %d channels, but maximum number of channels for selected output format is %d"), channels, ExportFFmpegOptions::fmts[mSubFormat].maxchannels), _("Error")); return false; } mName = fName; TrackList *tracks = project->GetTracks(); bool ret = true; if (mSubFormat >= FMT_LAST) return false; wxString shortname(ExportFFmpegOptions::fmts[mSubFormat].shortname); if (mSubFormat == FMT_OTHER) shortname = gPrefs->Read(wxT("/FileFormats/FFmpegFormat"),wxT("matroska")); ret = Init(shortname.mb_str(),project, metadata, subformat); if (!ret) return false; int pcmBufferSize = 1024; int numWaveTracks; WaveTrack **waveTracks; tracks->GetWaveTracks(selectionOnly, &numWaveTracks, &waveTracks); Mixer *mixer = CreateMixer(numWaveTracks, waveTracks, tracks->GetTimeTrack(), t0, t1, channels, pcmBufferSize, true, mSampleRate, int16Sample, true, mixerSpec); delete[] waveTracks; int updateResult = eProgressSuccess; { ProgressDialog progress(wxFileName(fName).GetName(), selectionOnly ? wxString::Format(_("Exporting selected audio as %s"), ExportFFmpegOptions::fmts[mSubFormat].description) : wxString::Format(_("Exporting entire file as %s"), ExportFFmpegOptions::fmts[mSubFormat].description)); while (updateResult == eProgressSuccess) { sampleCount pcmNumSamples = mixer->Process(pcmBufferSize); if (pcmNumSamples == 0) break; short *pcmBuffer = (short *)mixer->GetBuffer(); EncodeAudioFrame(pcmBuffer, (pcmNumSamples)*sizeof(int16_t)*mChannels); updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0); } } delete mixer; Finalize(); return updateResult; }