tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_FloatAudioFileFormat.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14static int getFloatFileHeaderIntV1() { return (int) juce::ByteOrder::littleEndianInt ("TRKF"); }
15static int getFloatFileHeaderIntV2() { return (int) juce::ByteOrder::littleEndianInt ("TF64"); }
16
17
18//==============================================================================
20{
21public:
23 : AudioFormatReader (in, TRANS("Tracktion audio file"))
24 {
26
27 auto header = in->readInt();
28 if (header == getFloatFileHeaderIntV1())
29 {
30 dataStartOffset = in->readInt();
31 sampleRate = in->readInt();
33 numChannels = (unsigned int) in->readShort();
34 bigEndian = in->readShort() != 0;
35 bitsPerSample = 32;
36
37 if (sampleRate < 32000 || sampleRate > 192000 || numChannels < 1 || numChannels > 16)
38 sampleRate = 0;
39 }
40 else if (header == getFloatFileHeaderIntV2())
41 {
42 dataStartOffset = in->readInt();
43 sampleRate = in->readInt();
45 numChannels = (unsigned int) in->readShort();
46 bigEndian = in->readShort() != 0;
47 bitsPerSample = 32;
48
49 if (sampleRate < 32000 || sampleRate > 192000 || numChannels < 1 || numChannels > 16)
50 sampleRate = 0;
51 }
52 }
53
54 bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer,
55 juce::int64 startSampleInFile, int numSamples)
56 {
57 clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
58 startSampleInFile, numSamples, lengthInSamples);
59
60 if (numSamples <= 0)
61 return true;
62
63 input->setPosition (4 * startSampleInFile * numChannels + dataStartOffset);
64 const int bytesPerFrame = 4 * (int) numChannels;
65
66 while (numSamples > 0)
67 {
68 const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
69 char tempBuffer [tempBufSize];
70
71 const int numThisTime = std::min (tempBufSize / bytesPerFrame, numSamples);
72 const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);
73
74 if (bytesRead < numThisTime * bytesPerFrame)
75 {
76 jassert (bytesRead >= 0);
77 std::memset (tempBuffer + bytesRead, 0, (size_t) (numThisTime * bytesPerFrame - bytesRead));
78 }
79
80 if (bigEndian)
82 ::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime);
83 else
85 ::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime);
86
87 startOffsetInDestBuffer += numThisTime;
88 numSamples -= numThisTime;
89 }
90
91 return true;
92 }
93
94 int dataStartOffset;
95 bool bigEndian = false;
96
98};
99
100//==============================================================================
102{
103public:
104 FloatAudioFormatWriter (juce::OutputStream* out, double sampleRate_, unsigned int numChannels_)
105 : AudioFormatWriter (out,
106 TRANS("Tracktion audio file"),
107 sampleRate_,
108 numChannels_,
109 32),
110 lengthInSamples (0)
111 {
113 writeHeader();
114 }
115
117 {
118 output->setPosition (0);
119 writeHeader();
120 }
121
122 //==============================================================================
123 bool write (const int** data, int numSamps)
124 {
125 lengthInSamples += numSamps;
126
127 for (int j = 0; j < numSamps; ++j)
128 {
129 for (unsigned int i = 0; i < numChannels; ++i)
130 {
131 if (const float* chan = (float*) (data[i]))
132 {
133 float val = chan[j];
134 JUCE_UNDENORMALISE (val);
135 output->writeFloat (val);
136 }
137 else
138 {
139 break;
140 }
141 }
142 }
143
144 return true;
145 }
146
147private:
148 juce::int64 lengthInSamples;
149
150 void writeHeader()
151 {
152 const int headerSize = 512;
153 output->writeInt (getFloatFileHeaderIntV2());
154 output->writeInt (headerSize);
156 output->writeInt64 (lengthInSamples);
157 output->writeShort ((short)numChannels);
158 output->writeShort (0); // big-endian
159
160 while (output->getPosition() < headerSize)
161 output->writeByte (0);
162 }
163
165};
166
167//==============================================================================
169{
170public:
173 reader.dataStartOffset,
174 reader.lengthInSamples * 4 * reader.numChannels,
175 4 * (int) reader.numChannels),
176 bigEndian (reader.bigEndian)
177 {
178 }
179
180 bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer,
181 juce::int64 startSampleInFile, int numSamples) override
182 {
183 clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
184 startSampleInFile, numSamples, lengthInSamples);
185
186 if (map == nullptr || ! mappedSection.contains ({ startSampleInFile, startSampleInFile + numSamples }))
187 {
188 jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
189 return false;
190 }
191
192 if (bigEndian)
194 ::read (destSamples, startOffsetInDestBuffer, numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
195 else
197 ::read (destSamples, startOffsetInDestBuffer, numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
198
199 return true;
200 }
201
202 void getSample (juce::int64 sample, float* result) const noexcept override
203 {
204 if (map == nullptr || ! mappedSection.contains (sample))
205 {
206 jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
207
208 std::memset (result, 0, sizeof (float) * numChannels);
209 return;
210 }
211
212 const void* sourceData = sampleToPointer (sample);
213
214 if (bigEndian)
216 ::read (&result, 0, 1, sourceData, 1, (int) numChannels);
217 else
219 ::read (&result, 0, 1, sourceData, 1, (int) numChannels);
220 }
221
223
224 void readMaxLevels (juce::int64 startSampleInFile, juce::int64 numSamples,
225 juce::Range<float>* results, int numChannelsToRead) override
226 {
227 if (numSamples <= 0)
228 {
229 for (int i = 0; i < numChannelsToRead; ++i)
230 results[i] = {};
231
232 return;
233 }
234
235 if (map == nullptr || ! mappedSection.contains ({ startSampleInFile, startSampleInFile + numSamples }))
236 {
237 jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
238
239 for (int i = 0; i < numChannelsToRead; ++i)
240 results[i] = {};
241
242 return;
243 }
244
245 switch (bitsPerSample)
246 {
247 case 8: scanMinAndMax<juce::AudioData::UInt8> (startSampleInFile, numSamples, results, numChannelsToRead); break;
248 case 16: scanMinAndMax<juce::AudioData::Int16> (startSampleInFile, numSamples, results, numChannelsToRead); break;
249 case 24: scanMinAndMax<juce::AudioData::Int24> (startSampleInFile, numSamples, results, numChannelsToRead); break;
250 case 32:
251 {
252 if (usesFloatingPointData) scanMinAndMax<juce::AudioData::Float32> (startSampleInFile, numSamples, results, numChannelsToRead);
253 else scanMinAndMax<juce::AudioData::Int32> (startSampleInFile, numSamples, results, numChannelsToRead);
254 break;
255 }
256 default: jassertfalse; break;
257 }
258 }
259
260private:
261 const bool bigEndian;
262
263 template <typename SampleType>
264 void scanMinAndMax (int64_t startSampleInFile, int64_t numSamples, juce::Range<float>* results, int numChannelsToRead) const
265 {
267
268 for (int i = 0; i < numChannelsToRead; ++i)
269 results[i] = SourceType (sampleToPointer (startSampleInFile), (int) numChannels).findMinAndMax ((size_t) numSamples);
270 }
271
273};
274
275//==============================================================================
276FloatAudioFormat::FloatAudioFormat() : AudioFormat ("Tracktion audio file", ".trkaudio") {}
277FloatAudioFormat::~FloatAudioFormat() {}
278
279juce::Array<int> FloatAudioFormat::getPossibleSampleRates() { return { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; }
280juce::Array<int> FloatAudioFormat::getPossibleBitDepths() { return { 32 }; }
281
282bool FloatAudioFormat::canDoStereo() { return true; }
283bool FloatAudioFormat::canDoMono() { return true; }
284
285bool FloatAudioFormat::canHandleFile (const juce::File& f)
286{
287 return f.hasFileExtension (".trkaudio")
288 #if JUCE_WINDOWS
289 || f.hasFileExtension ({})
290 #endif
291 || f.hasFileExtension (".freeze");
292}
293
294juce::AudioFormatReader* FloatAudioFormat::createReaderFor (juce::InputStream* in, bool deleteStreamIfOpeningFails)
295{
296 std::unique_ptr<FloatAudioFormatReader> r (new FloatAudioFormatReader (in));
297
298 if (r->sampleRate > 0)
299 return r.release();
300
301 if (! deleteStreamIfOpeningFails)
302 r->input = nullptr;
303
304 return {};
305}
306
307juce::MemoryMappedAudioFormatReader* FloatAudioFormat::createMemoryMappedReader (const juce::File& file)
308{
309 if (auto fin = file.createInputStream())
310 {
311 FloatAudioFormatReader reader (fin.release());
312
313 if (reader.lengthInSamples > 0)
314 return new MemoryMappedFloatReader (file, reader);
315 }
316
317 return {};
318}
319
320juce::AudioFormatWriter* FloatAudioFormat::createWriterFor (juce::OutputStream* out,
321 double sampleRate,
322 unsigned int numChannels,
323 int /*bitsPerSample*/,
324 const juce::StringPairArray& /*metadataValues*/,
325 int /*qualityOptionIndex*/)
326{
327 return new FloatAudioFormatWriter (out, sampleRate, numChannels);
328}
329
330}} // namespace tracktion { inline namespace engine
static void clearSamplesBeyondAvailableLength(int *const *destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int &numSamples, int64 fileLengthInSamples)
AudioFormatReader(InputStream *sourceStream, const String &formatName)
unsigned int bitsPerSample
virtual void readMaxLevels(int64 startSample, int64 numSamples, Range< float > *results, int numChannelsToRead)
unsigned int numChannels
unsigned int numChannels
AudioFormatWriter(OutputStream *destStream, const String &formatName, double sampleRate, unsigned int numberOfChannels, unsigned int bitsPerSample)
OutputStream * output
static constexpr uint32 littleEndianInt(const void *bytes) noexcept
bool hasFileExtension(StringRef extensionToTest) const
virtual int64 readInt64()
virtual bool setPosition(int64 newPosition)=0
virtual short readShort()
virtual int read(void *destBuffer, int maxBytesToRead)=0
virtual int readInt()
MemoryMappedAudioFormatReader(const File &file, const AudioFormatReader &details, int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame)
const void * sampleToPointer(int64 sample) const noexcept
virtual int64 getPosition()=0
virtual bool writeFloat(float value)
virtual bool writeByte(char byte)
virtual bool writeShort(short value)
virtual bool writeInt64(int64 value)
virtual bool setPosition(int64 newPosition)=0
virtual bool writeInt(int value)
static Range findMinAndMax(const ValueType *values, Integral numValues) noexcept
#define TRANS(stringLiteral)
#define JUCE_UNDENORMALISE(x)
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define jassertfalse
typedef int
T memset(T... args)
T min(T... args)
int roundToInt(const FloatType value) noexcept
long long int64
typedef int64_t