OpenShot Library | libopenshot-audio 0.2.0
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 "-Wshadow"
53 #endif
54
55 #include "oggvorbis/vorbisenc.h"
56 #include "oggvorbis/codec.h"
57 #include "oggvorbis/vorbisfile.h"
58
59 #include "oggvorbis/bitwise.c"
60 #include "oggvorbis/framing.c"
61 #include "oggvorbis/libvorbis-1.3.2/lib/analysis.c"
62 #include "oggvorbis/libvorbis-1.3.2/lib/bitrate.c"
63 #include "oggvorbis/libvorbis-1.3.2/lib/block.c"
64 #include "oggvorbis/libvorbis-1.3.2/lib/codebook.c"
65 #include "oggvorbis/libvorbis-1.3.2/lib/envelope.c"
66 #include "oggvorbis/libvorbis-1.3.2/lib/floor0.c"
67 #include "oggvorbis/libvorbis-1.3.2/lib/floor1.c"
68 #include "oggvorbis/libvorbis-1.3.2/lib/info.c"
69 #include "oggvorbis/libvorbis-1.3.2/lib/lpc.c"
70 #include "oggvorbis/libvorbis-1.3.2/lib/lsp.c"
71 #include "oggvorbis/libvorbis-1.3.2/lib/mapping0.c"
72 #include "oggvorbis/libvorbis-1.3.2/lib/mdct.c"
73 #include "oggvorbis/libvorbis-1.3.2/lib/psy.c"
74 #include "oggvorbis/libvorbis-1.3.2/lib/registry.c"
75 #include "oggvorbis/libvorbis-1.3.2/lib/res0.c"
76 #include "oggvorbis/libvorbis-1.3.2/lib/sharedbook.c"
77 #include "oggvorbis/libvorbis-1.3.2/lib/smallft.c"
78 #include "oggvorbis/libvorbis-1.3.2/lib/synthesis.c"
79 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c"
80 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c"
81 #include "oggvorbis/libvorbis-1.3.2/lib/window.c"
82
83 #if JUCE_MSVC
84 #pragma warning (pop)
85 #elif JUCE_CLANG
86 #pragma clang diagnostic pop
87 #elif JUCE_GCC
88 #pragma GCC diagnostic pop
89 #endif
90#else
91 #include <vorbis/vorbisenc.h>
92 #include <vorbis/codec.h>
93 #include <vorbis/vorbisfile.h>
94#endif
95}
96
97#undef max
98#undef min
99
100//==============================================================================
101static const char* const oggFormatName = "Ogg-Vorbis file";
102
103const char* const OggVorbisAudioFormat::encoderName = "encoder";
104const char* const OggVorbisAudioFormat::id3title = "id3title";
105const char* const OggVorbisAudioFormat::id3artist = "id3artist";
106const char* const OggVorbisAudioFormat::id3album = "id3album";
107const char* const OggVorbisAudioFormat::id3comment = "id3comment";
108const char* const OggVorbisAudioFormat::id3date = "id3date";
109const char* const OggVorbisAudioFormat::id3genre = "id3genre";
110const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber";
111
112
113//==============================================================================
114class OggReader : public AudioFormatReader
115{
116public:
117 OggReader (InputStream* inp) : AudioFormatReader (inp, oggFormatName)
118 {
119 sampleRate = 0;
120 usesFloatingPointData = true;
121
122 callbacks.read_func = &oggReadCallback;
123 callbacks.seek_func = &oggSeekCallback;
124 callbacks.close_func = &oggCloseCallback;
125 callbacks.tell_func = &oggTellCallback;
126
127 auto err = ov_open_callbacks (input, &ovFile, nullptr, 0, callbacks);
128
129 if (err == 0)
130 {
131 auto* info = ov_info (&ovFile, -1);
132
133 auto* comment = ov_comment (&ovFile, -1);
142
143 lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1);
144 numChannels = (unsigned int) info->channels;
145 bitsPerSample = 16;
146 sampleRate = info->rate;
147
148 reservoir.setSize ((int) numChannels, (int) jmin (lengthInSamples, (int64) 4096));
149 }
150 }
151
152 ~OggReader() override
153 {
154 ov_clear (&ovFile);
155 }
156
157 void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment, const char* name, const char* metadataName)
158 {
159 if (auto* value = vorbis_comment_query (comment, name, 0))
160 metadataValues.set (metadataName, value);
161 }
162
163 //==============================================================================
164 bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
165 int64 startSampleInFile, int numSamples) override
166 {
167 while (numSamples > 0)
168 {
170
172 {
173 // got a few samples overlapping, so use them before seeking..
174
175 auto numToUse = jmin (numSamples, numAvailable);
176
177 for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
178 if (destSamples[i] != nullptr)
180 reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)),
181 sizeof (float) * (size_t) numToUse);
182
184 numSamples -= numToUse;
186
187 if (numSamples == 0)
188 break;
189 }
190
193 {
194 // buffer miss, so refill the reservoir
195 reservoirStart = jmax (0, (int) startSampleInFile);
196 samplesInReservoir = reservoir.getNumSamples();
197
198 if (reservoirStart != (int) ov_pcm_tell (&ovFile))
200
201 int bitStream = 0;
202 int offset = 0;
204
205 while (numToRead > 0)
206 {
207 float** dataIn = nullptr;
209
210 if (samps <= 0)
211 break;
212
213 jassert (samps <= numToRead);
214
215 for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;)
216 memcpy (reservoir.getWritePointer (i, offset), dataIn[i], sizeof (float) * (size_t) samps);
217
218 numToRead -= samps;
219 offset += samps;
220 }
221
222 if (numToRead > 0)
223 reservoir.clear (offset, numToRead);
224 }
225 }
226
227 if (numSamples > 0)
228 {
229 for (int i = numDestChannels; --i >= 0;)
230 if (destSamples[i] != nullptr)
231 zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples);
232 }
233
234 return true;
235 }
236
237 //==============================================================================
238 static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource)
239 {
240 return (size_t) (static_cast<InputStream*> (datasource)->read (ptr, (int) (size * nmemb))) / size;
241 }
242
243 static int oggSeekCallback (void* datasource, OggVorbisNamespace::ogg_int64_t offset, int whence)
244 {
245 auto* in = static_cast<InputStream*> (datasource);
246
247 if (whence == SEEK_CUR)
248 offset += in->getPosition();
249 else if (whence == SEEK_END)
250 offset += in->getTotalLength();
251
252 in->setPosition (offset);
253 return 0;
254 }
255
256 static int oggCloseCallback (void*)
257 {
258 return 0;
259 }
260
261 static long oggTellCallback (void* datasource)
262 {
263 return (long) static_cast<InputStream*> (datasource)->getPosition();
264 }
265
266private:
267 OggVorbisNamespace::OggVorbis_File ovFile;
268 OggVorbisNamespace::ov_callbacks callbacks;
269 AudioBuffer<float> reservoir;
271
272 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
273};
274
275//==============================================================================
276class OggWriter : public AudioFormatWriter
277{
278public:
279 OggWriter (OutputStream* out, double rate,
280 unsigned int numChans, unsigned int bitsPerSamp,
281 int qualityIndex, const StringPairArray& metadata)
282 : AudioFormatWriter (out, oggFormatName, rate, numChans, bitsPerSamp)
283 {
285
286 if (vorbis_encode_init_vbr (&vi, (int) numChans, (int) rate,
287 jlimit (0.0f, 1.0f, qualityIndex * 0.1f)) == 0)
288 {
290
291 addMetadata (metadata, OggVorbisAudioFormat::encoderName, "ENCODER");
292 addMetadata (metadata, OggVorbisAudioFormat::id3title, "TITLE");
293 addMetadata (metadata, OggVorbisAudioFormat::id3artist, "ARTIST");
294 addMetadata (metadata, OggVorbisAudioFormat::id3album, "ALBUM");
295 addMetadata (metadata, OggVorbisAudioFormat::id3comment, "COMMENT");
296 addMetadata (metadata, OggVorbisAudioFormat::id3date, "DATE");
297 addMetadata (metadata, OggVorbisAudioFormat::id3genre, "GENRE");
298 addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber, "TRACKNUMBER");
299
302
304
305 OggVorbisNamespace::ogg_packet header, header_comm, header_code;
307
311
312 for (;;)
313 {
314 if (ogg_stream_flush (&os, &og) == 0)
315 break;
316
317 output->write (og.header, (size_t) og.header_len);
318 output->write (og.body, (size_t) og.body_len);
319 }
320
321 ok = true;
322 }
323 }
324
325 ~OggWriter() override
326 {
327 if (ok)
328 {
329 // write a zero-length packet to show ogg that we're finished..
330 writeSamples (0);
331
336
338 output->flush();
339 }
340 else
341 {
343 output = nullptr; // to stop the base class deleting this, as it needs to be returned
344 // to the caller of createWriter()
345 }
346 }
347
348 //==============================================================================
349 bool write (const int** samplesToWrite, int numSamples) override
350 {
351 if (ok)
352 {
353 if (numSamples > 0)
354 {
355 const double gain = 1.0 / 0x80000000u;
356 float** const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples);
357
358 for (int i = (int) numChannels; --i >= 0;)
359 {
360 if (auto* dst = vorbisBuffer[i])
361 {
362 if (const int* src = samplesToWrite [i])
363 {
364 for (int j = 0; j < numSamples; ++j)
365 dst[j] = (float) (src[j] * gain);
366 }
367 }
368 }
369 }
370
371 writeSamples (numSamples);
372 }
373
374 return ok;
375 }
376
377 void writeSamples (int numSamples)
378 {
379 vorbis_analysis_wrote (&vd, numSamples);
380
381 while (vorbis_analysis_blockout (&vd, &vb) == 1)
382 {
383 vorbis_analysis (&vb, nullptr);
385
386 while (vorbis_bitrate_flushpacket (&vd, &op))
387 {
388 ogg_stream_packetin (&os, &op);
389
390 for (;;)
391 {
392 if (ogg_stream_pageout (&os, &og) == 0)
393 break;
394
395 output->write (og.header, (size_t) og.header_len);
396 output->write (og.body, (size_t) og.body_len);
397
398 if (ogg_page_eos (&og))
399 break;
400 }
401 }
402 }
403 }
404
405 bool ok = false;
406
407private:
408 OggVorbisNamespace::ogg_stream_state os;
409 OggVorbisNamespace::ogg_page og;
410 OggVorbisNamespace::ogg_packet op;
411 OggVorbisNamespace::vorbis_info vi;
412 OggVorbisNamespace::vorbis_comment vc;
413 OggVorbisNamespace::vorbis_dsp_state vd;
414 OggVorbisNamespace::vorbis_block vb;
415
416 void addMetadata (const StringPairArray& metadata, const char* name, const char* vorbisName)
417 {
418 auto s = metadata [name];
419
420 if (s.isNotEmpty())
421 vorbis_comment_add_tag (&vc, vorbisName, const_cast<char*> (s.toRawUTF8()));
422 }
423
424 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter)
425};
426
427
428//==============================================================================
429OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg")
430{
431}
432
433OggVorbisAudioFormat::~OggVorbisAudioFormat()
434{
435}
436
437Array<int> OggVorbisAudioFormat::getPossibleSampleRates()
438{
439 return { 8000, 11025, 12000, 16000, 22050, 32000,
440 44100, 48000, 88200, 96000, 176400, 192000 };
441}
442
443Array<int> OggVorbisAudioFormat::getPossibleBitDepths()
444{
445 return { 32 };
446}
447
448bool OggVorbisAudioFormat::canDoStereo() { return true; }
449bool OggVorbisAudioFormat::canDoMono() { return true; }
450bool OggVorbisAudioFormat::isCompressed() { return true; }
451
452AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, bool deleteStreamIfOpeningFails)
453{
454 std::unique_ptr<OggReader> r (new OggReader (in));
455
456 if (r->sampleRate > 0)
457 return r.release();
458
459 if (! deleteStreamIfOpeningFails)
460 r->input = nullptr;
461
462 return nullptr;
463}
464
465AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out,
466 double sampleRate,
467 unsigned int numChannels,
468 int bitsPerSample,
469 const StringPairArray& metadataValues,
470 int qualityOptionIndex)
471{
472 if (out == nullptr)
473 return nullptr;
474
475 std::unique_ptr<OggWriter> w (new OggWriter (out, sampleRate, numChannels,
476 (unsigned int) bitsPerSample,
477 qualityOptionIndex, metadataValues));
478
479 return w->ok ? w.release() : nullptr;
480}
481
482StringArray OggVorbisAudioFormat::getQualityOptions()
483{
484 return { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps",
485 "192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps" };
486}
487
488int OggVorbisAudioFormat::estimateOggFileQuality (const File& source)
489{
490 if (auto* in = source.createInputStream())
491 {
492 if (auto r = std::unique_ptr<AudioFormatReader> (createReaderFor (in, true)))
493 {
494 auto lengthSecs = r->lengthInSamples / r->sampleRate;
495 auto approxBitsPerSecond = (int) (source.getSize() * 8 / lengthSecs);
496
497 auto qualities = getQualityOptions();
498 int bestIndex = 0;
499 int bestDiff = 10000;
500
501 for (int i = qualities.size(); --i >= 0;)
502 {
503 auto diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond);
504
505 if (diff < bestDiff)
506 {
507 bestDiff = diff;
508 bestIndex = i;
509 }
510 }
511
512 return bestIndex;
513 }
514 }
515
516 return 0;
517}
518
519#endif
520
521} // namespace juce
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:219
Array()=default
Creates an empty array.
static const char *const id3title
Metadata key for setting an ID3 title.
static const char *const encoderName
Metadata property name used by the Ogg writer - if you set a string for this value,...
static const char *const id3comment
Metadata key for setting an ID3 comment.
static const char *const id3album
Metadata key for setting an ID3 album.
static const char *const id3date
Metadata key for setting an ID3 date.
static const char *const id3trackNumber
Metadata key for setting an ID3 track number.
static const char *const id3genre
Metadata key for setting an ID3 genre.
static const char *const id3artist
Metadata key for setting an ID3 artist name.
static Random & getSystemRandom() noexcept
The overhead of creating a new Random object is fairly small, but if you want to avoid it,...