JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_FlacAudioFormat.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_FLAC
30
31}
32
33#if defined _WIN32 && !defined __CYGWIN__
34 #include <io.h>
35#else
36 #include <unistd.h>
37#endif
38
39#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
40 #include <sys/types.h> /* for off_t */
41#endif
42
43#if HAVE_INTTYPES_H
44 #define __STDC_FORMAT_MACROS
45 #include <inttypes.h>
46#endif
47
48#if defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ || defined __EMX__
49 #include <io.h> /* for _setmode(), chmod() */
50 #include <fcntl.h> /* for _O_BINARY */
51#else
52 #include <unistd.h> /* for chown(), unlink() */
53#endif
54
55#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
56 #if defined __BORLANDC__
57 #include <utime.h> /* for utime() */
58 #else
59 #include <sys/utime.h> /* for utime() */
60 #endif
61#else
62 #include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
63 #include <utime.h> /* for utime() */
64#endif
65
66#if defined _MSC_VER
67 #if _MSC_VER >= 1600
68 #include <stdint.h>
69 #else
70 #include <limits.h>
71 #endif
72#endif
73
74#ifdef _WIN32
75 #include <stdio.h>
76 #include <sys/stat.h>
77 #include <stdarg.h>
78 #include <windows.h>
79#endif
80
81#ifdef DEBUG
82 #include <assert.h>
83#endif
84
85#include <stdlib.h>
86#include <stdio.h>
87
88namespace juce
89{
90
91namespace FlacNamespace
92{
93#if JUCE_INCLUDE_FLAC_CODE || ! defined (JUCE_INCLUDE_FLAC_CODE)
94
95 #undef PACKAGE_VERSION
96 #define PACKAGE_VERSION "1.4.3"
97
98 #define FLAC__NO_DLL 1
99
100 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312 4505 4365 4005 4334 181 111 6340 6308 6297 6001 6320)
101 #if ! JUCE_MSVC
102 #define HAVE_LROUND 1
103 #endif
104
105 #if JUCE_MAC
106 #define FLAC__SYS_DARWIN 1
107 #endif
108
109 #ifndef SIZE_MAX
110 #define SIZE_MAX 0xffffffff
111 #endif
112
113 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wconversion",
114 "-Wdeprecated-register",
115 "-Wfloat-equal",
116 "-Wimplicit-fallthrough",
117 "-Wlanguage-extension-token",
118 "-Wredundant-decls",
119 "-Wshadow",
120 "-Wsign-conversion",
121 "-Wswitch-default",
122 "-Wswitch-enum",
123 "-Wzero-as-null-pointer-constant")
124
125 #if JUCE_INTEL
126 #if JUCE_32BIT
127 #define FLAC__CPU_IA32 1
128 #endif
129 #if JUCE_64BIT
130 #define FLAC__CPU_X86_64 1
131 #endif
132 #define FLAC__HAS_X86INTRIN 1
133 #endif
134
135 #if JUCE_ARM && JUCE_64BIT
136 #define FLAC__CPU_ARM64 1
137
138 #if JUCE_USE_ARM_NEON
139 #define FLAC__HAS_NEONINTRIN 1
140 #define FLAC__HAS_A64NEONINTRIN 1
141 #endif
142 #endif
143
144 #define flac_max jmax
145 #define flac_min jmin
146
147 #pragma push_macro ("DEBUG")
148 #pragma push_macro ("NDEBUG")
149 #undef DEBUG // (some flac code dumps debug trace if the app defines this macro)
150
151 #ifndef NDEBUG
152 #define NDEBUG // (some flac code prints cpu info if this isn't defined)
153 #endif
154
155#include <FLAC/all.h>
156#if 0
157 #include "flac/all.h"
158 #include "flac/libFLAC/bitmath.c"
159 #include "flac/libFLAC/bitreader.c"
160 #include "flac/libFLAC/bitwriter.c"
161 #include "flac/libFLAC/cpu.c"
162 #include "flac/libFLAC/crc.c"
163 #include "flac/libFLAC/fixed.c"
164 #include "flac/libFLAC/float.c"
165 #include "flac/libFLAC/format.c"
166 #include "flac/libFLAC/lpc_flac.c"
167 #include "flac/libFLAC/lpc_intrin_neon.c"
168 #include "flac/libFLAC/md5.c"
169 #include "flac/libFLAC/memory.c"
170 #include "flac/libFLAC/stream_decoder.c"
171 #include "flac/libFLAC/stream_encoder.c"
172 #include "flac/libFLAC/stream_encoder_framing.c"
173 #include "flac/libFLAC/window_flac.c"
174#endif
175
176 #pragma pop_macro ("DEBUG")
177 #pragma pop_macro ("NDEBUG")
178
179 #undef PACKAGE_VERSION
180
181 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
182 JUCE_END_IGNORE_WARNINGS_MSVC
183
184#else
185 #include <FLAC/all.h>
186#endif
187}
188
189#undef max
190#undef min
191
192//==============================================================================
193static const char* const flacFormatName = "FLAC file";
194
195template <typename Item>
196auto emptyRange (Item item) { return Range<Item>::emptyRange (item); }
197
198//==============================================================================
199class FlacReader final : public AudioFormatReader
200{
201public:
202 FlacReader (InputStream* in) : AudioFormatReader (in, flacFormatName)
203 {
204 lengthInSamples = 0;
205 decoder = FlacNamespace::FLAC__stream_decoder_new();
206
207 ok = FLAC__stream_decoder_init_stream (decoder,
208 readCallback_, seekCallback_, tellCallback_, lengthCallback_,
209 eofCallback_, writeCallback_, metadataCallback_, errorCallback_,
210 this) == FlacNamespace::FLAC__STREAM_DECODER_INIT_STATUS_OK;
211
212 if (ok)
213 {
214 FLAC__stream_decoder_process_until_end_of_metadata (decoder);
215
216 if (lengthInSamples == 0 && sampleRate > 0)
217 {
218 // the length hasn't been stored in the metadata, so we'll need to
219 // work it out the length the hard way, by scanning the whole file..
220 scanningForLength = true;
221 FLAC__stream_decoder_process_until_end_of_stream (decoder);
222 scanningForLength = false;
223 auto tempLength = lengthInSamples;
224
225 FLAC__stream_decoder_reset (decoder);
226 FLAC__stream_decoder_process_until_end_of_metadata (decoder);
227 lengthInSamples = tempLength;
228 }
229 }
230 }
231
232 ~FlacReader() override
233 {
234 FlacNamespace::FLAC__stream_decoder_delete (decoder);
235 }
236
237 void useMetadata (const FlacNamespace::FLAC__StreamMetadata_StreamInfo& info)
238 {
239 sampleRate = info.sample_rate;
240 bitsPerSample = info.bits_per_sample;
241 lengthInSamples = (unsigned int) info.total_samples;
242 numChannels = info.channels;
243
244 reservoir.setSize ((int) numChannels, 2 * (int) info.max_blocksize, false, false, true);
245 }
246
247 bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer,
248 int64 startSampleInFile, int numSamples) override
249 {
250 if (! ok)
251 return false;
252
253 const auto getBufferedRange = [this] { return bufferedRange; };
254
255 const auto readFromReservoir = [this, &destSamples, &numDestChannels, &startOffsetInDestBuffer, &startSampleInFile] (const Range<int64> rangeToRead)
256 {
257 const auto bufferIndices = rangeToRead - bufferedRange.getStart();
258 const auto writePos = (int64) startOffsetInDestBuffer + (rangeToRead.getStart() - startSampleInFile);
259
260 for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
261 {
262 if (destSamples[i] != nullptr)
263 {
264 memcpy (destSamples[i] + writePos,
265 reservoir.getReadPointer (i) + bufferIndices.getStart(),
266 (size_t) bufferIndices.getLength() * sizeof (int));
267 }
268 }
269 };
270
271 const auto fillReservoir = [this] (const int64 requestedStart)
272 {
273 if (requestedStart >= lengthInSamples)
274 {
275 bufferedRange = emptyRange (requestedStart);
276 return;
277 }
278
279 if (requestedStart < bufferedRange.getStart()
280 || jmax (bufferedRange.getEnd(), bufferedRange.getStart() + (int64) 511) < requestedStart)
281 {
282 // had some problems with flac crashing if the read pos is aligned more
283 // accurately than this. Probably fixed in newer versions of the library, though.
284 bufferedRange = emptyRange (requestedStart & ~511);
285 FLAC__stream_decoder_seek_absolute (decoder, (FlacNamespace::FLAC__uint64) bufferedRange.getStart());
286 return;
287 }
288
289 bufferedRange = emptyRange (bufferedRange.getEnd());
290 FLAC__stream_decoder_process_single (decoder);
291 };
292
293 const auto remainingSamples = Reservoir::doBufferedRead (Range<int64> { startSampleInFile, startSampleInFile + numSamples },
294 getBufferedRange,
295 readFromReservoir,
296 fillReservoir);
297
298 if (! remainingSamples.isEmpty())
299 for (int i = numDestChannels; --i >= 0;)
300 if (destSamples[i] != nullptr)
301 zeromem (destSamples[i] + startOffsetInDestBuffer + (remainingSamples.getStart() - startSampleInFile),
302 (size_t) remainingSamples.getLength() * sizeof (int));
303
304 return true;
305 }
306
307 void useSamples (const FlacNamespace::FLAC__int32* const buffer[], int numSamples)
308 {
309 if (scanningForLength)
310 {
311 lengthInSamples += numSamples;
312 }
313 else
314 {
315 if (numSamples > reservoir.getNumSamples())
316 reservoir.setSize ((int) numChannels, numSamples, false, false, true);
317
318 auto bitsToShift = 32 - bitsPerSample;
319
320 for (int i = 0; i < (int) numChannels; ++i)
321 {
322 auto* src = buffer[i];
323 int n = i;
324
325 while (src == nullptr && n > 0)
326 src = buffer [--n];
327
328 if (src != nullptr)
329 {
330 auto* dest = reinterpret_cast<int*> (reservoir.getWritePointer (i));
331
332 for (int j = 0; j < numSamples; ++j)
333 dest[j] = src[j] << bitsToShift;
334 }
335 }
336
337 bufferedRange.setLength (numSamples);
338 }
339 }
340
341 //==============================================================================
342 static FlacNamespace::FLAC__StreamDecoderReadStatus readCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__byte buffer[], size_t* bytes, void* client_data)
343 {
344 *bytes = (size_t) static_cast<const FlacReader*> (client_data)->input->read (buffer, (int) *bytes);
345 return FlacNamespace::FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
346 }
347
348 static FlacNamespace::FLAC__StreamDecoderSeekStatus seekCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64 absolute_byte_offset, void* client_data)
349 {
350 static_cast<const FlacReader*> (client_data)->input->setPosition ((int) absolute_byte_offset);
351 return FlacNamespace::FLAC__STREAM_DECODER_SEEK_STATUS_OK;
352 }
353
354 static FlacNamespace::FLAC__StreamDecoderTellStatus tellCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data)
355 {
356 *absolute_byte_offset = (uint64) static_cast<const FlacReader*> (client_data)->input->getPosition();
357 return FlacNamespace::FLAC__STREAM_DECODER_TELL_STATUS_OK;
358 }
359
360 static FlacNamespace::FLAC__StreamDecoderLengthStatus lengthCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* stream_length, void* client_data)
361 {
362 *stream_length = (uint64) static_cast<const FlacReader*> (client_data)->input->getTotalLength();
363 return FlacNamespace::FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
364 }
365
366 static FlacNamespace::FLAC__bool eofCallback_ (const FlacNamespace::FLAC__StreamDecoder*, void* client_data)
367 {
368 return static_cast<const FlacReader*> (client_data)->input->isExhausted();
369 }
370
371 static FlacNamespace::FLAC__StreamDecoderWriteStatus writeCallback_ (const FlacNamespace::FLAC__StreamDecoder*,
372 const FlacNamespace::FLAC__Frame* frame,
373 const FlacNamespace::FLAC__int32* const buffer[],
374 void* client_data)
375 {
376 static_cast<FlacReader*> (client_data)->useSamples (buffer, (int) frame->header.blocksize);
377 return FlacNamespace::FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
378 }
379
380 static void metadataCallback_ (const FlacNamespace::FLAC__StreamDecoder*,
381 const FlacNamespace::FLAC__StreamMetadata* metadata,
382 void* client_data)
383 {
384 static_cast<FlacReader*> (client_data)->useMetadata (metadata->data.stream_info);
385 }
386
387 static void errorCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__StreamDecoderErrorStatus, void*)
388 {
389 }
390
391private:
392 FlacNamespace::FLAC__StreamDecoder* decoder;
393 AudioBuffer<float> reservoir;
394 Range<int64> bufferedRange;
395 bool ok = false, scanningForLength = false;
396
398};
399
400
401//==============================================================================
402class FlacWriter final : public AudioFormatWriter
403{
404public:
405 FlacWriter (OutputStream* out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex)
406 : AudioFormatWriter (out, flacFormatName, rate, numChans, bits),
407 streamStartPos (output != nullptr ? jmax (output->getPosition(), 0ll) : 0ll)
408 {
409 encoder = FlacNamespace::FLAC__stream_encoder_new();
410
411 if (qualityOptionIndex > 0)
412 FLAC__stream_encoder_set_compression_level (encoder, (uint32) jmin (8, qualityOptionIndex));
413
414 FLAC__stream_encoder_set_do_mid_side_stereo (encoder, numChannels == 2);
415 FLAC__stream_encoder_set_loose_mid_side_stereo (encoder, numChannels == 2);
416 FLAC__stream_encoder_set_channels (encoder, numChannels);
417 FLAC__stream_encoder_set_bits_per_sample (encoder, jmin ((unsigned int) 24, bitsPerSample));
418 FLAC__stream_encoder_set_sample_rate (encoder, (unsigned int) sampleRate);
419 FLAC__stream_encoder_set_blocksize (encoder, 0);
420 FLAC__stream_encoder_set_do_escape_coding (encoder, true);
421
422 ok = FLAC__stream_encoder_init_stream (encoder,
423 encodeWriteCallback, encodeSeekCallback,
424 encodeTellCallback, encodeMetadataCallback,
425 this) == FlacNamespace::FLAC__STREAM_ENCODER_INIT_STATUS_OK;
426 }
427
428 ~FlacWriter() override
429 {
430 if (ok)
431 {
432 FlacNamespace::FLAC__stream_encoder_finish (encoder);
433 output->flush();
434 }
435 else
436 {
437 output = nullptr; // to stop the base class deleting this, as it needs to be returned
438 // to the caller of createWriter()
439 }
440
441 FlacNamespace::FLAC__stream_encoder_delete (encoder);
442 }
443
444 //==============================================================================
445 bool write (const int** samplesToWrite, int numSamples) override
446 {
447 if (! ok)
448 return false;
449
450 HeapBlock<int*> channels;
451 HeapBlock<int> temp;
452 auto bitsToShift = 32 - (int) bitsPerSample;
453
454 if (bitsToShift > 0)
455 {
456 temp.malloc (numChannels * (size_t) numSamples);
457 channels.calloc (numChannels + 1);
458
459 for (unsigned int i = 0; i < numChannels; ++i)
460 {
461 if (samplesToWrite[i] == nullptr)
462 break;
463
464 auto* destData = temp.get() + i * (size_t) numSamples;
465 channels[i] = destData;
466
467 for (int j = 0; j < numSamples; ++j)
468 destData[j] = (samplesToWrite[i][j] >> bitsToShift);
469 }
470
471 samplesToWrite = const_cast<const int**> (channels.get());
472 }
473
474 return FLAC__stream_encoder_process (encoder, (const FlacNamespace::FLAC__int32**) samplesToWrite, (unsigned) numSamples) != 0;
475 }
476
477 bool writeData (const void* const data, const int size) const
478 {
479 return output->write (data, (size_t) size);
480 }
481
482 static void packUint32 (FlacNamespace::FLAC__uint32 val, FlacNamespace::FLAC__byte* b, const int bytes)
483 {
484 b += bytes;
485
486 for (int i = 0; i < bytes; ++i)
487 {
488 *(--b) = (FlacNamespace::FLAC__byte) (val & 0xff);
489 val >>= 8;
490 }
491 }
492
493 void writeMetaData (const FlacNamespace::FLAC__StreamMetadata* metadata)
494 {
495 using namespace FlacNamespace;
496 auto& info = metadata->data.stream_info;
497
498 unsigned char buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH];
499 const unsigned int channelsMinus1 = info.channels - 1;
500 const unsigned int bitsMinus1 = info.bits_per_sample - 1;
501
502 packUint32 (info.min_blocksize, buffer, 2);
503 packUint32 (info.max_blocksize, buffer + 2, 2);
504 packUint32 (info.min_framesize, buffer + 4, 3);
505 packUint32 (info.max_framesize, buffer + 7, 3);
506 buffer[10] = (uint8) ((info.sample_rate >> 12) & 0xff);
507 buffer[11] = (uint8) ((info.sample_rate >> 4) & 0xff);
508 buffer[12] = (uint8) (((info.sample_rate & 0x0f) << 4) | (channelsMinus1 << 1) | (bitsMinus1 >> 4));
509 buffer[13] = (FLAC__byte) (((bitsMinus1 & 0x0f) << 4) | (unsigned int) ((info.total_samples >> 32) & 0x0f));
510 packUint32 ((FLAC__uint32) info.total_samples, buffer + 14, 4);
511 memcpy (buffer + 18, info.md5sum, 16);
512
513 [[maybe_unused]] const bool seekOk = output->setPosition (streamStartPos + 4);
514
515 // if this fails, you've given it an output stream that can't seek! It needs
516 // to be able to seek back to write the header
517 jassert (seekOk);
518
519 output->writeIntBigEndian (FLAC__STREAM_METADATA_STREAMINFO_LENGTH);
520 output->write (buffer, FLAC__STREAM_METADATA_STREAMINFO_LENGTH);
521 }
522
523 //==============================================================================
524 static FlacNamespace::FLAC__StreamEncoderWriteStatus encodeWriteCallback (const FlacNamespace::FLAC__StreamEncoder*,
525 const FlacNamespace::FLAC__byte buffer[],
526 size_t bytes,
527 unsigned int /*samples*/,
528 unsigned int /*current_frame*/,
529 void* client_data)
530 {
531 return static_cast<FlacWriter*> (client_data)->writeData (buffer, (int) bytes)
532 ? FlacNamespace::FLAC__STREAM_ENCODER_WRITE_STATUS_OK
533 : FlacNamespace::FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
534 }
535
536 static FlacNamespace::FLAC__StreamEncoderSeekStatus encodeSeekCallback (const FlacNamespace::FLAC__StreamEncoder*, FlacNamespace::FLAC__uint64, void*)
537 {
538 return FlacNamespace::FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED;
539 }
540
541 static FlacNamespace::FLAC__StreamEncoderTellStatus encodeTellCallback (const FlacNamespace::FLAC__StreamEncoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data)
542 {
543 if (client_data == nullptr)
544 return FlacNamespace::FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED;
545
546 *absolute_byte_offset = (FlacNamespace::FLAC__uint64) static_cast<FlacWriter*> (client_data)->output->getPosition();
547 return FlacNamespace::FLAC__STREAM_ENCODER_TELL_STATUS_OK;
548 }
549
550 static void encodeMetadataCallback (const FlacNamespace::FLAC__StreamEncoder*, const FlacNamespace::FLAC__StreamMetadata* metadata, void* client_data)
551 {
552 static_cast<FlacWriter*> (client_data)->writeMetaData (metadata);
553 }
554
555 bool ok = false;
556
557private:
558 FlacNamespace::FLAC__StreamEncoder* encoder;
559 int64 streamStartPos;
560
562};
563
564
565//==============================================================================
566FlacAudioFormat::FlacAudioFormat() : AudioFormat (flacFormatName, ".flac") {}
567FlacAudioFormat::~FlacAudioFormat() {}
568
569Array<int> FlacAudioFormat::getPossibleSampleRates()
570{
571 return { 8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000,
572 88200, 96000, 176400, 192000, 352800, 384000 };
573}
574
575Array<int> FlacAudioFormat::getPossibleBitDepths()
576{
577 return { 16, 24 };
578}
579
580bool FlacAudioFormat::canDoStereo() { return true; }
581bool FlacAudioFormat::canDoMono() { return true; }
582bool FlacAudioFormat::isCompressed() { return true; }
583
584AudioFormatReader* FlacAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails)
585{
586 std::unique_ptr<FlacReader> r (new FlacReader (in));
587
588 if (r->sampleRate > 0)
589 return r.release();
590
591 if (! deleteStreamIfOpeningFails)
592 r->input = nullptr;
593
594 return nullptr;
595}
596
597AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out,
598 double sampleRate,
599 unsigned int numberOfChannels,
600 int bitsPerSample,
601 const StringPairArray& /*metadataValues*/,
602 int qualityOptionIndex)
603{
604 if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
605 {
606 std::unique_ptr<FlacWriter> w (new FlacWriter (out, sampleRate, numberOfChannels,
607 (uint32) bitsPerSample, qualityOptionIndex));
608 if (w->ok)
609 return w.release();
610 }
611
612 return nullptr;
613}
614
615StringArray FlacAudioFormat::getQualityOptions()
616{
617 return { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)" };
618}
619
620#endif
621
622} // namespace juce
static constexpr Range emptyRange(const ValueType start) noexcept
Returns a range with the specified start position and a length of zero.
Definition juce_Range.h:73
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
typedef int
memcpy
JUCE Namespace.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.
Definition juce_Memory.h:28
write
typedef size_t