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<TrackListHolder> 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
234 auto tracks = trackFactory->CreateMany(vi->channels, int16Sample, vi->rate);
235
236 mStreams.push_back(tracks);
237 }
238
239 /* The number of bytes to get from the codec in each run */
240#define CODEC_TRANSFER_SIZE 4096u
241
242 /* The number of samples to read between calls to the callback.
243 * Balance between responsiveness of the GUI and throughput of import. */
244#define SAMPLES_PER_CALLBACK 100000
245
246 long bytesRead = 0;
247 {
249
250 /* determine endianness (clever trick courtesy of Nicholas Devillard,
251 * (http://www.eso.org/~ndevilla/endian/) */
252 int testvar = 1, endian;
253 if (*(char *)&testvar)
254 endian = 0; // little endian
255 else
256 endian = 1; // big endian
257
258 /* number of samples currently in each channel's buffer */
259 long samplesRead = 0;
260 int bitstream = 0;
261 int samplesSinceLastCallback = 0;
262
263 // You would think that the stream would already be seeked to 0, and
264 // indeed it is if the file is legit. But I had several ogg files on
265 // my hard drive that have malformed headers, and this added call
266 // causes them to be read correctly. Otherwise they have lots of
267 // zeros inserted at the beginning
268 ov_pcm_seek(mVorbisFile.get(), 0);
269
270 do {
271 /* get data from the decoder */
272 bytesRead = ov_read(mVorbisFile.get(), (char *)mainBuffer.get(),
274 endian,
275 2, // word length (2 for 16 bit samples)
276 1, // signed
277 &bitstream);
278
279 if (bytesRead == OV_HOLE) {
280 wxFileName ff(GetFilename());
281 wxLogError(wxT("Ogg Vorbis importer: file %s is malformed, ov_read() reported a hole"),
282 ff.GetFullName());
283 /* http://lists.xiph.org/pipermail/vorbis-dev/2001-February/003223.html
284 * is the justification for doing this - best effort for malformed file,
285 * hence the message.
286 */
287 continue;
288 }
289 else if (bytesRead < 0) {
290 /* Malformed Ogg Vorbis file. */
291 /* TODO: Return some sort of meaningful error. */
292 wxLogError(wxT("Ogg Vorbis importer: ov_read() returned error %i"),
293 bytesRead);
294 break;
295 }
296
297 samplesRead = bytesRead / mVorbisFile->vi[bitstream].channels / sizeof(short);
298
299 if (mStreamUsage[bitstream] != 0)
300 {
301 /* give the data to the wavetracks */
302 unsigned chn = 0;
303 ImportUtils::ForEachChannel(**std::next(mStreams.begin(), bitstream), [&](auto& channel)
304 {
305 channel.AppendBuffer(
306 (char *)(mainBuffer.get() + chn),
307 int16Sample,
308 samplesRead,
309 mVorbisFile->vi[bitstream].channels,
310 int16Sample
311 );
312 ++chn;
313 });
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& stream : mStreams)
339 {
340 ImportUtils::FinalizeImport(outTracks, std::move(*stream));
341 }
342 mStreams.clear();
343
344 //\todo { Extract comments from each stream? }
345 if (mVorbisFile->vc[0].comments > 0) {
346 tags->Clear();
347 for (int c = 0; c < mVorbisFile->vc[0].comments; c++) {
348 wxString comment = UTF8CTOWX(mVorbisFile->vc[0].user_comments[c]);
349 wxString name = comment.BeforeFirst(wxT('='));
350 wxString value = comment.AfterFirst(wxT('='));
351 if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
352 long val;
353 if (value.length() == 4 && value.ToLong(&val)) {
354 name = TAG_YEAR;
355 }
356 }
357 tags->SetTag(name, value);
358 }
359 }
360
361 progressListener.OnImportResult(IsStopped()
364}
365
367{
368 ov_clear(mVorbisFile.get());
369 mFile->Detach(); // so that it doesn't try to close the file (ov_clear()
370 // did that already)
371}
wxT("CloseDown"))
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
wxString name
Definition: TagsEditor.cpp:166
const auto tracks
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 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
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::vector< TrackListHolder > mStreams
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: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:871
TrackListHolder CreateMany(size_t nChannels)
Creates tracks with project's default rate and format and the given number of channels.
Definition: WaveTrack.cpp:423
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.