Audacity  3.0.3
ExportOGG.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ExportOGG.cpp
6 
7  Joshua Haberman
8 
9  This program is distributed under the GNU General Public License, version 2.
10  A copy of this license is included with this source.
11 
12  Portions from vorbis-tools, copyright 2000-2002 Michael Smith
13  <[email protected]>; Vorbize, Kenneth Arnold <[email protected]>;
14  and libvorbis examples, Monty <[email protected]>
15 
16 **********************************************************************/
17 
18 
19 
20 #ifdef USE_LIBVORBIS
21 
22 #include "Export.h"
23 
24 #include <wx/log.h>
25 #include <wx/slider.h>
26 #include <wx/stream.h>
27 
28 #include <vorbis/vorbisenc.h>
29 
30 #include "../FileIO.h"
31 #include "../ProjectSettings.h"
32 #include "../Mix.h"
33 #include "../Prefs.h"
34 #include "../ShuttleGui.h"
35 
36 #include "../Tags.h"
37 #include "../Track.h"
38 #include "../widgets/AudacityMessageBox.h"
39 #include "../widgets/ProgressDialog.h"
40 
41 //----------------------------------------------------------------------------
42 // ExportOGGOptions
43 //----------------------------------------------------------------------------
44 
45 class ExportOGGOptions final : public wxPanelWrapper
46 {
47 public:
48 
49  ExportOGGOptions(wxWindow *parent, int format);
50  virtual ~ExportOGGOptions();
51 
53  bool TransferDataToWindow() override;
54  bool TransferDataFromWindow() override;
55 
56 private:
57 
59 };
60 
63 ExportOGGOptions::ExportOGGOptions(wxWindow *parent, int WXUNUSED(format))
64 : wxPanelWrapper(parent, wxID_ANY)
65 {
66  mOggQualityUnscaled = gPrefs->Read(wxT("/FileFormats/OggExportQuality"),50)/10;
67 
70 
72 }
73 
75 {
77 }
78 
82 {
83  S.StartVerticalLay();
84  {
85  S.StartHorizontalLay(wxEXPAND);
86  {
87  S.SetSizerProportion(1);
88  S.StartMultiColumn(2, wxCENTER);
89  {
90  S.SetStretchyCol(1);
91  S.Prop(1).TieSlider(
92  XXO("Quality:"), mOggQualityUnscaled, 10);
93  }
94  S.EndMultiColumn();
95  }
96  S.EndHorizontalLay();
97  }
98  S.EndVerticalLay();
99 }
100 
104 {
105  return true;
106 }
107 
111 {
112  ShuttleGui S(this, eIsSavingToPrefs);
114 
115  gPrefs->Write(wxT("/FileFormats/OggExportQuality"),mOggQualityUnscaled * 10);
116  gPrefs->Flush();
117 
118  return true;
119 }
120 
121 //----------------------------------------------------------------------------
122 // ExportOGG
123 //----------------------------------------------------------------------------
124 
125 #define SAMPLES_PER_RUN 8192u
126 
127 class ExportOGG final : public ExportPlugin
128 {
129 public:
130 
131  ExportOGG();
132 
133  // Required
134  void OptionsCreate(ShuttleGui &S, int format) override;
135 
137  std::unique_ptr<ProgressDialog> &pDialog,
138  unsigned channels,
139  const wxFileNameWrapper &fName,
140  bool selectedOnly,
141  double t0,
142  double t1,
143  MixerSpec *mixerSpec = NULL,
144  const Tags *metadata = NULL,
145  int subformat = 0) override;
146 
147 private:
148 
149  bool FillComment(AudacityProject *project, vorbis_comment *comment, const Tags *metadata);
150 };
151 
153 : ExportPlugin()
154 {
155  AddFormat();
156  SetFormat(wxT("OGG"),0);
157  AddExtension(wxT("ogg"),0);
158  SetMaxChannels(255,0);
159  SetCanMetaData(true,0);
160  SetDescription(XO("Ogg Vorbis Files"),0);
161 }
162 
164  std::unique_ptr<ProgressDialog> &pDialog,
165  unsigned numChannels,
166  const wxFileNameWrapper &fName,
167  bool selectionOnly,
168  double t0,
169  double t1,
170  MixerSpec *mixerSpec,
171  const Tags *metadata,
172  int WXUNUSED(subformat))
173 {
174  double rate = ProjectSettings::Get( *project ).GetRate();
175  const auto &tracks = TrackList::Get( *project );
176  double quality = (gPrefs->Read(wxT("/FileFormats/OggExportQuality"), 50)/(float)100.0);
177 
178  wxLogNull logNo; // temporarily disable wxWidgets error messages
179  auto updateResult = ProgressResult::Success;
180  int eos = 0;
181 
182  FileIO outFile(fName, FileIO::Output);
183 
184  if (!outFile.IsOpened()) {
185  AudacityMessageBox( XO("Unable to open target file for writing") );
187  }
188 
189  // All the Ogg and Vorbis encoding data
190  ogg_stream_state stream;
191  ogg_page page;
192  ogg_packet packet;
193 
194  vorbis_info info;
195  vorbis_comment comment;
196  vorbis_dsp_state dsp;
197  vorbis_block block;
198 
199 
200  auto cleanup1 = finally( [&] {
201  vorbis_info_clear(&info);
202  } );
203 
204 
205  // Many of the library functions called below return 0 for success and
206  // various nonzero codes for failure.
207 
208  // Encoding setup
209  vorbis_info_init(&info);
210  if (vorbis_encode_init_vbr(&info, numChannels, (int)(rate + 0.5), quality)) {
211  // TODO: more precise message
212  AudacityMessageBox( XO("Unable to export - rate or quality problem") );
214  }
215 
216  auto cleanup2 = finally( [&] {
217  ogg_stream_clear(&stream);
218 
219  vorbis_block_clear(&block);
220  vorbis_dsp_clear(&dsp);
221  vorbis_comment_clear(&comment);
222  } );
223 
224  // Retrieve tags
225  if (!FillComment(project, &comment, metadata)) {
226  AudacityMessageBox( XO("Unable to export - problem with metadata") );
228  }
229 
230  // Set up analysis state and auxiliary encoding storage
231  if (vorbis_analysis_init(&dsp, &info) ||
232  vorbis_block_init(&dsp, &block)) {
233  AudacityMessageBox( XO("Unable to export - problem initialising") );
235  }
236 
237  // Set up packet->stream encoder. According to encoder example,
238  // a random serial number makes it more likely that you can make
239  // chained streams with concatenation.
240  srand(time(NULL));
241  if (ogg_stream_init(&stream, rand())) {
242  AudacityMessageBox( XO("Unable to export - problem creating stream") );
244  }
245 
246  // First we need to write the required headers:
247  // 1. The Ogg bitstream header, which contains codec setup params
248  // 2. The Vorbis comment header
249  // 3. The bitstream codebook.
250  //
251  // After we create those our responsibility is complete, libvorbis will
252  // take care of any other ogg bitstream constraints (again, according
253  // to the example encoder source)
254  ogg_packet bitstream_header;
255  ogg_packet comment_header;
256  ogg_packet codebook_header;
257 
258  if(vorbis_analysis_headerout(&dsp, &comment, &bitstream_header, &comment_header,
259  &codebook_header) ||
260  // Place these headers into the stream
261  ogg_stream_packetin(&stream, &bitstream_header) ||
262  ogg_stream_packetin(&stream, &comment_header) ||
263  ogg_stream_packetin(&stream, &codebook_header)) {
264  AudacityMessageBox( XO("Unable to export - problem with packets") );
266  }
267 
268  // Flushing these headers now guarantees that audio data will
269  // start on a NEW page, which apparently makes streaming easier
270  while (ogg_stream_flush(&stream, &page)) {
271  if ( outFile.Write(page.header, page.header_len).GetLastError() ||
272  outFile.Write(page.body, page.body_len).GetLastError()) {
273  AudacityMessageBox( XO("Unable to export - problem with file") );
275  }
276  }
277 
278  {
279  auto mixer = CreateMixer(tracks, selectionOnly,
280  t0, t1,
281  numChannels, SAMPLES_PER_RUN, false,
282  rate, floatSample, mixerSpec);
283 
284  InitProgress( pDialog, fName,
285  selectionOnly
286  ? XO("Exporting the selected audio as Ogg Vorbis")
287  : XO("Exporting the audio as Ogg Vorbis") );
288  auto &progress = *pDialog;
289 
290  while (updateResult == ProgressResult::Success && !eos) {
291  float **vorbis_buffer = vorbis_analysis_buffer(&dsp, SAMPLES_PER_RUN);
292  auto samplesThisRun = mixer->Process(SAMPLES_PER_RUN);
293 
294  int err;
295  if (samplesThisRun == 0) {
296  // Tell the library that we wrote 0 bytes - signalling the end.
297  err = vorbis_analysis_wrote(&dsp, 0);
298  }
299  else {
300 
301  for (size_t i = 0; i < numChannels; i++) {
302  float *temp = (float *)mixer->GetBuffer(i);
303  memcpy(vorbis_buffer[i], temp, sizeof(float)*SAMPLES_PER_RUN);
304  }
305 
306  // tell the encoder how many samples we have
307  err = vorbis_analysis_wrote(&dsp, samplesThisRun);
308  }
309 
310  // I don't understand what this call does, so here is the comment
311  // from the example, verbatim:
312  //
313  // vorbis does some data preanalysis, then divvies up blocks
314  // for more involved (potentially parallel) processing. Get
315  // a single block for encoding now
316  while (!err && vorbis_analysis_blockout(&dsp, &block) == 1) {
317 
318  // analysis, assume we want to use bitrate management
319  err = vorbis_analysis(&block, NULL);
320  if (!err)
321  err = vorbis_bitrate_addblock(&block);
322 
323  while (!err && vorbis_bitrate_flushpacket(&dsp, &packet)) {
324 
325  // add the packet to the bitstream
326  err = ogg_stream_packetin(&stream, &packet);
327 
328  // From vorbis-tools-1.0/oggenc/encode.c:
329  // If we've gone over a page boundary, we can do actual output,
330  // so do so (for however many pages are available).
331 
332  while (!err && !eos) {
333  int result = ogg_stream_pageout(&stream, &page);
334  if (!result) {
335  break;
336  }
337 
338  if ( outFile.Write(page.header, page.header_len).GetLastError() ||
339  outFile.Write(page.body, page.body_len).GetLastError()) {
340  // TODO: more precise message
343  }
344 
345  if (ogg_page_eos(&page)) {
346  eos = 1;
347  }
348  }
349  }
350  }
351 
352  if (err) {
353  updateResult = ProgressResult::Cancelled;
354  // TODO: more precise message
355  ShowExportErrorDialog("OGG:355");
356  break;
357  }
358 
359  updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
360  }
361  }
362 
363  if ( !outFile.Close() ) {
364  updateResult = ProgressResult::Cancelled;
365  // TODO: more precise message
366  ShowExportErrorDialog("OGG:366");
367  }
368 
369  return updateResult;
370 }
371 
373 {
375 }
376 
377 bool ExportOGG::FillComment(AudacityProject *project, vorbis_comment *comment, const Tags *metadata)
378 {
379  // Retrieve tags from project if not over-ridden
380  if (metadata == NULL)
381  metadata = &Tags::Get( *project );
382 
383  vorbis_comment_init(comment);
384 
385  wxString n;
386  for (const auto &pair : metadata->GetRange()) {
387  n = pair.first;
388  const auto &v = pair.second;
389  if (n == TAG_YEAR) {
390  n = wxT("DATE");
391  }
392  vorbis_comment_add_tag(comment,
393  (char *) (const char *) n.mb_str(wxConvUTF8),
394  (char *) (const char *) v.mb_str(wxConvUTF8));
395  }
396 
397  return true;
398 }
399 
401  []{ return std::make_unique< ExportOGG >(); }
402 };
403 
404 #endif // USE_LIBVORBIS
405 
ExportOGGOptions
Definition: ExportOGG.cpp:46
ExportOGGOptions::mOggQualityUnscaled
int mOggQualityUnscaled
Definition: ExportOGG.cpp:58
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1177
ExportOGG
Definition: ExportOGG.cpp:128
ExportOGG::FillComment
bool FillComment(AudacityProject *project, vorbis_comment *comment, const Tags *metadata)
Definition: ExportOGG.cpp:377
ExportOGGOptions::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S)
Definition: ExportOGG.cpp:81
wxFileNameWrapper
Definition: wxFileNameWrapper.h:21
ExportOGG::OptionsCreate
void OptionsCreate(ShuttleGui &S, int format) override
Definition: ExportOGG.cpp:372
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:67
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: AudacityMessageBox.h:20
ExportPlugin::AddExtension
void AddExtension(const FileExtension &extension, int index)
Definition: Export.cpp:126
ShowExportErrorDialog
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption)
Definition: Export.cpp:1518
wxPanelWrapper
Definition: wxPanelWrapper.h:41
Tags
ID3 Tags (for MP3)
Definition: Tags.h:74
Tags::GetRange
Iterators GetRange() const
Definition: Tags.cpp:480
ShuttleGuiBase::TieSlider
wxSlider * TieSlider(const TranslatableString &Prompt, int &pos, const int max, const int min=0)
Definition: ShuttleGui.cpp:1661
XO
#define XO(s)
Definition: Internat.h:32
ProgressResult::Cancelled
@ Cancelled
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:39
ShuttleGuiBase::EndMultiColumn
void EndMultiColumn()
Definition: ShuttleGui.cpp:1212
FileIO::Output
@ Output
Definition: FileIO.h:27
ExportOGGOptions::ExportOGGOptions
ExportOGGOptions(wxWindow *parent, int format)
Definition: ExportOGG.cpp:63
floatSample
@ floatSample
Definition: Types.h:714
ExportOGGOptions::TransferDataToWindow
bool TransferDataToWindow() override
Definition: ExportOGG.cpp:103
FileIO::Close
bool Close()
Definition: FileIO.cpp:55
Tags::Get
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:236
ShuttleGuiBase::SetSizerProportion
void SetSizerProportion(int iProp)
Definition: ShuttleGui.h:498
ShowDiskFullExportErrorDialog
void ShowDiskFullExportErrorDialog(const wxFileNameWrapper &fileName)
Definition: Export.cpp:1529
XXO
#define XXO(s)
Definition: Internat.h:45
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1170
ExportPlugin::InitProgress
static void InitProgress(std::unique_ptr< ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
Definition: Export.cpp:250
ProgressResult
ProgressResult
Definition: ProgressDialog.h:33
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1160
ProgressResult::Success
@ Success
ShuttleGuiBase::StartMultiColumn
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:1203
ExportPlugin::SetFormat
void SetFormat(const wxString &format, int index)
Definition: Export.cpp:116
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1196
format
int format
Definition: ExportPCM.cpp:54
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:503
FileIO::Write
wxOutputStream & Write(const void *buffer, size_t size)
Definition: FileIO.cpp:78
Export.h
TAG_YEAR
#define TAG_YEAR
Definition: Tags.h:64
ExportPlugin::SetDescription
void SetDescription(const TranslatableString &description, int index)
Definition: Export.cpp:121
ExportOGGOptions::~ExportOGGOptions
virtual ~ExportOGGOptions()
Definition: ExportOGG.cpp:74
ShuttleGui::Prop
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:732
ExportPlugin::SetMaxChannels
void SetMaxChannels(unsigned maxchannels, unsigned index)
Definition: Export.cpp:141
eIsSavingToPrefs
@ eIsSavingToPrefs
Definition: ShuttleGui.h:46
SAMPLES_PER_RUN
#define SAMPLES_PER_RUN
Definition: ExportOGG.cpp:125
Exporter::RegisteredExportPlugin
Definition: Export.h:175
ExportOGG::ExportOGG
ExportOGG()
Definition: ExportOGG.cpp:152
ShuttleGuiBase::AddWindow
wxWindow * AddWindow(wxWindow *pWindow)
Definition: ShuttleGui.cpp:292
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:151
FileIO::IsOpened
bool IsOpened()
Definition: FileIO.cpp:50
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:112
ExportPlugin::CreateMixer
std::unique_ptr< Mixer > CreateMixer(const TrackList &tracks, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerSpec *mixerSpec)
Definition: Export.cpp:222
ProjectSettings::GetRate
double GetRate() const
Definition: ProjectSettings.cpp:166
ExportPlugin::SetCanMetaData
void SetCanMetaData(bool canmetadata, int index)
Definition: Export.cpp:146
ExportOGG::Export
ProgressResult Export(AudacityProject *project, std::unique_ptr< ProgressDialog > &pDialog, unsigned channels, const wxFileNameWrapper &fName, bool selectedOnly, double t0, double t1, MixerSpec *mixerSpec=NULL, const Tags *metadata=NULL, int subformat=0) override
called to export audio into a file.
Definition: ExportOGG.cpp:163
ExportPlugin::AddFormat
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
Definition: Export.cpp:100
FileIO
Definition: FileIO.h:22
MixerSpec
Class used with Mixer.
Definition: Mix.h:57
eIsCreatingFromPrefs
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:45
ExportPlugin
Definition: Export.h:65
safenew
#define safenew
Definition: MemoryX.h:8
ShuttleGuiBase::SetStretchyCol
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:195
sRegisteredPlugin
static Exporter::RegisteredExportPlugin sRegisteredPlugin
Definition: ExportOGG.cpp:400
ExportOGGOptions::TransferDataFromWindow
bool TransferDataFromWindow() override
Definition: ExportOGG.cpp:110
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:638