JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_OggVorbisAudioFormat.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29#if JUCE_USE_OGGVORBIS
30
31#if JUCE_MAC && ! defined (__MACOSX__)
32 #define __MACOSX__ 1
33#endif
34
35namespace OggVorbisNamespace
36{
37#if JUCE_INCLUDE_OGGVORBIS_CODE || ! defined (JUCE_INCLUDE_OGGVORBIS_CODE)
38 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365 4456 4457 4459 6297 6011 6001 6308 6255 6386 6385 6246 6387 6263 6262 28182)
39
40 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-align",
41 "-Wconversion",
45 "-Wfloat-equal",
50 "-Wshadow",
53 "-Wswitch-enum",
54 "-Wzero-as-null-pointer-constant")
55 JUCE_BEGIN_NO_SANITIZE ("undefined")
56
57 #include "oggvorbis/vorbisenc.h"
58 #include "oggvorbis/codec.h"
59 #include "oggvorbis/vorbisfile.h"
60
61 #include "oggvorbis/bitwise.c"
62 #include "oggvorbis/framing.c"
63 #include "oggvorbis/libvorbis-1.3.7/lib/analysis.c"
64 #include "oggvorbis/libvorbis-1.3.7/lib/bitrate.c"
65 #include "oggvorbis/libvorbis-1.3.7/lib/block.c"
66 #include "oggvorbis/libvorbis-1.3.7/lib/codebook.c"
67 #include "oggvorbis/libvorbis-1.3.7/lib/envelope.c"
68 #include "oggvorbis/libvorbis-1.3.7/lib/floor0.c"
69 #include "oggvorbis/libvorbis-1.3.7/lib/floor1.c"
70 #include "oggvorbis/libvorbis-1.3.7/lib/info.c"
71 #include "oggvorbis/libvorbis-1.3.7/lib/lpc.c"
72 #include "oggvorbis/libvorbis-1.3.7/lib/lsp.c"
73 #include "oggvorbis/libvorbis-1.3.7/lib/mapping0.c"
74 #include "oggvorbis/libvorbis-1.3.7/lib/mdct.c"
75 #include "oggvorbis/libvorbis-1.3.7/lib/psy.c"
76 #include "oggvorbis/libvorbis-1.3.7/lib/registry.c"
77 #include "oggvorbis/libvorbis-1.3.7/lib/res0.c"
78 #include "oggvorbis/libvorbis-1.3.7/lib/sharedbook.c"
79 #include "oggvorbis/libvorbis-1.3.7/lib/smallft.c"
80 #include "oggvorbis/libvorbis-1.3.7/lib/synthesis.c"
81 #include "oggvorbis/libvorbis-1.3.7/lib/vorbisenc.c"
82 #include "oggvorbis/libvorbis-1.3.7/lib/vorbisfile.c"
83 #include "oggvorbis/libvorbis-1.3.7/lib/window.c"
84
85 JUCE_END_NO_SANITIZE
86 JUCE_END_IGNORE_WARNINGS_MSVC
87 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
88#else
89 #include <vorbis/vorbisenc.h>
90 #include <vorbis/codec.h>
91 #include <vorbis/vorbisfile.h>
92#endif
93}
94
95#undef max
96#undef min
97
98//==============================================================================
99static const char* const oggFormatName = "Ogg-Vorbis file";
100
101const char* const OggVorbisAudioFormat::encoderName = "encoder";
102const char* const OggVorbisAudioFormat::id3title = "id3title";
103const char* const OggVorbisAudioFormat::id3artist = "id3artist";
104const char* const OggVorbisAudioFormat::id3album = "id3album";
105const char* const OggVorbisAudioFormat::id3comment = "id3comment";
106const char* const OggVorbisAudioFormat::id3date = "id3date";
107const char* const OggVorbisAudioFormat::id3genre = "id3genre";
108const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber";
109
110
111//==============================================================================
112class OggReader final : public AudioFormatReader
113{
114public:
115 OggReader (InputStream* inp) : AudioFormatReader (inp, oggFormatName)
116 {
117 sampleRate = 0;
118 usesFloatingPointData = true;
119
120 callbacks.read_func = &oggReadCallback;
121 callbacks.seek_func = &oggSeekCallback;
122 callbacks.close_func = &oggCloseCallback;
123 callbacks.tell_func = &oggTellCallback;
124
125 auto err = ov_open_callbacks (input, &ovFile, nullptr, 0, callbacks);
126
127 if (err == 0)
128 {
129 auto* info = ov_info (&ovFile, -1);
130
131 auto* comment = ov_comment (&ovFile, -1);
140
141 lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1);
142 numChannels = (unsigned int) info->channels;
143 bitsPerSample = 16;
144 sampleRate = (double) info->rate;
145
146 reservoir.setSize ((int) numChannels, (int) jmin (lengthInSamples, (int64) 4096));
147 }
148 }
149
150 ~OggReader() override
151 {
152 ov_clear (&ovFile);
153 }
154
155 void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment, const char* name, const char* metadataName)
156 {
157 if (auto* value = vorbis_comment_query (comment, name, 0))
158 metadataValues.set (metadataName, value);
159 }
160
161 //==============================================================================
162 bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer,
163 int64 startSampleInFile, int numSamples) override
164 {
165 const auto getBufferedRange = [this] { return bufferedRange; };
166
168 {
169 const auto bufferIndices = rangeToRead - bufferedRange.getStart();
170 const auto writePos = (int64) startOffsetInDestBuffer + (rangeToRead.getStart() - startSampleInFile);
171
172 for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
173 if (destSamples[i] != nullptr)
174 memcpy (destSamples[i] + writePos,
175 reservoir.getReadPointer (i) + bufferIndices.getStart(),
176 (size_t) bufferIndices.getLength() * sizeof (float));
177 };
178
179 const auto fillReservoir = [this] (int64 requestedStart)
180 {
181 const auto newStart = jmax ((int64) 0, requestedStart);
182 bufferedRange = Range<int64> { newStart, newStart + reservoir.getNumSamples() };
183
184 if (bufferedRange.getStart() != ov_pcm_tell (&ovFile))
185 ov_pcm_seek (&ovFile, bufferedRange.getStart());
186
187 int bitStream = 0;
188 int offset = 0;
189 int numToRead = (int) bufferedRange.getLength();
190
191 while (numToRead > 0)
192 {
193 float** dataIn = nullptr;
194 auto samps = static_cast<int> (ov_read_float (&ovFile, &dataIn, numToRead, &bitStream));
195
196 if (samps <= 0)
197 break;
198
200
201 for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;)
202 memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps * sizeof (float));
203
204 numToRead -= samps;
205 offset += samps;
206 }
207
208 if (numToRead > 0)
209 reservoir.clear (offset, numToRead);
210 };
211
212 const auto remainingSamples = Reservoir::doBufferedRead (Range<int64> { startSampleInFile, startSampleInFile + numSamples },
216
217 if (! remainingSamples.isEmpty())
218 for (int i = numDestChannels; --i >= 0;)
219 if (destSamples[i] != nullptr)
221 (size_t) remainingSamples.getLength() * sizeof (int));
222
223 return true;
224 }
225
226 //==============================================================================
227 static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource)
228 {
229 return (size_t) (static_cast<InputStream*> (datasource)->read (ptr, (int) (size * nmemb))) / size;
230 }
231
232 static int oggSeekCallback (void* datasource, OggVorbisNamespace::ogg_int64_t offset, int whence)
233 {
234 auto* in = static_cast<InputStream*> (datasource);
235
236 if (whence == SEEK_CUR)
237 offset += in->getPosition();
238 else if (whence == SEEK_END)
239 offset += in->getTotalLength();
240
241 in->setPosition (offset);
242 return 0;
243 }
244
245 static int oggCloseCallback (void*)
246 {
247 return 0;
248 }
249
250 static long oggTellCallback (void* datasource)
251 {
252 return (long) static_cast<InputStream*> (datasource)->getPosition();
253 }
254
255private:
256 OggVorbisNamespace::OggVorbis_File ovFile;
257 OggVorbisNamespace::ov_callbacks callbacks;
258 AudioBuffer<float> reservoir;
259 Range<int64> bufferedRange;
260
262};
263
264//==============================================================================
265class OggWriter final : public AudioFormatWriter
266{
267public:
268 OggWriter (OutputStream* out, double rate,
269 unsigned int numChans, unsigned int bitsPerSamp,
270 int qualityIndex, const StringPairArray& metadata)
271 : AudioFormatWriter (out, oggFormatName, rate, numChans, bitsPerSamp)
272 {
274
275 if (vorbis_encode_init_vbr (&vi, (int) numChans, (int) rate,
276 jlimit (0.0f, 1.0f, (float) qualityIndex * 0.1f)) == 0)
277 {
279
288
291
293
294 OggVorbisNamespace::ogg_packet header, header_comm, header_code;
296
297 ogg_stream_packetin (&os, &header);
300
301 for (;;)
302 {
303 if (ogg_stream_flush (&os, &og) == 0)
304 break;
305
306 output->write (og.header, (size_t) og.header_len);
307 output->write (og.body, (size_t) og.body_len);
308 }
309
310 ok = true;
311 }
312 }
313
314 ~OggWriter() override
315 {
316 if (ok)
317 {
318 // write a zero-length packet to show ogg that we're finished..
319 writeSamples (0);
320
325
327 output->flush();
328 }
329 else
330 {
332 output = nullptr; // to stop the base class deleting this, as it needs to be returned
333 // to the caller of createWriter()
334 }
335 }
336
337 //==============================================================================
338 bool write (const int** samplesToWrite, int numSamples) override
339 {
340 if (ok)
341 {
342 if (numSamples > 0)
343 {
344 const double gain = 1.0 / 0x80000000u;
345 float** const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples);
346
347 for (int i = (int) numChannels; --i >= 0;)
348 {
349 if (auto* dst = vorbisBuffer[i])
350 {
351 if (const int* src = samplesToWrite [i])
352 {
353 for (int j = 0; j < numSamples; ++j)
354 dst[j] = (float) (src[j] * gain);
355 }
356 }
357 }
358 }
359
360 writeSamples (numSamples);
361 }
362
363 return ok;
364 }
365
366 void writeSamples (int numSamples)
367 {
368 vorbis_analysis_wrote (&vd, numSamples);
369
370 while (vorbis_analysis_blockout (&vd, &vb) == 1)
371 {
372 vorbis_analysis (&vb, nullptr);
374
375 while (vorbis_bitrate_flushpacket (&vd, &op))
376 {
377 ogg_stream_packetin (&os, &op);
378
379 for (;;)
380 {
381 if (ogg_stream_pageout (&os, &og) == 0)
382 break;
383
384 output->write (og.header, (size_t) og.header_len);
385 output->write (og.body, (size_t) og.body_len);
386
387 if (ogg_page_eos (&og))
388 break;
389 }
390 }
391 }
392 }
393
394 bool ok = false;
395
396private:
397 OggVorbisNamespace::ogg_stream_state os;
398 OggVorbisNamespace::ogg_page og;
399 OggVorbisNamespace::ogg_packet op;
400 OggVorbisNamespace::vorbis_info vi;
401 OggVorbisNamespace::vorbis_comment vc;
402 OggVorbisNamespace::vorbis_dsp_state vd;
403 OggVorbisNamespace::vorbis_block vb;
404
405 void addMetadata (const StringPairArray& metadata, const char* name, const char* vorbisName)
406 {
407 auto s = metadata [name];
408
409 if (s.isNotEmpty())
410 vorbis_comment_add_tag (&vc, vorbisName, const_cast<char*> (s.toRawUTF8()));
411 }
412
414};
415
416
417//==============================================================================
418OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg")
419{
420}
421
422OggVorbisAudioFormat::~OggVorbisAudioFormat()
423{
424}
425
426Array<int> OggVorbisAudioFormat::getPossibleSampleRates()
427{
428 return { 8000, 11025, 12000, 16000, 22050, 32000,
429 44100, 48000, 88200, 96000, 176400, 192000 };
430}
431
432Array<int> OggVorbisAudioFormat::getPossibleBitDepths()
433{
434 return { 32 };
435}
436
437bool OggVorbisAudioFormat::canDoStereo() { return true; }
438bool OggVorbisAudioFormat::canDoMono() { return true; }
439bool OggVorbisAudioFormat::isCompressed() { return true; }
440
441AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, bool deleteStreamIfOpeningFails)
442{
443 std::unique_ptr<OggReader> r (new OggReader (in));
444
445 if (r->sampleRate > 0)
446 return r.release();
447
448 if (! deleteStreamIfOpeningFails)
449 r->input = nullptr;
450
451 return nullptr;
452}
453
454AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out,
455 double sampleRate,
456 unsigned int numChannels,
457 int bitsPerSample,
458 const StringPairArray& metadataValues,
459 int qualityOptionIndex)
460{
461 if (out == nullptr)
462 return nullptr;
463
464 std::unique_ptr<OggWriter> w (new OggWriter (out, sampleRate, numChannels,
465 (unsigned int) bitsPerSample,
466 qualityOptionIndex, metadataValues));
467
468 return w->ok ? w.release() : nullptr;
469}
470
471StringArray OggVorbisAudioFormat::getQualityOptions()
472{
473 return { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps",
474 "192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps" };
475}
476
477int OggVorbisAudioFormat::estimateOggFileQuality (const File& source)
478{
479 if (auto in = source.createInputStream())
480 {
481 if (auto r = std::unique_ptr<AudioFormatReader> (createReaderFor (in.release(), true)))
482 {
483 auto lengthSecs = (double) r->lengthInSamples / r->sampleRate;
484 auto approxBitsPerSecond = (int) ((double) source.getSize() * 8 / lengthSecs);
485
486 auto qualities = getQualityOptions();
487 int bestIndex = 0;
488 int bestDiff = 10000;
489
490 for (int i = qualities.size(); --i >= 0;)
491 {
492 auto diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond);
493
494 if (diff < bestDiff)
495 {
496 bestDiff = diff;
497 bestIndex = i;
498 }
499 }
500
501 return bestIndex;
502 }
503 }
504
505 return 0;
506}
507
508#endif
509
510} // namespace juce
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,...
#define JUCE_BEGIN_NO_SANITIZE(warnings)
Disable sanitizers for a range of functions.
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
typedef int
typedef double
memcpy
JUCE Namespace.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
long long int64
A platform-independent 64-bit integer type.
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.
Definition juce_Memory.h:28
write
T size(T... args)
static Range< Index > doBufferedRead(Range< Index > rangeToRead, GetBufferedRange &&getBufferedRange, ReadFromReservoir &&readFromReservoir, FillReservoir &&fillReservoir)
Attempts to read the requested range from some kind of input stream, with intermediate buffering in a...
typedef size_t