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