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