Audacity  3.0.3
ImportOGG.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ImportOGG.cpp
6 
7  Joshua Haberman
8  Leland Lucius
9 
10 *//****************************************************************//****************************************************************//*******************************************************************/
30 
31 
32 
33 // For compilers that support precompilation, includes "wx/wx.h".
34 #include <wx/wxprec.h>
35 
36 #ifndef WX_PRECOMP
37 #include <wx/window.h>
38 #endif
39 
40 #include <wx/intl.h>
41 #include "Import.h"
42 #include "Prefs.h"
43 #include "../Tags.h"
44 #include "../widgets/ProgressDialog.h"
45 
46 
47 #define DESC XO("Ogg Vorbis files")
48 
49 static const auto exts = {
50  wxT("ogg")
51 };
52 
53 #ifndef USE_LIBVORBIS
54 /* BPF There is no real reason to compile without LIBVORBIS, but if you do, you will needs this header */
55 #include "ImportPlugin.h"
56 
58  std::make_unique<UnusableImportPlugin>
59  (DESC, FileExtensions( exts.begin(), exts.end() ) )
60 };
61 
62 #else /* USE_LIBVORBIS */
63 
64 #include <wx/log.h>
65 #include <wx/string.h>
66 #include <wx/utils.h>
67 #include <wx/intl.h>
68 /* ffile.h must be included AFTER at least one other wx header that includes
69  * wx/setup.h, otherwise #ifdefs erroneously collapse it to nothing. This is
70  * a bug in wxWidgets (ffile.h should itself include wx/setup.h), and it
71  * was a bitch to track down. */
72 #include <wx/ffile.h>
73 
74 #include <vorbis/vorbisfile.h>
75 
76 #include "../WaveTrack.h"
77 #include "ImportPlugin.h"
78 
79 using NewChannelGroup = std::vector< std::shared_ptr<WaveTrack> >;
80 
81 class OggImportPlugin final : public ImportPlugin
82 {
83 public:
85  : ImportPlugin( FileExtensions( exts.begin(), exts.end() ) )
86  {
87  }
88 
90 
91  wxString GetPluginStringID() override { return wxT("liboggvorbis"); }
93  std::unique_ptr<ImportFileHandle> Open(
94  const FilePath &Filename, AudacityProject*) override;
95 };
96 
97 
99 {
100 public:
101  OggImportFileHandle(const FilePath & filename,
102  std::unique_ptr<wxFFile> &&file,
103  std::unique_ptr<OggVorbis_File> &&vorbisFile)
104  : ImportFileHandle(filename),
105  mFile(std::move(file)),
106  mVorbisFile(std::move(vorbisFile))
107  , mStreamUsage{ static_cast<size_t>(mVorbisFile->links) }
108  {
109  for (int i = 0; i < mVorbisFile->links; i++)
110  {
111  auto strinfo = XO("Index[%02x] Version[%d], Channels[%d], Rate[%ld]")
112  .Format(
113  (unsigned int) i,
114  mVorbisFile->vi[i].version,
115  mVorbisFile->vi[i].channels,
116  mVorbisFile->vi[i].rate);
117  mStreamInfo.push_back(strinfo);
118  mStreamUsage[i] = 0;
119  }
120 
121  }
123 
126  ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks,
127  Tags *tags) override;
128 
129  wxInt32 GetStreamCount() override
130  {
131  if (mVorbisFile)
132  return mVorbisFile->links;
133  else
134  return 0;
135  }
136 
138  {
139  return mStreamInfo;
140  }
141 
142  void SetStreamUsage(wxInt32 StreamID, bool Use) override
143  {
144  if (mVorbisFile)
145  {
146  if (StreamID < mVorbisFile->links)
147  mStreamUsage[StreamID] = (Use ? 1 : 0);
148  }
149  }
150 
151 private:
152  std::unique_ptr<wxFFile> mFile;
153  std::unique_ptr<OggVorbis_File> mVorbisFile;
154 
157  std::list<NewChannelGroup> mChannels;
158 };
159 
160 
162 {
163  return DESC;
164 }
165 
166 std::unique_ptr<ImportFileHandle> OggImportPlugin::Open(
167  const FilePath &filename, AudacityProject*)
168 {
169  // Suppress some compiler warnings about unused global variables in the library header
170  wxUnusedVar(OV_CALLBACKS_DEFAULT);
171  wxUnusedVar(OV_CALLBACKS_NOCLOSE);
172  wxUnusedVar(OV_CALLBACKS_STREAMONLY);
173  wxUnusedVar(OV_CALLBACKS_STREAMONLY_NOCLOSE);
174 
175  auto vorbisFile = std::make_unique<OggVorbis_File>();
176  auto file = std::make_unique<wxFFile>(filename, wxT("rb"));
177 
178  if (!file->IsOpened()) {
179  // No need for a message box, it's done automatically (but how?)
180  return nullptr;
181  }
182 
183  int err = ov_open(file->fp(), vorbisFile.get(), NULL, 0);
184 
185  if (err < 0) {
186  TranslatableString message;
187 
188  switch (err) {
189  case OV_EREAD:
190  message = XO("Media read error");
191  break;
192  case OV_ENOTVORBIS:
193  message = XO("Not an Ogg Vorbis file");
194  break;
195  case OV_EVERSION:
196  message = XO("Vorbis version mismatch");
197  break;
198  case OV_EBADHEADER:
199  message = XO("Invalid Vorbis bitstream header");
200  break;
201  case OV_EFAULT:
202  message = XO("Internal logic fault");
203  break;
204  }
205 
206  // what to do with message?
207  return nullptr;
208  }
209 
210  return std::make_unique<OggImportFileHandle>(filename, std::move(file), std::move(vorbisFile));
211 }
212 
214  std::make_unique< OggImportPlugin >()
215 };
216 
218 {
219  return DESC;
220 }
221 
223 {
224  // TODO:
225  return 0;
226 }
227 
229  WaveTrackFactory *trackFactory, TrackHolders &outTracks,
230  Tags *tags)
231 {
232  outTracks.clear();
233 
234  wxASSERT(mFile->IsOpened());
235 
236  CreateProgress();
237 
238  //Number of streams used may be less than mVorbisFile->links,
239  //but this way bitstream matches array index.
240  mChannels.resize(mVorbisFile->links);
241 
242  int i = -1;
243  for (auto &link : mChannels)
244  {
245  ++i;
246 
247  //Stream is not used
248  if (mStreamUsage[i] == 0)
249  {
250  //This is just a padding to keep bitstream number and
251  //array indices matched.
252  continue;
253  }
254 
255  vorbis_info *vi = ov_info(mVorbisFile.get(), i);
256 
257  link.resize(vi->channels);
258 
259  for (auto &channel : link)
260  // The format agrees with what is always passed to Append() below
261  channel = NewWaveTrack(*trackFactory, int16Sample, vi->rate);
262  }
263 
264  /* The number of bytes to get from the codec in each run */
265 #define CODEC_TRANSFER_SIZE 4096u
266 
267  /* The number of samples to read between calls to the callback.
268  * Balance between responsiveness of the GUI and throughput of import. */
269 #define SAMPLES_PER_CALLBACK 100000
270 
271  auto updateResult = ProgressResult::Success;
272  long bytesRead = 0;
273  {
274  ArrayOf<short> mainBuffer{ CODEC_TRANSFER_SIZE };
275 
276  /* determine endianness (clever trick courtesy of Nicholas Devillard,
277  * (http://www.eso.org/~ndevilla/endian/) */
278  int testvar = 1, endian;
279  if (*(char *)&testvar)
280  endian = 0; // little endian
281  else
282  endian = 1; // big endian
283 
284  /* number of samples currently in each channel's buffer */
285  long samplesRead = 0;
286  int bitstream = 0;
287  int samplesSinceLastCallback = 0;
288 
289  // You would think that the stream would already be seeked to 0, and
290  // indeed it is if the file is legit. But I had several ogg files on
291  // my hard drive that have malformed headers, and this added call
292  // causes them to be read correctly. Otherwise they have lots of
293  // zeros inserted at the beginning
294  ov_pcm_seek(mVorbisFile.get(), 0);
295 
296  do {
297  /* get data from the decoder */
298  bytesRead = ov_read(mVorbisFile.get(), (char *)mainBuffer.get(),
300  endian,
301  2, // word length (2 for 16 bit samples)
302  1, // signed
303  &bitstream);
304 
305  if (bytesRead == OV_HOLE) {
306  wxFileName ff(mFilename);
307  wxLogError(wxT("Ogg Vorbis importer: file %s is malformed, ov_read() reported a hole"),
308  ff.GetFullName());
309  /* http://lists.xiph.org/pipermail/vorbis-dev/2001-February/003223.html
310  * is the justification for doing this - best effort for malformed file,
311  * hence the message.
312  */
313  continue;
314  }
315  else if (bytesRead < 0) {
316  /* Malformed Ogg Vorbis file. */
317  /* TODO: Return some sort of meaningful error. */
318  wxLogError(wxT("Ogg Vorbis importer: ov_read() returned error %i"),
319  bytesRead);
320  break;
321  }
322 
323  samplesRead = bytesRead / mVorbisFile->vi[bitstream].channels / sizeof(short);
324 
325  /* give the data to the wavetracks */
326  auto iter = mChannels.begin();
327  std::advance(iter, bitstream);
328  if (mStreamUsage[bitstream] != 0)
329  {
330  auto iter2 = iter->begin();
331  for (int c = 0; c < mVorbisFile->vi[bitstream].channels; ++iter2, ++c)
332  iter2->get()->Append((char *)(mainBuffer.get() + c),
333  int16Sample,
334  samplesRead,
335  mVorbisFile->vi[bitstream].channels);
336  }
337 
338  samplesSinceLastCallback += samplesRead;
339  if (samplesSinceLastCallback > SAMPLES_PER_CALLBACK) {
340  updateResult = mProgress->Update(ov_time_tell(mVorbisFile.get()),
341  ov_time_total(mVorbisFile.get(), bitstream));
342  samplesSinceLastCallback -= SAMPLES_PER_CALLBACK;
343  }
344  } while (updateResult == ProgressResult::Success && bytesRead != 0);
345  }
346 
347  auto res = updateResult;
348  if (bytesRead < 0)
349  res = ProgressResult::Failed;
350 
351  if (res == ProgressResult::Failed || res == ProgressResult::Cancelled) {
352  return res;
353  }
354 
355  for (auto &link : mChannels)
356  {
357  for (auto &channel : link)
358  channel->Flush();
359  outTracks.push_back(std::move(link));
360  }
361 
362  //\todo { Extract comments from each stream? }
363  if (mVorbisFile->vc[0].comments > 0) {
364  tags->Clear();
365  for (int c = 0; c < mVorbisFile->vc[0].comments; c++) {
366  wxString comment = UTF8CTOWX(mVorbisFile->vc[0].user_comments[c]);
367  wxString name = comment.BeforeFirst(wxT('='));
368  wxString value = comment.AfterFirst(wxT('='));
369  if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
370  long val;
371  if (value.length() == 4 && value.ToLong(&val)) {
372  name = TAG_YEAR;
373  }
374  }
375  tags->SetTag(name, value);
376  }
377  }
378 
379  return res;
380 }
381 
383 {
384  ov_clear(mVorbisFile.get());
385  mFile->Detach(); // so that it doesn't try to close the file (ov_clear()
386  // did that already)
387 }
388 
389 #endif /* USE_LIBVORBIS */
OggImportFileHandle::Import
ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
Definition: ImportOGG.cpp:228
OggImportFileHandle::mVorbisFile
std::unique_ptr< OggVorbis_File > mVorbisFile
Definition: ImportOGG.cpp:153
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
OggImportFileHandle::mChannels
std::list< NewChannelGroup > mChannels
Definition: ImportOGG.cpp:157
OggImportFileHandle::SetStreamUsage
void SetStreamUsage(wxInt32 StreamID, bool Use) override
Definition: ImportOGG.cpp:142
TrackHolders
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
registered
static Importer::RegisteredImportPlugin registered
Definition: ImportOGG.cpp:213
BasicUI::ProgressResult::Success
@ Success
OggImportFileHandle
Definition: ImportOGG.cpp:99
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
ImportPlugin
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
Import.h
Tags::HasTag
bool HasTag(const wxString &name) const
Definition: Tags.cpp:452
OggImportFileHandle::mStreamUsage
ArrayOf< int > mStreamUsage
Definition: ImportOGG.cpp:155
Tags
ID3 Tags (for MP3)
Definition: Tags.h:74
OggImportFileHandle::~OggImportFileHandle
~OggImportFileHandle()
Definition: ImportOGG.cpp:382
DESC
#define DESC
Definition: ImportOGG.cpp:47
BasicUI::ProgressResult
ProgressResult
Definition: BasicUI.h:145
OggImportFileHandle::GetStreamInfo
const TranslatableStrings & GetStreamInfo() override
Definition: ImportOGG.cpp:137
RefreshCode::Cancelled
@ Cancelled
Definition: RefreshCode.h:23
XO
#define XO(s)
Definition: Internat.h:31
ImportFileHandle::mProgress
std::unique_ptr< ProgressDialog > mProgress
Definition: ImportPlugin.h:159
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
OggImportFileHandle::OggImportFileHandle
OggImportFileHandle(const FilePath &filename, std::unique_ptr< wxFFile > &&file, std::unique_ptr< OggVorbis_File > &&vorbisFile)
Definition: ImportOGG.cpp:101
OggImportPlugin
Definition: ImportOGG.cpp:82
ImportFileHandle::CreateProgress
void CreateProgress()
Definition: ImportPlugin.cpp:45
SAMPLES_PER_CALLBACK
#define SAMPLES_PER_CALLBACK
ImportFileHandle::NewWaveTrack
std::shared_ptr< WaveTrack > NewWaveTrack(WaveTrackFactory &trackFactory, sampleFormat effectiveFormat, double rate)
Build a wave track with appropriate format, which will not be narrower than the specified one.
Definition: ImportPlugin.cpp:69
FilePath
wxString FilePath
Definition: Project.h:20
Tags::SetTag
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:486
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
OggImportFileHandle::GetStreamCount
wxInt32 GetStreamCount() override
Definition: ImportOGG.cpp:129
OggImportFileHandle::mStreamInfo
TranslatableStrings mStreamInfo
Definition: ImportOGG.cpp:156
name
const TranslatableString name
Definition: Distortion.cpp:98
UTF8CTOWX
#define UTF8CTOWX(X)
Definition: Internat.h:159
Tags::Clear
void Clear()
Definition: Tags.cpp:337
exts
static const auto exts
Definition: ImportOGG.cpp:49
OggImportFileHandle::GetFileDescription
TranslatableString GetFileDescription() override
Definition: ImportOGG.cpp:217
ImportFileHandle::mFilename
FilePath mFilename
Definition: ImportPlugin.h:158
TAG_YEAR
#define TAG_YEAR
Definition: Tags.h:64
ImportFileHandle
An ImportFileHandle for data.
Definition: ImportPlugin.h:107
FileExtensions
wxArrayStringEx FileExtensions
Definition: Identifier.h:225
WaveTrackFactory
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:713
OggImportPlugin::GetPluginFormatDescription
TranslatableString GetPluginFormatDescription() override
Definition: ImportOGG.cpp:161
OggImportPlugin::~OggImportPlugin
~OggImportPlugin()
Definition: ImportOGG.cpp:89
OggImportPlugin::OggImportPlugin
OggImportPlugin()
Definition: ImportOGG.cpp:84
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
ImportPlugin.h
The interface that all file import "plugins" (if you want to call them that) must implement....
Importer::RegisteredImportPlugin
Definition: Import.h:85
OggImportPlugin::GetPluginStringID
wxString GetPluginStringID() override
Definition: ImportOGG.cpp:91
Prefs.h
OggImportFileHandle::GetFileUncompressedBytes
ByteCount GetFileUncompressedBytes() override
Definition: ImportOGG.cpp:222
NewChannelGroup
std::vector< std::shared_ptr< WaveTrack > > NewChannelGroup
Definition: Import.cpp:62
OggImportPlugin::Open
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *) override
Definition: ImportOGG.cpp:166
ImportFileHandle::ByteCount
unsigned long long ByteCount
Definition: ImportPlugin.h:127
OggImportFileHandle::mFile
std::unique_ptr< wxFFile > mFile
Definition: ImportOGG.cpp:152
ArrayOf< int >
CODEC_TRANSFER_SIZE
#define CODEC_TRANSFER_SIZE
Importer::RegisteredUnusableImportPlugin
Definition: Import.h:94