The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

624 lines
22KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. #if JUCE_USE_FLAC
  22. }
  23. #if defined _WIN32 && !defined __CYGWIN__
  24. #include <io.h>
  25. #else
  26. #include <unistd.h>
  27. #endif
  28. #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
  29. #include <sys/types.h> /* for off_t */
  30. #endif
  31. #if HAVE_INTTYPES_H
  32. #define __STDC_FORMAT_MACROS
  33. #include <inttypes.h>
  34. #endif
  35. #if defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ || defined __EMX__
  36. #include <io.h> /* for _setmode(), chmod() */
  37. #include <fcntl.h> /* for _O_BINARY */
  38. #else
  39. #include <unistd.h> /* for chown(), unlink() */
  40. #endif
  41. #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
  42. #if defined __BORLANDC__
  43. #include <utime.h> /* for utime() */
  44. #else
  45. #include <sys/utime.h> /* for utime() */
  46. #endif
  47. #else
  48. #include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
  49. #include <utime.h> /* for utime() */
  50. #endif
  51. #if defined _MSC_VER
  52. #if _MSC_VER >= 1600
  53. #include <stdint.h>
  54. #else
  55. #include <limits.h>
  56. #endif
  57. #endif
  58. #ifdef _WIN32
  59. #include <stdio.h>
  60. #include <sys/stat.h>
  61. #include <stdarg.h>
  62. #include <windows.h>
  63. #endif
  64. #ifdef DEBUG
  65. #include <assert.h>
  66. #endif
  67. #include <stdlib.h>
  68. #include <stdio.h>
  69. namespace juce
  70. {
  71. namespace FlacNamespace
  72. {
  73. #if JUCE_INCLUDE_FLAC_CODE || ! defined (JUCE_INCLUDE_FLAC_CODE)
  74. #undef VERSION
  75. #define VERSION "1.3.1"
  76. #define FLAC__NO_DLL 1
  77. #if JUCE_MSVC
  78. #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312 4505 4365 4005 4334 181 111)
  79. #else
  80. #define HAVE_LROUND 1
  81. #endif
  82. #if JUCE_MAC
  83. #define FLAC__SYS_DARWIN 1
  84. #endif
  85. #ifndef SIZE_MAX
  86. #define SIZE_MAX 0xffffffff
  87. #endif
  88. #if JUCE_CLANG
  89. #pragma clang diagnostic push
  90. #pragma clang diagnostic ignored "-Wconversion"
  91. #pragma clang diagnostic ignored "-Wshadow"
  92. #pragma clang diagnostic ignored "-Wdeprecated-register"
  93. #pragma clang diagnostic ignored "-Wswitch-enum"
  94. #if __has_warning ("-Wimplicit-fallthrough")
  95. #pragma clang diagnostic ignored "-Wimplicit-fallthrough"
  96. #endif
  97. #if __has_warning("-Wzero-as-null-pointer-constant")
  98. #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
  99. #endif
  100. #endif
  101. #if JUCE_GCC
  102. #pragma GCC diagnostic push
  103. #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
  104. #pragma GCC diagnostic ignored "-Wconversion"
  105. #pragma GCC diagnostic ignored "-Wsign-conversion"
  106. #pragma GCC diagnostic ignored "-Wswitch-enum"
  107. #pragma GCC diagnostic ignored "-Wswitch-default"
  108. #pragma GCC diagnostic ignored "-Wredundant-decls"
  109. #if __GNUC__ >= 7
  110. #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
  111. #endif
  112. #endif
  113. #if JUCE_INTEL
  114. #if JUCE_32BIT
  115. #define FLAC__CPU_IA32 1
  116. #endif
  117. #if JUCE_64BIT
  118. #define FLAC__CPU_X86_64 1
  119. #endif
  120. #define FLAC__HAS_X86INTRIN 1
  121. #endif
  122. #undef __STDC_LIMIT_MACROS
  123. #define __STDC_LIMIT_MACROS 1
  124. #define flac_max jmax
  125. #define flac_min jmin
  126. #undef DEBUG // (some flac code dumps debug trace if the app defines this macro)
  127. #include "flac/all.h"
  128. #include "flac/libFLAC/bitmath.c"
  129. #include "flac/libFLAC/bitreader.c"
  130. #include "flac/libFLAC/bitwriter.c"
  131. #include "flac/libFLAC/cpu.c"
  132. #include "flac/libFLAC/crc.c"
  133. #include "flac/libFLAC/fixed.c"
  134. #include "flac/libFLAC/float.c"
  135. #include "flac/libFLAC/format.c"
  136. #include "flac/libFLAC/lpc_flac.c"
  137. #include "flac/libFLAC/md5.c"
  138. #include "flac/libFLAC/memory.c"
  139. #include "flac/libFLAC/stream_decoder.c"
  140. #include "flac/libFLAC/stream_encoder.c"
  141. #include "flac/libFLAC/stream_encoder_framing.c"
  142. #include "flac/libFLAC/window_flac.c"
  143. #undef VERSION
  144. #else
  145. #include <FLAC/all.h>
  146. #endif
  147. #if JUCE_CLANG
  148. #pragma clang diagnostic pop
  149. #endif
  150. #if JUCE_GCC
  151. #pragma GCC diagnostic pop
  152. #endif
  153. }
  154. #undef max
  155. #undef min
  156. //==============================================================================
  157. static const char* const flacFormatName = "FLAC file";
  158. //==============================================================================
  159. class FlacReader : public AudioFormatReader
  160. {
  161. public:
  162. FlacReader (InputStream* in) : AudioFormatReader (in, flacFormatName)
  163. {
  164. lengthInSamples = 0;
  165. decoder = FlacNamespace::FLAC__stream_decoder_new();
  166. ok = FLAC__stream_decoder_init_stream (decoder,
  167. readCallback_, seekCallback_, tellCallback_, lengthCallback_,
  168. eofCallback_, writeCallback_, metadataCallback_, errorCallback_,
  169. this) == FlacNamespace::FLAC__STREAM_DECODER_INIT_STATUS_OK;
  170. if (ok)
  171. {
  172. FLAC__stream_decoder_process_until_end_of_metadata (decoder);
  173. if (lengthInSamples == 0 && sampleRate > 0)
  174. {
  175. // the length hasn't been stored in the metadata, so we'll need to
  176. // work it out the length the hard way, by scanning the whole file..
  177. scanningForLength = true;
  178. FLAC__stream_decoder_process_until_end_of_stream (decoder);
  179. scanningForLength = false;
  180. auto tempLength = lengthInSamples;
  181. FLAC__stream_decoder_reset (decoder);
  182. FLAC__stream_decoder_process_until_end_of_metadata (decoder);
  183. lengthInSamples = tempLength;
  184. }
  185. }
  186. }
  187. ~FlacReader() override
  188. {
  189. FlacNamespace::FLAC__stream_decoder_delete (decoder);
  190. }
  191. void useMetadata (const FlacNamespace::FLAC__StreamMetadata_StreamInfo& info)
  192. {
  193. sampleRate = info.sample_rate;
  194. bitsPerSample = info.bits_per_sample;
  195. lengthInSamples = (unsigned int) info.total_samples;
  196. numChannels = info.channels;
  197. reservoir.setSize ((int) numChannels, 2 * (int) info.max_blocksize, false, false, true);
  198. }
  199. // returns the number of samples read
  200. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  201. int64 startSampleInFile, int numSamples) override
  202. {
  203. if (! ok)
  204. return false;
  205. while (numSamples > 0)
  206. {
  207. if (startSampleInFile >= reservoirStart
  208. && startSampleInFile < reservoirStart + samplesInReservoir)
  209. {
  210. auto num = (int) jmin ((int64) numSamples,
  211. reservoirStart + samplesInReservoir - startSampleInFile);
  212. jassert (num > 0);
  213. for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
  214. if (destSamples[i] != nullptr)
  215. memcpy (destSamples[i] + startOffsetInDestBuffer,
  216. reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)),
  217. (size_t) num * sizeof (int));
  218. startOffsetInDestBuffer += num;
  219. startSampleInFile += num;
  220. numSamples -= num;
  221. }
  222. else
  223. {
  224. if (startSampleInFile >= lengthInSamples)
  225. {
  226. samplesInReservoir = 0;
  227. }
  228. else if (startSampleInFile < reservoirStart
  229. || startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511))
  230. {
  231. // had some problems with flac crashing if the read pos is aligned more
  232. // accurately than this. Probably fixed in newer versions of the library, though.
  233. reservoirStart = (int) (startSampleInFile & ~511);
  234. samplesInReservoir = 0;
  235. FLAC__stream_decoder_seek_absolute (decoder, (FlacNamespace::FLAC__uint64) reservoirStart);
  236. }
  237. else
  238. {
  239. reservoirStart += samplesInReservoir;
  240. samplesInReservoir = 0;
  241. FLAC__stream_decoder_process_single (decoder);
  242. }
  243. if (samplesInReservoir == 0)
  244. break;
  245. }
  246. }
  247. if (numSamples > 0)
  248. {
  249. for (int i = numDestChannels; --i >= 0;)
  250. if (destSamples[i] != nullptr)
  251. zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int));
  252. }
  253. return true;
  254. }
  255. void useSamples (const FlacNamespace::FLAC__int32* const buffer[], int numSamples)
  256. {
  257. if (scanningForLength)
  258. {
  259. lengthInSamples += numSamples;
  260. }
  261. else
  262. {
  263. if (numSamples > reservoir.getNumSamples())
  264. reservoir.setSize ((int) numChannels, numSamples, false, false, true);
  265. auto bitsToShift = 32 - bitsPerSample;
  266. for (int i = 0; i < (int) numChannels; ++i)
  267. {
  268. auto* src = buffer[i];
  269. int n = i;
  270. while (src == nullptr && n > 0)
  271. src = buffer [--n];
  272. if (src != nullptr)
  273. {
  274. auto* dest = reinterpret_cast<int*> (reservoir.getWritePointer(i));
  275. for (int j = 0; j < numSamples; ++j)
  276. dest[j] = src[j] << bitsToShift;
  277. }
  278. }
  279. samplesInReservoir = numSamples;
  280. }
  281. }
  282. //==============================================================================
  283. static FlacNamespace::FLAC__StreamDecoderReadStatus readCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__byte buffer[], size_t* bytes, void* client_data)
  284. {
  285. *bytes = (size_t) static_cast<const FlacReader*> (client_data)->input->read (buffer, (int) *bytes);
  286. return FlacNamespace::FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
  287. }
  288. static FlacNamespace::FLAC__StreamDecoderSeekStatus seekCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64 absolute_byte_offset, void* client_data)
  289. {
  290. static_cast<const FlacReader*> (client_data)->input->setPosition ((int) absolute_byte_offset);
  291. return FlacNamespace::FLAC__STREAM_DECODER_SEEK_STATUS_OK;
  292. }
  293. static FlacNamespace::FLAC__StreamDecoderTellStatus tellCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data)
  294. {
  295. *absolute_byte_offset = (uint64) static_cast<const FlacReader*> (client_data)->input->getPosition();
  296. return FlacNamespace::FLAC__STREAM_DECODER_TELL_STATUS_OK;
  297. }
  298. static FlacNamespace::FLAC__StreamDecoderLengthStatus lengthCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* stream_length, void* client_data)
  299. {
  300. *stream_length = (uint64) static_cast<const FlacReader*> (client_data)->input->getTotalLength();
  301. return FlacNamespace::FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
  302. }
  303. static FlacNamespace::FLAC__bool eofCallback_ (const FlacNamespace::FLAC__StreamDecoder*, void* client_data)
  304. {
  305. return static_cast<const FlacReader*> (client_data)->input->isExhausted();
  306. }
  307. static FlacNamespace::FLAC__StreamDecoderWriteStatus writeCallback_ (const FlacNamespace::FLAC__StreamDecoder*,
  308. const FlacNamespace::FLAC__Frame* frame,
  309. const FlacNamespace::FLAC__int32* const buffer[],
  310. void* client_data)
  311. {
  312. static_cast<FlacReader*> (client_data)->useSamples (buffer, (int) frame->header.blocksize);
  313. return FlacNamespace::FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
  314. }
  315. static void metadataCallback_ (const FlacNamespace::FLAC__StreamDecoder*,
  316. const FlacNamespace::FLAC__StreamMetadata* metadata,
  317. void* client_data)
  318. {
  319. static_cast<FlacReader*> (client_data)->useMetadata (metadata->data.stream_info);
  320. }
  321. static void errorCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__StreamDecoderErrorStatus, void*)
  322. {
  323. }
  324. private:
  325. FlacNamespace::FLAC__StreamDecoder* decoder;
  326. AudioBuffer<float> reservoir;
  327. int reservoirStart = 0, samplesInReservoir = 0;
  328. bool ok = false, scanningForLength = false;
  329. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacReader)
  330. };
  331. //==============================================================================
  332. class FlacWriter : public AudioFormatWriter
  333. {
  334. public:
  335. FlacWriter (OutputStream* out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex)
  336. : AudioFormatWriter (out, flacFormatName, rate, numChans, bits),
  337. streamStartPos (output != nullptr ? jmax (output->getPosition(), 0ll) : 0ll)
  338. {
  339. encoder = FlacNamespace::FLAC__stream_encoder_new();
  340. if (qualityOptionIndex > 0)
  341. FLAC__stream_encoder_set_compression_level (encoder, (uint32) jmin (8, qualityOptionIndex));
  342. FLAC__stream_encoder_set_do_mid_side_stereo (encoder, numChannels == 2);
  343. FLAC__stream_encoder_set_loose_mid_side_stereo (encoder, numChannels == 2);
  344. FLAC__stream_encoder_set_channels (encoder, numChannels);
  345. FLAC__stream_encoder_set_bits_per_sample (encoder, jmin ((unsigned int) 24, bitsPerSample));
  346. FLAC__stream_encoder_set_sample_rate (encoder, (unsigned int) sampleRate);
  347. FLAC__stream_encoder_set_blocksize (encoder, 0);
  348. FLAC__stream_encoder_set_do_escape_coding (encoder, true);
  349. ok = FLAC__stream_encoder_init_stream (encoder,
  350. encodeWriteCallback, encodeSeekCallback,
  351. encodeTellCallback, encodeMetadataCallback,
  352. this) == FlacNamespace::FLAC__STREAM_ENCODER_INIT_STATUS_OK;
  353. }
  354. ~FlacWriter() override
  355. {
  356. if (ok)
  357. {
  358. FlacNamespace::FLAC__stream_encoder_finish (encoder);
  359. output->flush();
  360. }
  361. else
  362. {
  363. output = nullptr; // to stop the base class deleting this, as it needs to be returned
  364. // to the caller of createWriter()
  365. }
  366. FlacNamespace::FLAC__stream_encoder_delete (encoder);
  367. }
  368. //==============================================================================
  369. bool write (const int** samplesToWrite, int numSamples) override
  370. {
  371. if (! ok)
  372. return false;
  373. HeapBlock<int*> channels;
  374. HeapBlock<int> temp;
  375. auto bitsToShift = 32 - (int) bitsPerSample;
  376. if (bitsToShift > 0)
  377. {
  378. temp.malloc (numChannels * (size_t) numSamples);
  379. channels.calloc (numChannels + 1);
  380. for (unsigned int i = 0; i < numChannels; ++i)
  381. {
  382. if (samplesToWrite[i] == nullptr)
  383. break;
  384. auto* destData = temp.get() + i * (size_t) numSamples;
  385. channels[i] = destData;
  386. for (int j = 0; j < numSamples; ++j)
  387. destData[j] = (samplesToWrite[i][j] >> bitsToShift);
  388. }
  389. samplesToWrite = const_cast<const int**> (channels.get());
  390. }
  391. return FLAC__stream_encoder_process (encoder, (const FlacNamespace::FLAC__int32**) samplesToWrite, (unsigned) numSamples) != 0;
  392. }
  393. bool writeData (const void* const data, const int size) const
  394. {
  395. return output->write (data, (size_t) size);
  396. }
  397. static void packUint32 (FlacNamespace::FLAC__uint32 val, FlacNamespace::FLAC__byte* b, const int bytes)
  398. {
  399. b += bytes;
  400. for (int i = 0; i < bytes; ++i)
  401. {
  402. *(--b) = (FlacNamespace::FLAC__byte) (val & 0xff);
  403. val >>= 8;
  404. }
  405. }
  406. void writeMetaData (const FlacNamespace::FLAC__StreamMetadata* metadata)
  407. {
  408. using namespace FlacNamespace;
  409. auto& info = metadata->data.stream_info;
  410. unsigned char buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH];
  411. const unsigned int channelsMinus1 = info.channels - 1;
  412. const unsigned int bitsMinus1 = info.bits_per_sample - 1;
  413. packUint32 (info.min_blocksize, buffer, 2);
  414. packUint32 (info.max_blocksize, buffer + 2, 2);
  415. packUint32 (info.min_framesize, buffer + 4, 3);
  416. packUint32 (info.max_framesize, buffer + 7, 3);
  417. buffer[10] = (uint8) ((info.sample_rate >> 12) & 0xff);
  418. buffer[11] = (uint8) ((info.sample_rate >> 4) & 0xff);
  419. buffer[12] = (uint8) (((info.sample_rate & 0x0f) << 4) | (channelsMinus1 << 1) | (bitsMinus1 >> 4));
  420. buffer[13] = (FLAC__byte) (((bitsMinus1 & 0x0f) << 4) | (unsigned int) ((info.total_samples >> 32) & 0x0f));
  421. packUint32 ((FLAC__uint32) info.total_samples, buffer + 14, 4);
  422. memcpy (buffer + 18, info.md5sum, 16);
  423. const bool seekOk = output->setPosition (streamStartPos + 4);
  424. ignoreUnused (seekOk);
  425. // if this fails, you've given it an output stream that can't seek! It needs
  426. // to be able to seek back to write the header
  427. jassert (seekOk);
  428. output->writeIntBigEndian (FLAC__STREAM_METADATA_STREAMINFO_LENGTH);
  429. output->write (buffer, FLAC__STREAM_METADATA_STREAMINFO_LENGTH);
  430. }
  431. //==============================================================================
  432. static FlacNamespace::FLAC__StreamEncoderWriteStatus encodeWriteCallback (const FlacNamespace::FLAC__StreamEncoder*,
  433. const FlacNamespace::FLAC__byte buffer[],
  434. size_t bytes,
  435. unsigned int /*samples*/,
  436. unsigned int /*current_frame*/,
  437. void* client_data)
  438. {
  439. return static_cast<FlacWriter*> (client_data)->writeData (buffer, (int) bytes)
  440. ? FlacNamespace::FLAC__STREAM_ENCODER_WRITE_STATUS_OK
  441. : FlacNamespace::FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
  442. }
  443. static FlacNamespace::FLAC__StreamEncoderSeekStatus encodeSeekCallback (const FlacNamespace::FLAC__StreamEncoder*, FlacNamespace::FLAC__uint64, void*)
  444. {
  445. return FlacNamespace::FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED;
  446. }
  447. static FlacNamespace::FLAC__StreamEncoderTellStatus encodeTellCallback (const FlacNamespace::FLAC__StreamEncoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data)
  448. {
  449. if (client_data == nullptr)
  450. return FlacNamespace::FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED;
  451. *absolute_byte_offset = (FlacNamespace::FLAC__uint64) static_cast<FlacWriter*> (client_data)->output->getPosition();
  452. return FlacNamespace::FLAC__STREAM_ENCODER_TELL_STATUS_OK;
  453. }
  454. static void encodeMetadataCallback (const FlacNamespace::FLAC__StreamEncoder*, const FlacNamespace::FLAC__StreamMetadata* metadata, void* client_data)
  455. {
  456. static_cast<FlacWriter*> (client_data)->writeMetaData (metadata);
  457. }
  458. bool ok = false;
  459. private:
  460. FlacNamespace::FLAC__StreamEncoder* encoder;
  461. int64 streamStartPos;
  462. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacWriter)
  463. };
  464. //==============================================================================
  465. FlacAudioFormat::FlacAudioFormat() : AudioFormat (flacFormatName, ".flac") {}
  466. FlacAudioFormat::~FlacAudioFormat() {}
  467. Array<int> FlacAudioFormat::getPossibleSampleRates()
  468. {
  469. return { 8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000,
  470. 88200, 96000, 176400, 192000, 352800, 384000 };
  471. }
  472. Array<int> FlacAudioFormat::getPossibleBitDepths()
  473. {
  474. return { 16, 24 };
  475. }
  476. bool FlacAudioFormat::canDoStereo() { return true; }
  477. bool FlacAudioFormat::canDoMono() { return true; }
  478. bool FlacAudioFormat::isCompressed() { return true; }
  479. AudioFormatReader* FlacAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails)
  480. {
  481. std::unique_ptr<FlacReader> r (new FlacReader (in));
  482. if (r->sampleRate > 0)
  483. return r.release();
  484. if (! deleteStreamIfOpeningFails)
  485. r->input = nullptr;
  486. return nullptr;
  487. }
  488. AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out,
  489. double sampleRate,
  490. unsigned int numberOfChannels,
  491. int bitsPerSample,
  492. const StringPairArray& /*metadataValues*/,
  493. int qualityOptionIndex)
  494. {
  495. if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
  496. {
  497. std::unique_ptr<FlacWriter> w (new FlacWriter (out, sampleRate, numberOfChannels,
  498. (uint32) bitsPerSample, qualityOptionIndex));
  499. if (w->ok)
  500. return w.release();
  501. }
  502. return nullptr;
  503. }
  504. StringArray FlacAudioFormat::getQualityOptions()
  505. {
  506. return { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)" };
  507. }
  508. #endif
  509. } // namespace juce