OpenShot Audio Library | OpenShotAudio: juce_OggVorbisAudioFormat.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.3.2

Loading...
Searching...
No Matches
juce_OggVorbisAudioFormat.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 5 End-User License
11 Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
12 27th April 2017).
13
14 End User License Agreement: www.juce.com/juce-5-licence
15 Privacy Policy: www.juce.com/juce-5-privacy-policy
16
17 Or: You may also use this code under the terms of the GPL v3 (see
18 www.gnu.org/licenses).
19
20 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22 DISCLAIMED.
23
24 ==============================================================================
25*/
26
27namespace juce
28{
29
30#if JUCE_USE_OGGVORBIS
31
32#if JUCE_MAC && ! defined (__MACOSX__)
33 #define __MACOSX__ 1
34#endif
35
36namespace OggVorbisNamespace
37{
38#if JUCE_INCLUDE_OGGVORBIS_CODE || ! defined (JUCE_INCLUDE_OGGVORBIS_CODE)
39 #if JUCE_MSVC
40 #pragma warning (push)
41 #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365 4456 4457 4459)
42 #elif JUCE_CLANG
43 #pragma clang diagnostic push
44 #pragma clang diagnostic ignored "-Wconversion"
45 #pragma clang diagnostic ignored "-Wshadow"
46 #pragma clang diagnostic ignored "-Wdeprecated-register"
47 #if __has_warning("-Wzero-as-null-pointer-constant")
48 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
49 #endif
50 #elif JUCE_GCC
51 #pragma GCC diagnostic push
52 #pragma GCC diagnostic ignored "-Wconversion"
53 #pragma GCC diagnostic ignored "-Wshadow"
54 #pragma GCC diagnostic ignored "-Wsign-conversion"
55 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
56 #endif
57
58 #include "oggvorbis/vorbisenc.h"
59 #include "oggvorbis/codec.h"
60 #include "oggvorbis/vorbisfile.h"
61
62 #include "oggvorbis/bitwise.c"
63 #include "oggvorbis/framing.c"
64 #include "oggvorbis/libvorbis-1.3.2/lib/analysis.c"
65 #include "oggvorbis/libvorbis-1.3.2/lib/bitrate.c"
66 #include "oggvorbis/libvorbis-1.3.2/lib/block.c"
67 #include "oggvorbis/libvorbis-1.3.2/lib/codebook.c"
68 #include "oggvorbis/libvorbis-1.3.2/lib/envelope.c"
69 #include "oggvorbis/libvorbis-1.3.2/lib/floor0.c"
70 #include "oggvorbis/libvorbis-1.3.2/lib/floor1.c"
71 #include "oggvorbis/libvorbis-1.3.2/lib/info.c"
72 #include "oggvorbis/libvorbis-1.3.2/lib/lpc.c"
73 #include "oggvorbis/libvorbis-1.3.2/lib/lsp.c"
74 #include "oggvorbis/libvorbis-1.3.2/lib/mapping0.c"
75 #include "oggvorbis/libvorbis-1.3.2/lib/mdct.c"
76 #include "oggvorbis/libvorbis-1.3.2/lib/psy.c"
77 #include "oggvorbis/libvorbis-1.3.2/lib/registry.c"
78 #include "oggvorbis/libvorbis-1.3.2/lib/res0.c"
79 #include "oggvorbis/libvorbis-1.3.2/lib/sharedbook.c"
80 #include "oggvorbis/libvorbis-1.3.2/lib/smallft.c"
81 #include "oggvorbis/libvorbis-1.3.2/lib/synthesis.c"
82 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c"
83 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c"
84 #include "oggvorbis/libvorbis-1.3.2/lib/window.c"
85
86 #if JUCE_MSVC
87 #pragma warning (pop)
88 #elif JUCE_CLANG
89 #pragma clang diagnostic pop
90 #elif JUCE_GCC
91 #pragma GCC diagnostic pop
92 #endif
93#else
94 #include <vorbis/vorbisenc.h>
95 #include <vorbis/codec.h>
96 #include <vorbis/vorbisfile.h>
97#endif
98}
99
100#undef max
101#undef min
102
103//==============================================================================
104static const char* const oggFormatName = "Ogg-Vorbis file";
105
106const char* const OggVorbisAudioFormat::encoderName = "encoder";
107const char* const OggVorbisAudioFormat::id3title = "id3title";
108const char* const OggVorbisAudioFormat::id3artist = "id3artist";
109const char* const OggVorbisAudioFormat::id3album = "id3album";
110const char* const OggVorbisAudioFormat::id3comment = "id3comment";
111const char* const OggVorbisAudioFormat::id3date = "id3date";
112const char* const OggVorbisAudioFormat::id3genre = "id3genre";
113const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber";
114
115
116//==============================================================================
117class OggReader : public AudioFormatReader
118{
119public:
120 OggReader (InputStream* inp) : AudioFormatReader (inp, oggFormatName)
121 {
122 sampleRate = 0;
123 usesFloatingPointData = true;
124
125 callbacks.read_func = &oggReadCallback;
126 callbacks.seek_func = &oggSeekCallback;
127 callbacks.close_func = &oggCloseCallback;
128 callbacks.tell_func = &oggTellCallback;
129
130 auto err = ov_open_callbacks (input, &ovFile, nullptr, 0, callbacks);
131
132 if (err == 0)
133 {
134 auto* info = ov_info (&ovFile, -1);
135
136 auto* comment = ov_comment (&ovFile, -1);
137 addMetadataItem (comment, "ENCODER", OggVorbisAudioFormat::encoderName);
138 addMetadataItem (comment, "TITLE", OggVorbisAudioFormat::id3title);
139 addMetadataItem (comment, "ARTIST", OggVorbisAudioFormat::id3artist);
140 addMetadataItem (comment, "ALBUM", OggVorbisAudioFormat::id3album);
141 addMetadataItem (comment, "COMMENT", OggVorbisAudioFormat::id3comment);
142 addMetadataItem (comment, "DATE", OggVorbisAudioFormat::id3date);
143 addMetadataItem (comment, "GENRE", OggVorbisAudioFormat::id3genre);
144 addMetadataItem (comment, "TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber);
145
146 lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1);
147 numChannels = (unsigned int) info->channels;
148 bitsPerSample = 16;
149 sampleRate = info->rate;
150
151 reservoir.setSize ((int) numChannels, (int) jmin (lengthInSamples, (int64) 4096));
152 }
153 }
154
155 ~OggReader() override
156 {
157 ov_clear (&ovFile);
158 }
159
160 void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment, const char* name, const char* metadataName)
161 {
162 if (auto* value = vorbis_comment_query (comment, name, 0))
163 metadataValues.set (metadataName, value);
164 }
165
166 //==============================================================================
167 bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
168 int64 startSampleInFile, int numSamples) override
169 {
170 while (numSamples > 0)
171 {
172 auto numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile);
173
174 if (startSampleInFile >= reservoirStart && numAvailable > 0)
175 {
176 // got a few samples overlapping, so use them before seeking..
177
178 auto numToUse = jmin (numSamples, numAvailable);
179
180 for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
181 if (destSamples[i] != nullptr)
182 memcpy (destSamples[i] + startOffsetInDestBuffer,
183 reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)),
184 (size_t) numToUse * sizeof (float));
185
186 startSampleInFile += numToUse;
187 numSamples -= numToUse;
188 startOffsetInDestBuffer += numToUse;
189
190 if (numSamples == 0)
191 break;
192 }
193
194 if (startSampleInFile < reservoirStart
195 || startSampleInFile + numSamples > reservoirStart + samplesInReservoir)
196 {
197 // buffer miss, so refill the reservoir
198 reservoirStart = jmax (0, (int) startSampleInFile);
199 samplesInReservoir = reservoir.getNumSamples();
200
201 if (reservoirStart != (int) ov_pcm_tell (&ovFile))
202 ov_pcm_seek (&ovFile, reservoirStart);
203
204 int bitStream = 0;
205 int offset = 0;
206 int numToRead = samplesInReservoir;
207
208 while (numToRead > 0)
209 {
210 float** dataIn = nullptr;
211 auto samps = ov_read_float (&ovFile, &dataIn, numToRead, &bitStream);
212
213 if (samps <= 0)
214 break;
215
216 jassert (samps <= numToRead);
217
218 for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;)
219 memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps * sizeof (float));
220
221 numToRead -= samps;
222 offset += samps;
223 }
224
225 if (numToRead > 0)
226 reservoir.clear (offset, numToRead);
227 }
228 }
229
230 if (numSamples > 0)
231 {
232 for (int i = numDestChannels; --i >= 0;)
233 if (destSamples[i] != nullptr)
234 zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int));
235 }
236
237 return true;
238 }
239
240 //==============================================================================
241 static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource)
242 {
243 return (size_t) (static_cast<InputStream*> (datasource)->read (ptr, (int) (size * nmemb))) / size;
244 }
245
246 static int oggSeekCallback (void* datasource, OggVorbisNamespace::ogg_int64_t offset, int whence)
247 {
248 auto* in = static_cast<InputStream*> (datasource);
249
250 if (whence == SEEK_CUR)
251 offset += in->getPosition();
252 else if (whence == SEEK_END)
253 offset += in->getTotalLength();
254
255 in->setPosition (offset);
256 return 0;
257 }
258
259 static int oggCloseCallback (void*)
260 {
261 return 0;
262 }
263
264 static long oggTellCallback (void* datasource)
265 {
266 return (long) static_cast<InputStream*> (datasource)->getPosition();
267 }
268
269private:
270 OggVorbisNamespace::OggVorbis_File ovFile;
271 OggVorbisNamespace::ov_callbacks callbacks;
272 AudioBuffer<float> reservoir;
273 int reservoirStart = 0, samplesInReservoir = 0;
274
275 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
276};
277
278//==============================================================================
279class OggWriter : public AudioFormatWriter
280{
281public:
282 OggWriter (OutputStream* out, double rate,
283 unsigned int numChans, unsigned int bitsPerSamp,
284 int qualityIndex, const StringPairArray& metadata)
285 : AudioFormatWriter (out, oggFormatName, rate, numChans, bitsPerSamp)
286 {
287 vorbis_info_init (&vi);
288
289 if (vorbis_encode_init_vbr (&vi, (int) numChans, (int) rate,
290 jlimit (0.0f, 1.0f, qualityIndex * 0.1f)) == 0)
291 {
292 vorbis_comment_init (&vc);
293
294 addMetadata (metadata, OggVorbisAudioFormat::encoderName, "ENCODER");
295 addMetadata (metadata, OggVorbisAudioFormat::id3title, "TITLE");
296 addMetadata (metadata, OggVorbisAudioFormat::id3artist, "ARTIST");
297 addMetadata (metadata, OggVorbisAudioFormat::id3album, "ALBUM");
298 addMetadata (metadata, OggVorbisAudioFormat::id3comment, "COMMENT");
299 addMetadata (metadata, OggVorbisAudioFormat::id3date, "DATE");
300 addMetadata (metadata, OggVorbisAudioFormat::id3genre, "GENRE");
301 addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber, "TRACKNUMBER");
302
303 vorbis_analysis_init (&vd, &vi);
304 vorbis_block_init (&vd, &vb);
305
306 ogg_stream_init (&os, Random::getSystemRandom().nextInt());
307
308 OggVorbisNamespace::ogg_packet header, header_comm, header_code;
309 vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code);
310
311 ogg_stream_packetin (&os, &header);
312 ogg_stream_packetin (&os, &header_comm);
313 ogg_stream_packetin (&os, &header_code);
314
315 for (;;)
316 {
317 if (ogg_stream_flush (&os, &og) == 0)
318 break;
319
320 output->write (og.header, (size_t) og.header_len);
321 output->write (og.body, (size_t) og.body_len);
322 }
323
324 ok = true;
325 }
326 }
327
328 ~OggWriter() override
329 {
330 if (ok)
331 {
332 // write a zero-length packet to show ogg that we're finished..
333 writeSamples (0);
334
335 ogg_stream_clear (&os);
336 vorbis_block_clear (&vb);
337 vorbis_dsp_clear (&vd);
338 vorbis_comment_clear (&vc);
339
340 vorbis_info_clear (&vi);
341 output->flush();
342 }
343 else
344 {
345 vorbis_info_clear (&vi);
346 output = nullptr; // to stop the base class deleting this, as it needs to be returned
347 // to the caller of createWriter()
348 }
349 }
350
351 //==============================================================================
352 bool write (const int** samplesToWrite, int numSamples) override
353 {
354 if (ok)
355 {
356 if (numSamples > 0)
357 {
358 const double gain = 1.0 / 0x80000000u;
359 float** const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples);
360
361 for (int i = (int) numChannels; --i >= 0;)
362 {
363 if (auto* dst = vorbisBuffer[i])
364 {
365 if (const int* src = samplesToWrite [i])
366 {
367 for (int j = 0; j < numSamples; ++j)
368 dst[j] = (float) (src[j] * gain);
369 }
370 }
371 }
372 }
373
374 writeSamples (numSamples);
375 }
376
377 return ok;
378 }
379
380 void writeSamples (int numSamples)
381 {
382 vorbis_analysis_wrote (&vd, numSamples);
383
384 while (vorbis_analysis_blockout (&vd, &vb) == 1)
385 {
386 vorbis_analysis (&vb, nullptr);
387 vorbis_bitrate_addblock (&vb);
388
389 while (vorbis_bitrate_flushpacket (&vd, &op))
390 {
391 ogg_stream_packetin (&os, &op);
392
393 for (;;)
394 {
395 if (ogg_stream_pageout (&os, &og) == 0)
396 break;
397
398 output->write (og.header, (size_t) og.header_len);
399 output->write (og.body, (size_t) og.body_len);
400
401 if (ogg_page_eos (&og))
402 break;
403 }
404 }
405 }
406 }
407
408 bool ok = false;
409
410private:
411 OggVorbisNamespace::ogg_stream_state os;
412 OggVorbisNamespace::ogg_page og;
413 OggVorbisNamespace::ogg_packet op;
414 OggVorbisNamespace::vorbis_info vi;
415 OggVorbisNamespace::vorbis_comment vc;
416 OggVorbisNamespace::vorbis_dsp_state vd;
417 OggVorbisNamespace::vorbis_block vb;
418
419 void addMetadata (const StringPairArray& metadata, const char* name, const char* vorbisName)
420 {
421 auto s = metadata [name];
422
423 if (s.isNotEmpty())
424 vorbis_comment_add_tag (&vc, vorbisName, const_cast<char*> (s.toRawUTF8()));
425 }
426
427 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter)
428};
429
430
431//==============================================================================
432OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg")
433{
434}
435
436OggVorbisAudioFormat::~OggVorbisAudioFormat()
437{
438}
439
440Array<int> OggVorbisAudioFormat::getPossibleSampleRates()
441{
442 return { 8000, 11025, 12000, 16000, 22050, 32000,
443 44100, 48000, 88200, 96000, 176400, 192000 };
444}
445
446Array<int> OggVorbisAudioFormat::getPossibleBitDepths()
447{
448 return { 32 };
449}
450
451bool OggVorbisAudioFormat::canDoStereo() { return true; }
452bool OggVorbisAudioFormat::canDoMono() { return true; }
453bool OggVorbisAudioFormat::isCompressed() { return true; }
454
455AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, bool deleteStreamIfOpeningFails)
456{
457 std::unique_ptr<OggReader> r (new OggReader (in));
458
459 if (r->sampleRate > 0)
460 return r.release();
461
462 if (! deleteStreamIfOpeningFails)
463 r->input = nullptr;
464
465 return nullptr;
466}
467
468AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out,
469 double sampleRate,
470 unsigned int numChannels,
471 int bitsPerSample,
472 const StringPairArray& metadataValues,
473 int qualityOptionIndex)
474{
475 if (out == nullptr)
476 return nullptr;
477
478 std::unique_ptr<OggWriter> w (new OggWriter (out, sampleRate, numChannels,
479 (unsigned int) bitsPerSample,
480 qualityOptionIndex, metadataValues));
481
482 return w->ok ? w.release() : nullptr;
483}
484
485StringArray OggVorbisAudioFormat::getQualityOptions()
486{
487 return { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps",
488 "192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps" };
489}
490
491int OggVorbisAudioFormat::estimateOggFileQuality (const File& source)
492{
493 if (auto* in = source.createInputStream())
494 {
495 if (auto r = std::unique_ptr<AudioFormatReader> (createReaderFor (in, true)))
496 {
497 auto lengthSecs = r->lengthInSamples / r->sampleRate;
498 auto approxBitsPerSecond = (int) (source.getSize() * 8 / lengthSecs);
499
500 auto qualities = getQualityOptions();
501 int bestIndex = 0;
502 int bestDiff = 10000;
503
504 for (int i = qualities.size(); --i >= 0;)
505 {
506 auto diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond);
507
508 if (diff < bestDiff)
509 {
510 bestDiff = diff;
511 bestIndex = i;
512 }
513 }
514
515 return bestIndex;
516 }
517 }
518
519 return 0;
520}
521
522#endif
523
524} // namespace juce