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(
102 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
103 TrackHolders& outTracks, Tags* tags,
104 std::optional<LibFileFormats::AcidizerTags>& outAcidTags) 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::vector<WaveTrack::Holder> mStreams;
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 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
207 TrackHolders& outTracks, Tags* tags,
208 std::optional<LibFileFormats::AcidizerTags>&)
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 mStreams.reserve(mVorbisFile->links);
219
220 for(int i = 0; i < mVorbisFile->links; ++i)
221 {
222 //Stream is not used
223 if (mStreamUsage[i] == 0)
224 {
225 //This is just a padding to keep bitstream number and
226 //array indices matched.
227 mStreams.push_back({});
228 continue;
229 }
230
231 vorbis_info *vi = ov_info(mVorbisFile.get(), i);
232
233 // The format agrees with what is always passed to Append() below
235 *trackFactory,
236 vi->channels,
238 vi->rate));
239 }
240
241 /* The number of bytes to get from the codec in each run */
242#define CODEC_TRANSFER_SIZE 4096u
243
244 /* The number of samples to read between calls to the callback.
245 * Balance between responsiveness of the GUI and throughput of import. */
246#define SAMPLES_PER_CALLBACK 100000
247
248 long bytesRead = 0;
249 {
251
252 /* determine endianness (clever trick courtesy of Nicholas Devillard,
253 * (http://www.eso.org/~ndevilla/endian/) */
254 int testvar = 1, endian;
255 if (*(char *)&testvar)
256 endian = 0; // little endian
257 else
258 endian = 1; // big endian
259
260 /* number of samples currently in each channel's buffer */
261 long samplesRead = 0;
262 int bitstream = 0;
263 int samplesSinceLastCallback = 0;
264
265 // You would think that the stream would already be seeked to 0, and
266 // indeed it is if the file is legit. But I had several ogg files on
267 // my hard drive that have malformed headers, and this added call
268 // causes them to be read correctly. Otherwise they have lots of
269 // zeros inserted at the beginning
270 ov_pcm_seek(mVorbisFile.get(), 0);
271
272 do {
273 /* get data from the decoder */
274 bytesRead = ov_read(mVorbisFile.get(), (char *)mainBuffer.get(),
276 endian,
277 2, // word length (2 for 16 bit samples)
278 1, // signed
279 &bitstream);
280
281 if (bytesRead == OV_HOLE) {
282 wxFileName ff(GetFilename());
283 wxLogError(wxT("Ogg Vorbis importer: file %s is malformed, ov_read() reported a hole"),
284 ff.GetFullName());
285 /* http://lists.xiph.org/pipermail/vorbis-dev/2001-February/003223.html
286 * is the justification for doing this - best effort for malformed file,
287 * hence the message.
288 */
289 continue;
290 }
291 else if (bytesRead < 0) {
292 /* Malformed Ogg Vorbis file. */
293 /* TODO: Return some sort of meaningful error. */
294 wxLogError(wxT("Ogg Vorbis importer: ov_read() returned error %i"),
295 bytesRead);
296 break;
297 }
298
299 samplesRead = bytesRead / mVorbisFile->vi[bitstream].channels / sizeof(short);
300
301 if (mStreamUsage[bitstream] != 0)
302 {
303 /* give the data to the wavetracks */
304 unsigned chn = 0;
305 ImportUtils::ForEachChannel(**std::next(mStreams.begin(), bitstream), [&](auto& channel)
306 {
307 channel.AppendBuffer(
308 (char *)(mainBuffer.get() + chn),
309 int16Sample,
310 samplesRead,
311 mVorbisFile->vi[bitstream].channels,
312 int16Sample
313 );
314 ++chn;
315 });
316 }
317
318 samplesSinceLastCallback += samplesRead;
319 if (samplesSinceLastCallback > SAMPLES_PER_CALLBACK) {
320 const auto timeTotal = ov_time_total(mVorbisFile.get(), bitstream);
321 if(timeTotal > 0)
322 progressListener.OnImportProgress(ov_time_tell(mVorbisFile.get()) / timeTotal);
323 samplesSinceLastCallback -= SAMPLES_PER_CALLBACK;
324 }
325 } while (!IsCancelled() && !IsStopped() && bytesRead != 0);
326 }
327
328 if (bytesRead < 0)
329 {
331 return;
332 }
333
334 if(IsCancelled())
335 {
337 return;
338 }
339
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< Track > > 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< WaveTrack > NewWaveTrack(WaveTrackFactory &trackFactory, unsigned nChannels, sampleFormat effectiveFormat, double rate)
Definition: ImportUtils.cpp:35
static void ForEachChannel(TrackList &trackList, const std::function< void(WaveChannel &)> &op)
Iterates over channels in each wave track from the list.
Definition: ImportUtils.cpp:73
static void FinalizeImport(TrackHolders &outTracks, const std::vector< std::shared_ptr< WaveTrack > > &importedStreams)
Flushes the given channels and moves them to outTracks.
Definition: ImportUtils.cpp:49
wxInt32 GetStreamCount() override
Definition: ImportOGG.cpp:106
std::unique_ptr< wxFFile > mFile
Definition: ImportOGG.cpp:129
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags, std::optional< LibFileFormats::AcidizerTags > &outAcidTags) override
Definition: ImportOGG.cpp:205
TranslatableString GetFileDescription() override
Definition: ImportOGG.cpp:194
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
std::vector< WaveTrack::Holder > mStreams
Definition: ImportOGG.cpp:134
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::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:293
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:870
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
const char * begin(const char *str) noexcept
Definition: StringUtils.h:101
STL namespace.