LMMS
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
41 "-Wshadow",
42 "-Wfloat-conversion",
43 "-Wdeprecated-register",
44 "-Wdeprecated-declarations",
45 "-Wswitch-enum",
46 "-Wzero-as-null-pointer-constant",
47 "-Wsign-conversion",
48 "-Wswitch-default",
49 "-Wredundant-decls",
50 "-Wmisleading-indentation",
51 "-Wmissing-prototypes",
52 "-Wcast-align",
53 "-Wmaybe-uninitialized")
54 JUCE_BEGIN_NO_SANITIZE ("undefined")
55
56 #include "oggvorbis/vorbisenc.h"
57 #include "oggvorbis/codec.h"
58 #include "oggvorbis/vorbisfile.h"
59
60 #include "oggvorbis/bitwise.c"
61 #include "oggvorbis/framing.c"
83
87#else
88 #include <vorbis/vorbisenc.h>
89 #include <vorbis/codec.h>
90 #include <vorbis/vorbisfile.h>
91#endif
92}
93
94#undef max
95#undef min
96
97//==============================================================================
98static const char* const oggFormatName = "Ogg-Vorbis file";
99
100const char* const OggVorbisAudioFormat::encoderName = "encoder";
101const char* const OggVorbisAudioFormat::id3title = "id3title";
102const char* const OggVorbisAudioFormat::id3artist = "id3artist";
103const char* const OggVorbisAudioFormat::id3album = "id3album";
104const char* const OggVorbisAudioFormat::id3comment = "id3comment";
105const char* const OggVorbisAudioFormat::id3date = "id3date";
106const char* const OggVorbisAudioFormat::id3genre = "id3genre";
107const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber";
108
109
110//==============================================================================
111class OggReader : public AudioFormatReader
112{
113public:
114 OggReader (InputStream* inp) : AudioFormatReader (inp, oggFormatName)
115 {
116 sampleRate = 0;
117 usesFloatingPointData = true;
118
119 callbacks.read_func = &oggReadCallback;
120 callbacks.seek_func = &oggSeekCallback;
121 callbacks.close_func = &oggCloseCallback;
122 callbacks.tell_func = &oggTellCallback;
123
124 auto err = ov_open_callbacks (input, &ovFile, nullptr, 0, callbacks);
125
126 if (err == 0)
127 {
128 auto* info = ov_info (&ovFile, -1);
129
130 auto* comment = ov_comment (&ovFile, -1);
131 addMetadataItem (comment, "ENCODER", OggVorbisAudioFormat::encoderName);
132 addMetadataItem (comment, "TITLE", OggVorbisAudioFormat::id3title);
133 addMetadataItem (comment, "ARTIST", OggVorbisAudioFormat::id3artist);
134 addMetadataItem (comment, "ALBUM", OggVorbisAudioFormat::id3album);
135 addMetadataItem (comment, "COMMENT", OggVorbisAudioFormat::id3comment);
136 addMetadataItem (comment, "DATE", OggVorbisAudioFormat::id3date);
137 addMetadataItem (comment, "GENRE", OggVorbisAudioFormat::id3genre);
138 addMetadataItem (comment, "TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber);
139
140 lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1);
141 numChannels = (unsigned int) info->channels;
142 bitsPerSample = 16;
143 sampleRate = (double) info->rate;
144
145 reservoir.setSize ((int) numChannels, (int) jmin (lengthInSamples, (int64) 4096));
146 }
147 }
148
149 ~OggReader() override
150 {
151 ov_clear (&ovFile);
152 }
153
154 void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment, const char* name, const char* metadataName)
155 {
156 if (auto* value = vorbis_comment_query (comment, name, 0))
157 metadataValues.set (metadataName, value);
158 }
159
160 //==============================================================================
161 bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
162 int64 startSampleInFile, int numSamples) override
163 {
164 const auto getBufferedRange = [this] { return bufferedRange; };
165
166 const auto readFromReservoir = [this, &destSamples, &numDestChannels, &startOffsetInDestBuffer, &startSampleInFile] (const Range<int64> rangeToRead)
167 {
168 const auto bufferIndices = rangeToRead - bufferedRange.getStart();
169 const auto writePos = (int64) startOffsetInDestBuffer + (rangeToRead.getStart() - startSampleInFile);
170
171 for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
172 if (destSamples[i] != nullptr)
173 memcpy (destSamples[i] + writePos,
174 reservoir.getReadPointer (i) + bufferIndices.getStart(),
175 (size_t) bufferIndices.getLength() * sizeof (float));
176 };
177
178 const auto fillReservoir = [this] (int64 requestedStart)
179 {
180 const auto newStart = jmax ((int64) 0, requestedStart);
181 bufferedRange = Range<int64> { newStart, newStart + reservoir.getNumSamples() };
182
183 if (bufferedRange.getStart() != ov_pcm_tell (&ovFile))
184 ov_pcm_seek (&ovFile, bufferedRange.getStart());
185
186 int bitStream = 0;
187 int offset = 0;
188 int numToRead = (int) bufferedRange.getLength();
189
190 while (numToRead > 0)
191 {
192 float** dataIn = nullptr;
193 auto samps = static_cast<int> (ov_read_float (&ovFile, &dataIn, numToRead, &bitStream));
194
195 if (samps <= 0)
196 break;
197
198 jassert (samps <= numToRead);
199
200 for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;)
201 memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps * sizeof (float));
202
203 numToRead -= samps;
204 offset += samps;
205 }
206
207 if (numToRead > 0)
208 reservoir.clear (offset, numToRead);
209 };
210
211 const auto remainingSamples = Reservoir::doBufferedRead (Range<int64> { startSampleInFile, startSampleInFile + numSamples },
212 getBufferedRange,
213 readFromReservoir,
214 fillReservoir);
215
216 if (! remainingSamples.isEmpty())
217 for (int i = numDestChannels; --i >= 0;)
218 if (destSamples[i] != nullptr)
219 zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) remainingSamples.getLength() * sizeof (int));
220
221 return true;
222 }
223
224 //==============================================================================
225 static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource)
226 {
227 return (size_t) (static_cast<InputStream*> (datasource)->read (ptr, (int) (size * nmemb))) / size;
228 }
229
230 static int oggSeekCallback (void* datasource, OggVorbisNamespace::ogg_int64_t offset, int whence)
231 {
232 auto* in = static_cast<InputStream*> (datasource);
233
234 if (whence == SEEK_CUR)
235 offset += in->getPosition();
236 else if (whence == SEEK_END)
237 offset += in->getTotalLength();
238
239 in->setPosition (offset);
240 return 0;
241 }
242
243 static int oggCloseCallback (void*)
244 {
245 return 0;
246 }
247
248 static long oggTellCallback (void* datasource)
249 {
250 return (long) static_cast<InputStream*> (datasource)->getPosition();
251 }
252
253private:
254 OggVorbisNamespace::OggVorbis_File ovFile;
255 OggVorbisNamespace::ov_callbacks callbacks;
256 AudioBuffer<float> reservoir;
257 Range<int64> bufferedRange;
258
260};
261
262//==============================================================================
263class OggWriter : public AudioFormatWriter
264{
265public:
266 OggWriter (OutputStream* out, double rate,
267 unsigned int numChans, unsigned int bitsPerSamp,
268 int qualityIndex, const StringPairArray& metadata)
269 : AudioFormatWriter (out, oggFormatName, rate, numChans, bitsPerSamp)
270 {
271 vorbis_info_init (&vi);
272
273 if (vorbis_encode_init_vbr (&vi, (int) numChans, (int) rate,
274 jlimit (0.0f, 1.0f, (float) qualityIndex * 0.1f)) == 0)
275 {
277
278 addMetadata (metadata, OggVorbisAudioFormat::encoderName, "ENCODER");
279 addMetadata (metadata, OggVorbisAudioFormat::id3title, "TITLE");
280 addMetadata (metadata, OggVorbisAudioFormat::id3artist, "ARTIST");
281 addMetadata (metadata, OggVorbisAudioFormat::id3album, "ALBUM");
282 addMetadata (metadata, OggVorbisAudioFormat::id3comment, "COMMENT");
283 addMetadata (metadata, OggVorbisAudioFormat::id3date, "DATE");
284 addMetadata (metadata, OggVorbisAudioFormat::id3genre, "GENRE");
285 addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber, "TRACKNUMBER");
286
287 vorbis_analysis_init (&vd, &vi);
288 vorbis_block_init (&vd, &vb);
289
290 ogg_stream_init (&os, Random::getSystemRandom().nextInt());
291
292 OggVorbisNamespace::ogg_packet header, header_comm, header_code;
293 vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code);
294
295 ogg_stream_packetin (&os, &header);
296 ogg_stream_packetin (&os, &header_comm);
297 ogg_stream_packetin (&os, &header_code);
298
299 for (;;)
300 {
301 if (ogg_stream_flush (&os, &og) == 0)
302 break;
303
304 output->write (og.header, (size_t) og.header_len);
305 output->write (og.body, (size_t) og.body_len);
306 }
307
308 ok = true;
309 }
310 }
311
312 ~OggWriter() override
313 {
314 if (ok)
315 {
316 // write a zero-length packet to show ogg that we're finished..
317 writeSamples (0);
318
320 vorbis_block_clear (&vb);
321 vorbis_dsp_clear (&vd);
323
324 vorbis_info_clear (&vi);
325 output->flush();
326 }
327 else
328 {
329 vorbis_info_clear (&vi);
330 output = nullptr; // to stop the base class deleting this, as it needs to be returned
331 // to the caller of createWriter()
332 }
333 }
334
335 //==============================================================================
336 bool write (const int** samplesToWrite, int numSamples) override
337 {
338 if (ok)
339 {
340 if (numSamples > 0)
341 {
342 const double gain = 1.0 / 0x80000000u;
343 float** const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples);
344
345 for (int i = (int) numChannels; --i >= 0;)
346 {
347 if (auto* dst = vorbisBuffer[i])
348 {
349 if (const int* src = samplesToWrite [i])
350 {
351 for (int j = 0; j < numSamples; ++j)
352 dst[j] = (float) (src[j] * gain);
353 }
354 }
355 }
356 }
357
358 writeSamples (numSamples);
359 }
360
361 return ok;
362 }
363
364 void writeSamples (int numSamples)
365 {
366 vorbis_analysis_wrote (&vd, numSamples);
367
368 while (vorbis_analysis_blockout (&vd, &vb) == 1)
369 {
370 vorbis_analysis (&vb, nullptr);
372
373 while (vorbis_bitrate_flushpacket (&vd, &op))
374 {
375 ogg_stream_packetin (&os, &op);
376
377 for (;;)
378 {
379 if (ogg_stream_pageout (&os, &og) == 0)
380 break;
381
382 output->write (og.header, (size_t) og.header_len);
383 output->write (og.body, (size_t) og.body_len);
384
385 if (ogg_page_eos (&og))
386 break;
387 }
388 }
389 }
390 }
391
392 bool ok = false;
393
394private:
395 OggVorbisNamespace::ogg_stream_state os;
396 OggVorbisNamespace::ogg_page og;
397 OggVorbisNamespace::ogg_packet op;
398 OggVorbisNamespace::vorbis_info vi;
399 OggVorbisNamespace::vorbis_comment vc;
400 OggVorbisNamespace::vorbis_dsp_state vd;
401 OggVorbisNamespace::vorbis_block vb;
402
403 void addMetadata (const StringPairArray& metadata, const char* name, const char* vorbisName)
404 {
405 auto s = metadata [name];
406
407 if (s.isNotEmpty())
408 vorbis_comment_add_tag (&vc, vorbisName, const_cast<char*> (s.toRawUTF8()));
409 }
410
412};
413
414
415//==============================================================================
416OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg")
417{
418}
419
420OggVorbisAudioFormat::~OggVorbisAudioFormat()
421{
422}
423
424Array<int> OggVorbisAudioFormat::getPossibleSampleRates()
425{
426 return { 8000, 11025, 12000, 16000, 22050, 32000,
427 44100, 48000, 88200, 96000, 176400, 192000 };
428}
429
430Array<int> OggVorbisAudioFormat::getPossibleBitDepths()
431{
432 return { 32 };
433}
434
435bool OggVorbisAudioFormat::canDoStereo() { return true; }
436bool OggVorbisAudioFormat::canDoMono() { return true; }
437bool OggVorbisAudioFormat::isCompressed() { return true; }
438
439AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, bool deleteStreamIfOpeningFails)
440{
441 std::unique_ptr<OggReader> r (new OggReader (in));
442
443 if (r->sampleRate > 0)
444 return r.release();
445
446 if (! deleteStreamIfOpeningFails)
447 r->input = nullptr;
448
449 return nullptr;
450}
451
452AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out,
453 double sampleRate,
454 unsigned int numChannels,
455 int bitsPerSample,
456 const StringPairArray& metadataValues,
457 int qualityOptionIndex)
458{
459 if (out == nullptr)
460 return nullptr;
461
462 std::unique_ptr<OggWriter> w (new OggWriter (out, sampleRate, numChannels,
463 (unsigned int) bitsPerSample,
464 qualityOptionIndex, metadataValues));
465
466 return w->ok ? w.release() : nullptr;
467}
468
469StringArray OggVorbisAudioFormat::getQualityOptions()
470{
471 return { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps",
472 "192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps" };
473}
474
475int OggVorbisAudioFormat::estimateOggFileQuality (const File& source)
476{
477 if (auto in = source.createInputStream())
478 {
479 if (auto r = std::unique_ptr<AudioFormatReader> (createReaderFor (in.release(), true)))
480 {
481 auto lengthSecs = (double) r->lengthInSamples / r->sampleRate;
482 auto approxBitsPerSecond = (int) ((double) source.getSize() * 8 / lengthSecs);
483
484 auto qualities = getQualityOptions();
485 int bestIndex = 0;
486 int bestDiff = 10000;
487
488 for (int i = qualities.size(); --i >= 0;)
489 {
490 auto diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond);
491
492 if (diff < bestDiff)
493 {
494 bestDiff = diff;
495 bestIndex = i;
496 }
497 }
498
499 return bestIndex;
500 }
501 }
502
503 return 0;
504}
505
506#endif
507
508} // namespace juce
Type jmin(const Type a, const Type b)
Definition MathsFunctions.h:60
Type jmax(const Type a, const Type b)
Definition MathsFunctions.h:48
int64_t int64
Definition basics.h:91
uint32_t uint32
Definition basics.h:90
Definition Array.h:57
Definition File.h:50
FileInputStream * createInputStream() const
Definition File.cpp:733
int64 getSize() const
Definition File.cpp:1352
Definition StringArray.h:41
void vorbis_comment_init(vorbis_comment *vc)
Definition info.c:54
int vorbis_analysis_blockout(vorbis_dsp_state *v, vorbis_block *vb)
Definition block.c:523
void vorbis_comment_add_tag(vorbis_comment *vc, const char *tag, const char *contents)
Definition info.c:70
char * vorbis_comment_query(vorbis_comment *vc, const char *tag, int count)
Definition info.c:92
void vorbis_comment_clear(vorbis_comment *vc)
Definition info.c:132
int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, ogg_packet *op)
Definition bitrate.c:229
int vorbis_analysis(vorbis_block *vb, ogg_packet *op)
Definition analysis.c:29
void vorbis_dsp_clear(vorbis_dsp_state *v)
Definition block.c:314
float ** vorbis_analysis_buffer(vorbis_dsp_state *v, int vals)
Definition block.c:388
int vorbis_block_clear(vorbis_block *vb)
Definition block.c:148
int vorbis_analysis_headerout(vorbis_dsp_state *v, vorbis_comment *vc, ogg_packet *op, ogg_packet *op_comm, ogg_packet *op_code)
Definition info.c:590
int vorbis_bitrate_addblock(vorbis_block *vb)
Definition bitrate.c:73
int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb)
Definition block.c:77
void vorbis_info_clear(vorbis_info *vi)
Definition info.c:159
int vorbis_analysis_init(vorbis_dsp_state *v, vorbis_info *vi)
Definition block.c:294
int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals)
Definition block.c:459
void vorbis_info_init(vorbis_info *vi)
Definition info.c:154
UINT_D64 w
Definition inflate.c:942
register unsigned j
Definition inflate.c:1576
register unsigned i
Definition inflate.c:1575
unsigned s
Definition inflate.c:1555
int ogg_stream_clear(ogg_stream_state *os)
Definition framing.c:162
int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op)
Definition framing.c:339
int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og)
Definition framing.c:510
int ogg_stream_flush(ogg_stream_state *os, ogg_page *og)
Definition framing.c:494
int ogg_stream_init(ogg_stream_state *os, int serialno)
Definition framing.c:133
int ogg_page_eos(const ogg_page *og)
Definition framing.c:45
static PuglViewHint int value
Definition pugl.h:1708
static const char * name
Definition pugl.h:1582
struct backing_store_struct * info
Definition jmemsys.h:183
#define JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE(...)
Definition juce_CompilerWarnings.h:181
#define JUCE_END_IGNORE_WARNINGS_GCC_LIKE
Definition juce_CompilerWarnings.h:182
#define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings)
Definition juce_CompilerWarnings.h:198
#define JUCE_END_NO_SANITIZE
Definition juce_CompilerWarnings.h:218
#define JUCE_END_IGNORE_WARNINGS_MSVC
Definition juce_CompilerWarnings.h:199
#define JUCE_BEGIN_NO_SANITIZE(warnings)
Definition juce_CompilerWarnings.h:217
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
float in
Definition lilv_test.c:1460
float out
Definition lilv_test.c:1461
Definition carla_juce.cpp:31
void zeromem(void *memory, size_t numBytes) noexcept
Definition juce_Memory.h:28
static bool diff(const std::string fn1, const std::string fn2)
Definition playertest.cpp:161
memcpy(hh, h, RAND_HEAD_LEN)
int r
Definition crypt.c:458
ulg size
Definition extract.c:2350
typedef int(UZ_EXP MsgFn)()
#define SEEK_CUR
Definition unzpriv.h:1303
#define SEEK_END
Definition unzpriv.h:1304
static ZCONST char Far * os[NUM_HOSTS]
Definition zipinfo.c:1001
int vorbis_encode_init_vbr(vorbis_info *vi, long channels, long rate, float base_quality)
Definition vorbisenc.c:927
vorbis_info * ov_info(OggVorbis_File *vf, int link)
Definition vorbisfile.c:1906
int ov_pcm_seek(OggVorbis_File *vf, ogg_int64_t pos)
Definition vorbisfile.c:1705
vorbis_comment * ov_comment(OggVorbis_File *vf, int link)
Definition vorbisfile.c:1924
int ov_clear(OggVorbis_File *vf)
Definition vorbisfile.c:972
long ov_read_float(OggVorbis_File *vf, float ***pcm_channels, int length, int *bitstream)
Definition vorbisfile.c:2145
ogg_int64_t ov_pcm_total(OggVorbis_File *vf, int i)
Definition vorbisfile.c:1210
ogg_int64_t ov_pcm_tell(OggVorbis_File *vf)
Definition vorbisfile.c:1871
int ov_open_callbacks(void *f, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks)
Definition vorbisfile.c:1010