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