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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_RexFileFormat.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
14#if TRACKTION_ENABLE_REX
15
16const char* const RexAudioFormat::rexTempo = "rex tempo";
17const char* const RexAudioFormat::rexDenominator = "rex denominator";
18const char* const RexAudioFormat::rexNumerator = "rex numerator";
19const char* const RexAudioFormat::rexBeatPoints = "rex beat points";
20
21//==============================================================================
22inline const char* rexErrorToString (REX::REXError e) noexcept
23{
24 switch (e)
25 {
26 case REX::kREXError_NoError: return "No error";
27 case REX::kREXError_OperationAbortedByUser: return "Operation aborted by user";
28 case REX::kREXError_NoCreatorInfoAvailable: return "No creator info available";
29 case REX::kREXError_OutOfMemory: return "Out of Memory";
30 case REX::kREXError_FileCorrupt: return "File is corrupt";
31 case REX::kREXError_REX2FileTooNew: return "REX2 file is too new";
32 case REX::kREXError_FileHasZeroLoopLength: return "File has a zero length for the loop";
33 case REX::kREXImplError_DLLNotInitialized: return "DLL Not Initialized";
34 case REX::kREXImplError_DLLAlreadyInitialized: return "DLL Already Initialized";
35 case REX::kREXImplError_InvalidHandle: return "Invalid handle";
36 case REX::kREXImplError_InvalidSize: return "Invalid size";
37 case REX::kREXImplError_InvalidArgument: return "Invalid argument";
38 case REX::kREXImplError_InvalidSlice: return "Invalid slice";
39 case REX::kREXImplError_InvalidSampleRate: return "Invalid sample rate";
40 case REX::kREXImplError_BufferTooSmall: return "Buffer too small";
41 case REX::kREXImplError_IsBeingPreviewed: return "Is being previewed";
42 case REX::kREXImplError_NotBeingPreviewed: return "Not being previewed";
43 case REX::kREXImplError_InvalidTempo: return "Invalid tempo";
44 default: break;
45 }
46
47 return "Undefined";
48}
49
50static bool checkRexError (REX::REXError e)
51{
52 if (e == REX::kREXError_NoError)
53 return true;
54
55 TRACKTION_LOG_ERROR ("REX (Code " + juce::String ((int) e) + "): " + rexErrorToString (e));
56 return false;
57}
58
59//==============================================================================
60struct RexSystem
61{
62 RexSystem()
63 {
65 const REX::REXError e = REX::REXInitializeDLL();
66
67 if (e == REX::kREXImplError_DLLNotInitialized)
68 {
69 startupErrorMessage = TRANS("Error loading Propellerheads REX DLL!");
70 }
71 else if (e != REX::kREXError_NoError)
72 {
73 startupErrorMessage = TRANS("An unknown error occured with the Propellerheads REX format!");
74 }
75
76 initialised = checkRexError (e);
77 }
78
79 ~RexSystem()
80 {
82
83 if (initialised)
84 REX::REXUninitializeDLL();
85 }
86
88 bool initialised = false;
89 juce::String startupErrorMessage;
90
92};
93
94static RexSystem& getRexSystem()
95{
96 static RexSystem rex;
97 return rex;
98}
99
100struct RexHandleWrapper
101{
102 RexHandleWrapper (const void* rexData, size_t rexDataSize)
103 {
105 ok = checkRexError (REX::REXCreate (&handle, (const char*) rexData,
106 (REX::REX_int32_t) rexDataSize, nullptr, nullptr));
107 }
108
109 ~RexHandleWrapper()
110 {
112 if (handle != 0)
113 REX::REXDelete (&handle);
114 }
115
116 REX::REXHandle handle;
117 bool ok = false;
118
120};
121
122//==============================================================================
123class RexAudioFormatReader : public juce::AudioFormatReader
124{
125public:
126 RexAudioFormatReader (juce::InputStream* sourceStream, const juce::String& name)
127 : juce::AudioFormatReader (sourceStream, name), buffer (2, 2)
128 {
130 juce::MemoryBlock rexData;
131 sourceStream->readIntoMemoryBlock (rexData);
132
133 loadedOk = decompress (rexData.getData(), rexData.getSize());
134 }
135
136 bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer,
137 juce::int64 startSampleInFile, int numSamples) override
138 {
140 clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
141 startSampleInFile, numSamples, lengthInSamples);
142
143 if (numSamples > 0)
144 {
145 jassert (startOffsetInDestBuffer + numSamples <= buffer.getNumSamples());
146
147 for (int i = 0; i < numDestChannels; ++i)
148 {
149 if (float* dest = (float*) destSamples[i])
150 {
151 dest += startOffsetInDestBuffer;
152
153 if (i < buffer.getNumChannels())
154 memcpy (dest, buffer.getReadPointer (i, (int) startSampleInFile),
155 sizeof (float) * (size_t) numSamples);
156 }
157 }
158 }
159
160 return true;
161 }
162
163 bool loadedOk;
164
165private:
167
168 bool decompress (const void* rexData, size_t rexDataSize)
169 {
171 auto& rex = getRexSystem();
172
173 const juce::ScopedLock lock (rex.lock);
174
175 if (! rex.initialised)
176 return false;
177
178 RexHandleWrapper handle (rexData, rexDataSize);
179 if (! handle.ok)
180 return false;
181
182 REX::REXInfo info;
183 if (! checkRexError (REX::REXGetInfo (handle.handle, sizeof (info), &info)))
184 return false;
185
186 if (! checkRexError (REX::REXSetOutputSampleRate (handle.handle, info.fSampleRate)))
187 return false;
188
189 const double beats = info.fPPQLength / 15360.0;
190 const double bps = info.fTempo / (1000.0 * 60.0);
191
192 lengthInSamples = (SampleCount) ((beats / bps) * info.fSampleRate);
193 sampleRate = info.fSampleRate;
194 bitsPerSample = (unsigned int) info.fBitDepth;
195 numChannels = (unsigned int) info.fChannels;
196 usesFloatingPointData = true;
197
198 buffer.setSize ((int) info.fChannels, (int) lengthInSamples);
199 buffer.clear();
200
201 juce::StringArray beatPoints;
202
203 for (int j = 0; j < info.fSliceCount; ++j)
204 {
205 REX::REXSliceInfo slcInfo;
206 if (! checkRexError (REX::REXGetSliceInfo (handle.handle, j, sizeof(slcInfo), &slcInfo)))
207 return false;
208
209 juce::AudioBuffer<float> sliceData (buffer.getNumChannels(), (int) slcInfo.fSampleLength);
210
211 if (! checkRexError (REX::REXRenderSlice (handle.handle, j, slcInfo.fSampleLength, (float**)sliceData.getArrayOfWritePointers())))
212 return false;
213
214 auto offset = (SampleCount) ((slcInfo.fPPQPos / 15360.0) / (info.fTempo / (1000.0 * 60.0)) * info.fSampleRate);
215 auto numSamples = (int) std::min (lengthInSamples - offset, (SampleCount) slcInfo.fSampleLength);
216
217 if (numSamples > 0)
218 for (int i = 0; i < sliceData.getNumChannels(); ++i)
219 buffer.addFrom (i, (int) offset, sliceData, i, 0, numSamples);
220
221 beatPoints.add (juce::String (offset));
222 }
223
224 metadataValues.set (RexAudioFormat::rexDenominator, juce::String ((int) info.fTimeSignDenom));
225 metadataValues.set (RexAudioFormat::rexNumerator, juce::String ((int) info.fTimeSignNom));
226 metadataValues.set (RexAudioFormat::rexTempo, juce::String (info.fTempo / 1000.0));
227
228 if (beatPoints.size() > 0)
229 metadataValues.set (RexAudioFormat::rexBeatPoints, beatPoints.joinIntoString (";"));
230
231 return true;
232 }
233
235};
236
237//==============================================================================
238RexAudioFormat::RexAudioFormat()
239 : AudioFormat (NEEDS_TRANS("REX2 file"), ".rx2 .rex .rcy")
240{
241}
242
243RexAudioFormat::~RexAudioFormat()
244{
245}
246
247juce::AudioFormatReader* RexAudioFormat::createReaderFor (juce::InputStream* sourceStream, bool deleteStreamIfOpeningFails)
248{
250 auto r = std::make_unique<RexAudioFormatReader> (sourceStream, getFormatName());
251
252 if (r->loadedOk)
253 return r.release();
254
255 if (! deleteStreamIfOpeningFails)
256 r->input = nullptr;
257
258 return {};
259}
260
261juce::AudioFormatWriter* RexAudioFormat::createWriterFor (juce::OutputStream*, double, unsigned int, int, const juce::StringPairArray&, int)
262{
263 return {};
264}
265
266juce::String RexAudioFormat::getErrorLoadingDLL()
267{
268 return getRexSystem().startupErrorMessage;
269}
270
271#endif
272
273
274}} // namespace tracktion { inline namespace engine
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
int getNumChannels() const noexcept
void clear() noexcept
void addFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples, Type gainToApplyToSource=Type(1)) noexcept
virtual size_t readIntoMemoryBlock(MemoryBlock &destBlock, ssize_t maxNumBytesToRead=-1)
String joinIntoString(StringRef separatorString, int startIndex=0, int numberOfElements=-1) const
int size() const noexcept
void add(String stringToAdd)
T is_pointer_v
#define TRANS(stringLiteral)
#define NEEDS_TRANS(stringLiteral)
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
typedef int
T lock(T... args)
memcpy
T min(T... args)
long long int64
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.