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 
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 "-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 //==============================================================================
101 static const char* const oggFormatName = "Ogg-Vorbis file";
102 
103 const char* const OggVorbisAudioFormat::encoderName = "encoder";
104 const char* const OggVorbisAudioFormat::id3title = "id3title";
105 const char* const OggVorbisAudioFormat::id3artist = "id3artist";
106 const char* const OggVorbisAudioFormat::id3album = "id3album";
107 const char* const OggVorbisAudioFormat::id3comment = "id3comment";
108 const char* const OggVorbisAudioFormat::id3date = "id3date";
109 const char* const OggVorbisAudioFormat::id3genre = "id3genre";
110 const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber";
111 
112 
113 //==============================================================================
114 class OggReader : public AudioFormatReader
115 {
116 public:
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);
134  addMetadataItem (comment, "ENCODER", OggVorbisAudioFormat::encoderName);
135  addMetadataItem (comment, "TITLE", OggVorbisAudioFormat::id3title);
136  addMetadataItem (comment, "ARTIST", OggVorbisAudioFormat::id3artist);
137  addMetadataItem (comment, "ALBUM", OggVorbisAudioFormat::id3album);
138  addMetadataItem (comment, "COMMENT", OggVorbisAudioFormat::id3comment);
139  addMetadataItem (comment, "DATE", OggVorbisAudioFormat::id3date);
140  addMetadataItem (comment, "GENRE", OggVorbisAudioFormat::id3genre);
141  addMetadataItem (comment, "TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber);
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  {
169  auto numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile);
170 
171  if (startSampleInFile >= reservoirStart && numAvailable > 0)
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)
179  memcpy (destSamples[i] + startOffsetInDestBuffer,
180  reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)),
181  sizeof (float) * (size_t) numToUse);
182 
183  startSampleInFile += numToUse;
184  numSamples -= numToUse;
185  startOffsetInDestBuffer += numToUse;
186 
187  if (numSamples == 0)
188  break;
189  }
190 
191  if (startSampleInFile < reservoirStart
192  || startSampleInFile + numSamples > reservoirStart + samplesInReservoir)
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))
199  ov_pcm_seek (&ovFile, reservoirStart);
200 
201  int bitStream = 0;
202  int offset = 0;
203  int numToRead = samplesInReservoir;
204 
205  while (numToRead > 0)
206  {
207  float** dataIn = nullptr;
208  auto samps = ov_read_float (&ovFile, &dataIn, numToRead, &bitStream);
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 
266 private:
267  OggVorbisNamespace::OggVorbis_File ovFile;
268  OggVorbisNamespace::ov_callbacks callbacks;
269  AudioBuffer<float> reservoir;
270  int reservoirStart = 0, samplesInReservoir = 0;
271 
272  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
273 };
274 
275 //==============================================================================
276 class OggWriter : public AudioFormatWriter
277 {
278 public:
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  {
284  vorbis_info_init (&vi);
285 
286  if (vorbis_encode_init_vbr (&vi, (int) numChans, (int) rate,
287  jlimit (0.0f, 1.0f, qualityIndex * 0.1f)) == 0)
288  {
289  vorbis_comment_init (&vc);
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 
300  vorbis_analysis_init (&vd, &vi);
301  vorbis_block_init (&vd, &vb);
302 
303  ogg_stream_init (&os, Random::getSystemRandom().nextInt());
304 
305  OggVorbisNamespace::ogg_packet header, header_comm, header_code;
306  vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code);
307 
308  ogg_stream_packetin (&os, &header);
309  ogg_stream_packetin (&os, &header_comm);
310  ogg_stream_packetin (&os, &header_code);
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 
332  ogg_stream_clear (&os);
333  vorbis_block_clear (&vb);
334  vorbis_dsp_clear (&vd);
335  vorbis_comment_clear (&vc);
336 
337  vorbis_info_clear (&vi);
338  output->flush();
339  }
340  else
341  {
342  vorbis_info_clear (&vi);
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);
384  vorbis_bitrate_addblock (&vb);
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 
407 private:
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 //==============================================================================
429 OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg")
430 {
431 }
432 
433 OggVorbisAudioFormat::~OggVorbisAudioFormat()
434 {
435 }
436 
438 {
439  return { 8000, 11025, 12000, 16000, 22050, 32000,
440  44100, 48000, 88200, 96000, 176400, 192000 };
441 }
442 
444 {
445  return { 32 };
446 }
447 
448 bool OggVorbisAudioFormat::canDoStereo() { return true; }
449 bool OggVorbisAudioFormat::canDoMono() { return true; }
450 bool OggVorbisAudioFormat::isCompressed() { return true; }
451 
452 AudioFormatReader* 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 
465 AudioFormatWriter* 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 
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 
488 int 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
juce::OggVorbisAudioFormat::id3album
static const char *const id3album
Metadata key for setting an ID3 album.
Definition: juce_OggVorbisAudioFormat.h:82
juce::OggVorbisAudioFormat::getQualityOptions
StringArray getQualityOptions() override
Returns a list of different qualities that can be used when writing.
juce::OggVorbisAudioFormat::isCompressed
bool isCompressed() override
Returns true if the format uses compressed data.
juce::OggVorbisAudioFormat::encoderName
static const char *const encoderName
Metadata property name used by the Ogg writer - if you set a string for this value,...
Definition: juce_OggVorbisAudioFormat.h:78
juce::OggVorbisAudioFormat::id3genre
static const char *const id3genre
Metadata key for setting an ID3 genre.
Definition: juce_OggVorbisAudioFormat.h:85
juce::OggVorbisAudioFormat::canDoMono
bool canDoMono() override
Returns true if the format can do 1-channel audio.
juce::OggVorbisAudioFormat::getPossibleBitDepths
Array< int > getPossibleBitDepths() override
Returns a set of bit depths that the format can read and write.
juce::OggVorbisAudioFormat::createWriterFor
AudioFormatWriter * createWriterFor(OutputStream *streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray &metadataValues, int qualityOptionIndex) override
Tries to create an object that can write to a stream with this audio format.
juce::OggVorbisAudioFormat::createReaderFor
AudioFormatReader * createReaderFor(InputStream *sourceStream, bool deleteStreamIfOpeningFails) override
Tries to create an object that can read from a stream containing audio data in this format.
juce::OggVorbisAudioFormat::getPossibleSampleRates
Array< int > getPossibleSampleRates() override
Returns a set of sample rates that the format can read and write.
juce::OggVorbisAudioFormat::id3title
static const char *const id3title
Metadata key for setting an ID3 title.
Definition: juce_OggVorbisAudioFormat.h:80
juce::OggVorbisAudioFormat::id3date
static const char *const id3date
Metadata key for setting an ID3 date.
Definition: juce_OggVorbisAudioFormat.h:84
juce::OggVorbisAudioFormat::id3comment
static const char *const id3comment
Metadata key for setting an ID3 comment.
Definition: juce_OggVorbisAudioFormat.h:83
juce::OggVorbisAudioFormat::id3artist
static const char *const id3artist
Metadata key for setting an ID3 artist name.
Definition: juce_OggVorbisAudioFormat.h:81
juce::Random::getSystemRandom
static Random & getSystemRandom() noexcept
The overhead of creating a new Random object is fairly small, but if you want to avoid it,...
Definition: juce_Random.cpp:71
juce::OggVorbisAudioFormat::canDoStereo
bool canDoStereo() override
Returns true if the format can do 2-channel audio.
juce::OggVorbisAudioFormat::id3trackNumber
static const char *const id3trackNumber
Metadata key for setting an ID3 track number.
Definition: juce_OggVorbisAudioFormat.h:86
juce::OggVorbisAudioFormat::estimateOggFileQuality
int estimateOggFileQuality(const File &source)
Tries to estimate the quality level of an ogg file based on its size.