OpenShot Audio Library | OpenShotAudio  0.3.2
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 
27 namespace juce
28 {
29 
30 #if JUCE_USE_OGGVORBIS
31 
32 #if JUCE_MAC && ! defined (__MACOSX__)
33  #define __MACOSX__ 1
34 #endif
35 
36 namespace 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 //==============================================================================
104 static const char* const oggFormatName = "Ogg-Vorbis file";
105 
106 const char* const OggVorbisAudioFormat::encoderName = "encoder";
107 const char* const OggVorbisAudioFormat::id3title = "id3title";
108 const char* const OggVorbisAudioFormat::id3artist = "id3artist";
109 const char* const OggVorbisAudioFormat::id3album = "id3album";
110 const char* const OggVorbisAudioFormat::id3comment = "id3comment";
111 const char* const OggVorbisAudioFormat::id3date = "id3date";
112 const char* const OggVorbisAudioFormat::id3genre = "id3genre";
113 const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber";
114 
115 
116 //==============================================================================
117 class OggReader : public AudioFormatReader
118 {
119 public:
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 
269 private:
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 //==============================================================================
279 class OggWriter : public AudioFormatWriter
280 {
281 public:
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 
410 private:
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 //==============================================================================
432 OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg")
433 {
434 }
435 
436 OggVorbisAudioFormat::~OggVorbisAudioFormat()
437 {
438 }
439 
440 Array<int> OggVorbisAudioFormat::getPossibleSampleRates()
441 {
442  return { 8000, 11025, 12000, 16000, 22050, 32000,
443  44100, 48000, 88200, 96000, 176400, 192000 };
444 }
445 
446 Array<int> OggVorbisAudioFormat::getPossibleBitDepths()
447 {
448  return { 32 };
449 }
450 
451 bool OggVorbisAudioFormat::canDoStereo() { return true; }
452 bool OggVorbisAudioFormat::canDoMono() { return true; }
453 bool OggVorbisAudioFormat::isCompressed() { return true; }
454 
455 AudioFormatReader* 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 
468 AudioFormatWriter* 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 
485 StringArray 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 
491 int 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
static Random & getSystemRandom() noexcept
Definition: juce_Random.cpp:71