OpenShot Audio Library | OpenShotAudio  0.6.0
juce_OggVorbisAudioFormat.cpp
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 
26 namespace juce
27 {
28 
29 #if JUCE_USE_OGGVORBIS
30 
31 #if JUCE_MAC && ! defined (__MACOSX__)
32  #define __MACOSX__ 1
33 #endif
34 
35 namespace 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",
42  "-Wdeprecated-declarations",
43  "-Wdeprecated-register",
44  "-Wfloat-conversion",
45  "-Wfloat-equal",
46  "-Wmaybe-uninitialized",
47  "-Wmisleading-indentation",
48  "-Wmissing-prototypes",
49  "-Wredundant-decls",
50  "-Wshadow",
51  "-Wsign-conversion",
52  "-Wswitch-default",
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 //==============================================================================
99 static const char* const oggFormatName = "Ogg-Vorbis file";
100 
101 const char* const OggVorbisAudioFormat::encoderName = "encoder";
102 const char* const OggVorbisAudioFormat::id3title = "id3title";
103 const char* const OggVorbisAudioFormat::id3artist = "id3artist";
104 const char* const OggVorbisAudioFormat::id3album = "id3album";
105 const char* const OggVorbisAudioFormat::id3comment = "id3comment";
106 const char* const OggVorbisAudioFormat::id3date = "id3date";
107 const char* const OggVorbisAudioFormat::id3genre = "id3genre";
108 const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber";
109 
110 
111 //==============================================================================
112 class OggReader final : public AudioFormatReader
113 {
114 public:
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);
132  addMetadataItem (comment, "ENCODER", OggVorbisAudioFormat::encoderName);
133  addMetadataItem (comment, "TITLE", OggVorbisAudioFormat::id3title);
134  addMetadataItem (comment, "ARTIST", OggVorbisAudioFormat::id3artist);
135  addMetadataItem (comment, "ALBUM", OggVorbisAudioFormat::id3album);
136  addMetadataItem (comment, "COMMENT", OggVorbisAudioFormat::id3comment);
137  addMetadataItem (comment, "DATE", OggVorbisAudioFormat::id3date);
138  addMetadataItem (comment, "GENRE", OggVorbisAudioFormat::id3genre);
139  addMetadataItem (comment, "TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber);
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 
167  const auto readFromReservoir = [this, &destSamples, &numDestChannels, &startOffsetInDestBuffer, &startSampleInFile] (const Range<int64> rangeToRead)
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 
199  jassert (samps <= numToRead);
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 },
213  getBufferedRange,
214  readFromReservoir,
215  fillReservoir);
216 
217  if (! remainingSamples.isEmpty())
218  for (int i = numDestChannels; --i >= 0;)
219  if (destSamples[i] != nullptr)
220  zeromem (destSamples[i] + startOffsetInDestBuffer + (remainingSamples.getStart() - startSampleInFile),
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 
255 private:
256  OggVorbisNamespace::OggVorbis_File ovFile;
257  OggVorbisNamespace::ov_callbacks callbacks;
258  AudioBuffer<float> reservoir;
259  Range<int64> bufferedRange;
260 
261  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
262 };
263 
264 //==============================================================================
265 class OggWriter final : public AudioFormatWriter
266 {
267 public:
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  {
273  vorbis_info_init (&vi);
274 
275  if (vorbis_encode_init_vbr (&vi, (int) numChans, (int) rate,
276  jlimit (0.0f, 1.0f, (float) qualityIndex * 0.1f)) == 0)
277  {
278  vorbis_comment_init (&vc);
279 
280  addMetadata (metadata, OggVorbisAudioFormat::encoderName, "ENCODER");
281  addMetadata (metadata, OggVorbisAudioFormat::id3title, "TITLE");
282  addMetadata (metadata, OggVorbisAudioFormat::id3artist, "ARTIST");
283  addMetadata (metadata, OggVorbisAudioFormat::id3album, "ALBUM");
284  addMetadata (metadata, OggVorbisAudioFormat::id3comment, "COMMENT");
285  addMetadata (metadata, OggVorbisAudioFormat::id3date, "DATE");
286  addMetadata (metadata, OggVorbisAudioFormat::id3genre, "GENRE");
287  addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber, "TRACKNUMBER");
288 
289  vorbis_analysis_init (&vd, &vi);
290  vorbis_block_init (&vd, &vb);
291 
292  ogg_stream_init (&os, Random::getSystemRandom().nextInt());
293 
294  OggVorbisNamespace::ogg_packet header, header_comm, header_code;
295  vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code);
296 
297  ogg_stream_packetin (&os, &header);
298  ogg_stream_packetin (&os, &header_comm);
299  ogg_stream_packetin (&os, &header_code);
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 
321  ogg_stream_clear (&os);
322  vorbis_block_clear (&vb);
323  vorbis_dsp_clear (&vd);
324  vorbis_comment_clear (&vc);
325 
326  vorbis_info_clear (&vi);
327  output->flush();
328  }
329  else
330  {
331  vorbis_info_clear (&vi);
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);
373  vorbis_bitrate_addblock (&vb);
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 
396 private:
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 
413  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter)
414 };
415 
416 
417 //==============================================================================
418 OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg")
419 {
420 }
421 
422 OggVorbisAudioFormat::~OggVorbisAudioFormat()
423 {
424 }
425 
426 Array<int> OggVorbisAudioFormat::getPossibleSampleRates()
427 {
428  return { 8000, 11025, 12000, 16000, 22050, 32000,
429  44100, 48000, 88200, 96000, 176400, 192000 };
430 }
431 
432 Array<int> OggVorbisAudioFormat::getPossibleBitDepths()
433 {
434  return { 32 };
435 }
436 
437 bool OggVorbisAudioFormat::canDoStereo() { return true; }
438 bool OggVorbisAudioFormat::canDoMono() { return true; }
439 bool OggVorbisAudioFormat::isCompressed() { return true; }
440 
441 AudioFormatReader* 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 
454 AudioFormatWriter* 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 
471 StringArray 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 
477 int 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 Random & getSystemRandom() noexcept
Definition: juce_Random.cpp:67
static Range< Index > doBufferedRead(Range< Index > rangeToRead, GetBufferedRange &&getBufferedRange, ReadFromReservoir &&readFromReservoir, FillReservoir &&fillReservoir)