1 |
/*************************************************************************** |
2 |
* * |
3 |
* Copyright (C) 2014 Christian Schoenebeck * |
4 |
* <cuse@users.sourceforge.net> * |
5 |
* * |
6 |
* This library is part of libgig. * |
7 |
* * |
8 |
* This library is free software; you can redistribute it and/or modify * |
9 |
* it under the terms of the GNU General Public License as published by * |
10 |
* the Free Software Foundation; either version 2 of the License, or * |
11 |
* (at your option) any later version. * |
12 |
* * |
13 |
* This library is distributed in the hope that it will be useful, * |
14 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of * |
15 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
16 |
* GNU General Public License for more details. * |
17 |
* * |
18 |
* You should have received a copy of the GNU General Public License * |
19 |
* along with this library; if not, write to the Free Software * |
20 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * |
21 |
* MA 02111-1307 USA * |
22 |
***************************************************************************/ |
23 |
|
24 |
#include "Korg.h" |
25 |
|
26 |
#include <string.h> // for memset() |
27 |
|
28 |
#if WORDS_BIGENDIAN |
29 |
# define CHUNK_ID_MSP1 0x4d535031 |
30 |
# define CHUNK_ID_RLP1 0x524c5031 |
31 |
# define CHUNK_ID_SMP1 0x534d5031 |
32 |
# define CHUNK_ID_SMD1 0x534d4431 |
33 |
# define CHUNK_ID_NAME 0x4e414d45 |
34 |
#else // little endian |
35 |
# define CHUNK_ID_MSP1 0x3150534d |
36 |
# define CHUNK_ID_RLP1 0x31504c52 |
37 |
# define CHUNK_ID_SMP1 0x31504d53 |
38 |
# define CHUNK_ID_SMD1 0x31444d53 |
39 |
# define CHUNK_ID_NAME 0x454d414e |
40 |
#endif // WORDS_BIGENDIAN |
41 |
|
42 |
#define SMD1_CHUNK_HEADER_SZ 12 |
43 |
|
44 |
namespace Korg { |
45 |
|
46 |
#if defined(WIN32) |
47 |
static const String PATH_SEP = "\\"; |
48 |
#else |
49 |
static const String PATH_SEP = "/"; |
50 |
#endif |
51 |
|
52 |
// *************** Internal functions ************** |
53 |
// * |
54 |
|
55 |
template<unsigned int SZ> |
56 |
inline String readText(RIFF::Chunk* ck) { |
57 |
char buf[SZ+1] = {}; |
58 |
int n = (int) ck->Read(buf, SZ, 1); |
59 |
if (n != SZ) |
60 |
throw Exception("Premature end while reading text field"); |
61 |
String s = buf; |
62 |
return s; |
63 |
} |
64 |
|
65 |
/// Read 24 bytes of ASCII text from given chunk and return it as String. |
66 |
inline String readText24(RIFF::Chunk* ck) { |
67 |
return readText<24>(ck); |
68 |
} |
69 |
|
70 |
/// Read 16 bytes of ASCII text from given chunk and return it as String. |
71 |
inline String readText16(RIFF::Chunk* ck) { |
72 |
return readText<16>(ck); |
73 |
} |
74 |
|
75 |
/// Read 12 bytes of ASCII text from given chunk and return it as String. |
76 |
inline String readText12(RIFF::Chunk* ck) { |
77 |
return readText<12>(ck); |
78 |
} |
79 |
|
80 |
/// For example passing "FOO.KMP" will return "FOO". |
81 |
inline String removeFileTypeExtension(const String& filename) { |
82 |
size_t pos = filename.find_last_of('.'); |
83 |
if (pos == String::npos) return filename; |
84 |
return filename.substr(0, pos); |
85 |
} |
86 |
|
87 |
// *************** KSFSample *************** |
88 |
// * |
89 |
|
90 |
KSFSample::KSFSample(const String& filename) { |
91 |
RAMCache.Size = 0; |
92 |
RAMCache.pStart = NULL; |
93 |
RAMCache.NullExtensionSize = 0; |
94 |
|
95 |
riff = new RIFF::File( |
96 |
filename, CHUNK_ID_SMP1, RIFF::endian_big, RIFF::layout_flat |
97 |
); |
98 |
|
99 |
// read 'SMP1' chunk |
100 |
RIFF::Chunk* smp1 = riff->GetSubChunk(CHUNK_ID_SMP1); |
101 |
if (!smp1) |
102 |
throw Exception("Not a Korg sample file ('SMP1' chunk not found)"); |
103 |
if (smp1->GetSize() < 32) |
104 |
throw Exception("Not a Korg sample file ('SMP1' chunk size too small)"); |
105 |
Name = readText16(smp1); |
106 |
DefaultBank = smp1->ReadUint8(); |
107 |
Start = smp1->ReadUint8() << 16 | smp1->ReadUint8() << 8 | smp1->ReadUint8(); |
108 |
Start2 = smp1->ReadUint32(); |
109 |
LoopStart = smp1->ReadUint32(); |
110 |
LoopEnd = smp1->ReadUint32(); |
111 |
|
112 |
// read 'SMD1' chunk |
113 |
RIFF::Chunk* smd1 = riff->GetSubChunk(CHUNK_ID_SMD1); |
114 |
if (!smd1) |
115 |
throw Exception("Not a Korg sample file ('SMD1' chunk not found)"); |
116 |
if (smd1->GetSize() < 12) |
117 |
throw Exception("Not a Korg sample file ('SMD1' chunk size too small)"); |
118 |
SampleRate = smd1->ReadUint32(); |
119 |
Attributes = smd1->ReadUint8(); |
120 |
LoopTune = smd1->ReadInt8(); |
121 |
Channels = smd1->ReadUint8(); |
122 |
BitDepth = smd1->ReadUint8(); |
123 |
SamplePoints = smd1->ReadUint32(); |
124 |
} |
125 |
|
126 |
KSFSample::~KSFSample() { |
127 |
if (RAMCache.pStart) delete[] (int8_t*) RAMCache.pStart; |
128 |
if (riff) delete riff; |
129 |
} |
130 |
|
131 |
/** |
132 |
* Loads the whole sample wave into RAM. Use ReleaseSampleData() to free |
133 |
* the memory if you don't need the cached sample data anymore. |
134 |
* |
135 |
* @returns buffer_t structure with start address and size of the buffer |
136 |
* in bytes |
137 |
* @see ReleaseSampleData(), Read(), SetPos() |
138 |
*/ |
139 |
buffer_t KSFSample::LoadSampleData() { |
140 |
return LoadSampleDataWithNullSamplesExtension(this->SamplePoints, 0); // 0 amount of NullSamples |
141 |
} |
142 |
|
143 |
/** |
144 |
* Reads and caches the first \a SampleCount numbers of SamplePoints in RAM. |
145 |
* Use ReleaseSampleData() to free the memory space if you don't need the |
146 |
* cached samples anymore. |
147 |
* |
148 |
* @param SampleCount - number of sample points to load into RAM |
149 |
* @returns buffer_t structure with start address and size of |
150 |
* the cached sample data in bytes |
151 |
* @see ReleaseSampleData(), Read(), SetPos() |
152 |
*/ |
153 |
buffer_t KSFSample::LoadSampleData(unsigned long SampleCount) { |
154 |
return LoadSampleDataWithNullSamplesExtension(SampleCount, 0); // 0 amount of NullSamples |
155 |
} |
156 |
|
157 |
/** |
158 |
* Loads the whole sample wave into RAM. Use ReleaseSampleData() to free the |
159 |
* memory if you don't need the cached sample data anymore. The method will |
160 |
* add \a NullSamplesCount silence samples past the official buffer end |
161 |
* (this won't affect the 'Size' member of the buffer_t structure, that |
162 |
* means 'Size' always reflects the size of the actual sample data, the |
163 |
* buffer might be bigger though). Silence samples past the official buffer |
164 |
* are needed for differential algorithms that always have to take |
165 |
* subsequent samples into account (resampling/interpolation would be an |
166 |
* important example) and avoids memory access faults in such cases. |
167 |
* |
168 |
* @param NullSamplesCount - number of silence samples the buffer should |
169 |
* be extended past it's data end |
170 |
* @returns buffer_t structure with start address and |
171 |
* size of the buffer in bytes |
172 |
* @see ReleaseSampleData(), Read(), SetPos() |
173 |
*/ |
174 |
buffer_t KSFSample::LoadSampleDataWithNullSamplesExtension(uint NullSamplesCount) { |
175 |
return LoadSampleDataWithNullSamplesExtension(this->SamplePoints, NullSamplesCount); |
176 |
} |
177 |
|
178 |
/** |
179 |
* Reads and caches the first \a SampleCount numbers of SamplePoints in RAM. |
180 |
* Use ReleaseSampleData() to free the memory space if you don't need the |
181 |
* cached samples anymore. The method will add \a NullSamplesCount silence |
182 |
* samples past the official buffer end (this won't affect the 'Size' member |
183 |
* of the buffer_t structure, that means 'Size' always reflects the size of |
184 |
* the actual sample data, the buffer might be bigger though). Silence |
185 |
* samples past the official buffer are needed for differential algorithms |
186 |
* that always have to take subsequent samples into account |
187 |
* (resampling/interpolation would be an important example) and avoids |
188 |
* memory access faults in such cases. |
189 |
* |
190 |
* @param SampleCount - number of sample points to load into RAM |
191 |
* @param NullSamplesCount - number of silence samples the buffer should |
192 |
* be extended past it's data end |
193 |
* @returns buffer_t structure with start address and |
194 |
* size of the cached sample data in bytes |
195 |
* @see ReleaseSampleData(), Read(), SetPos() |
196 |
*/ |
197 |
buffer_t KSFSample::LoadSampleDataWithNullSamplesExtension(unsigned long SampleCount, uint NullSamplesCount) { |
198 |
if (SampleCount > this->SamplePoints) SampleCount = this->SamplePoints; |
199 |
if (RAMCache.pStart) delete[] (int8_t*) RAMCache.pStart; |
200 |
unsigned long allocationsize = (SampleCount + NullSamplesCount) * FrameSize(); |
201 |
SetPos(0); // reset read position to beginning of sample |
202 |
RAMCache.pStart = new int8_t[allocationsize]; |
203 |
RAMCache.Size = Read(RAMCache.pStart, SampleCount) * FrameSize(); |
204 |
RAMCache.NullExtensionSize = allocationsize - RAMCache.Size; |
205 |
// fill the remaining buffer space with silence samples |
206 |
memset((int8_t*)RAMCache.pStart + RAMCache.Size, 0, RAMCache.NullExtensionSize); |
207 |
return GetCache(); |
208 |
} |
209 |
|
210 |
/** |
211 |
* Returns current cached sample points. A buffer_t structure will be |
212 |
* returned which contains address pointer to the begin of the cache and |
213 |
* the size of the cached sample data in bytes. Use LoadSampleData() to |
214 |
* cache a specific amount of sample points in RAM. |
215 |
* |
216 |
* @returns buffer_t structure with current cached sample points |
217 |
* @see LoadSampleData(); |
218 |
*/ |
219 |
buffer_t KSFSample::GetCache() const { |
220 |
// return a copy of the buffer_t structure |
221 |
buffer_t result; |
222 |
result.Size = this->RAMCache.Size; |
223 |
result.pStart = this->RAMCache.pStart; |
224 |
result.NullExtensionSize = this->RAMCache.NullExtensionSize; |
225 |
return result; |
226 |
} |
227 |
|
228 |
/** |
229 |
* Frees the cached sample from RAM if loaded with LoadSampleData() |
230 |
* previously. |
231 |
* |
232 |
* @see LoadSampleData(); |
233 |
*/ |
234 |
void KSFSample::ReleaseSampleData() { |
235 |
if (RAMCache.pStart) delete[] (int8_t*) RAMCache.pStart; |
236 |
RAMCache.pStart = NULL; |
237 |
RAMCache.Size = 0; |
238 |
RAMCache.NullExtensionSize = 0; |
239 |
} |
240 |
|
241 |
/** |
242 |
* Sets the position within the sample (in sample points, not in |
243 |
* bytes). Use this method and <i>Read()</i> if you don't want to load |
244 |
* the sample into RAM, thus for disk streaming. |
245 |
* |
246 |
* @param SampleCount number of sample points to jump |
247 |
* @param Whence optional: to which relation \a SampleCount refers |
248 |
* to, if omited <i>RIFF::stream_start</i> is assumed |
249 |
* @returns the new sample position |
250 |
* @see Read() |
251 |
*/ |
252 |
unsigned long KSFSample::SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence) { |
253 |
unsigned long samplePos = GetPos(); |
254 |
switch (Whence) { |
255 |
case RIFF::stream_curpos: |
256 |
samplePos += SampleCount; |
257 |
break; |
258 |
case RIFF::stream_end: |
259 |
samplePos = this->SamplePoints - 1 - SampleCount; |
260 |
break; |
261 |
case RIFF::stream_backward: |
262 |
samplePos -= SampleCount; |
263 |
break; |
264 |
case RIFF::stream_start: |
265 |
default: |
266 |
samplePos = SampleCount; |
267 |
break; |
268 |
} |
269 |
if (samplePos > this->SamplePoints) samplePos = this->SamplePoints; |
270 |
unsigned long bytes = samplePos * FrameSize(); |
271 |
RIFF::Chunk* smd1 = riff->GetSubChunk(CHUNK_ID_SMD1); |
272 |
unsigned long result = smd1->SetPos(SMD1_CHUNK_HEADER_SZ + bytes); |
273 |
return (result - SMD1_CHUNK_HEADER_SZ) / FrameSize(); |
274 |
} |
275 |
|
276 |
/** |
277 |
* Returns the current position in the sample (in sample points). |
278 |
*/ |
279 |
unsigned long KSFSample::GetPos() const { |
280 |
RIFF::Chunk* smd1 = riff->GetSubChunk(CHUNK_ID_SMD1); |
281 |
return (smd1->GetPos() - SMD1_CHUNK_HEADER_SZ) / FrameSize(); |
282 |
} |
283 |
|
284 |
/** |
285 |
* Reads \a SampleCount number of sample points from the current |
286 |
* position into the buffer pointed by \a pBuffer and increments the |
287 |
* position within the sample. Use this method and SetPos() if you don't |
288 |
* want to load the sample into RAM, thus for disk streaming. |
289 |
* |
290 |
* <b>Caution:</b> If you are using more than one streaming thread, you |
291 |
* have to use an external decompression buffer for <b>EACH</b> |
292 |
* streaming thread to avoid race conditions and crashes (which is not |
293 |
* implemented for this class yet)! |
294 |
* |
295 |
* @param pBuffer destination buffer |
296 |
* @param SampleCount number of sample points to read |
297 |
* @returns number of successfully read sample points |
298 |
* @see SetPos() |
299 |
*/ |
300 |
unsigned long KSFSample::Read(void* pBuffer, unsigned long SampleCount) { |
301 |
RIFF::Chunk* smd1 = riff->GetSubChunk(CHUNK_ID_SMD1); |
302 |
|
303 |
unsigned long samplestoread = SampleCount, totalreadsamples = 0, readsamples; |
304 |
|
305 |
if (samplestoread) do { |
306 |
readsamples = smd1->Read(pBuffer, SampleCount, FrameSize()); // FIXME: channel inversion due to endian correction? |
307 |
samplestoread -= readsamples; |
308 |
totalreadsamples += readsamples; |
309 |
} while (readsamples && samplestoread); |
310 |
|
311 |
return totalreadsamples; |
312 |
} |
313 |
|
314 |
/** |
315 |
* Returns the size of one sample point of this sample in bytes. |
316 |
*/ |
317 |
int KSFSample::FrameSize() const { |
318 |
return BitDepth / 8 * Channels; |
319 |
} |
320 |
|
321 |
uint8_t KSFSample::CompressionID() const { |
322 |
return Attributes & 0x04; |
323 |
} |
324 |
|
325 |
bool KSFSample::IsCompressed() const { |
326 |
return Attributes & 0x10; |
327 |
} |
328 |
|
329 |
bool KSFSample::Use2ndStart() const { |
330 |
return !(Attributes & 0x20); |
331 |
} |
332 |
|
333 |
String KSFSample::FileName() const { |
334 |
return riff->GetFileName(); |
335 |
} |
336 |
|
337 |
// *************** KMPRegion *************** |
338 |
// * |
339 |
|
340 |
KMPRegion::KMPRegion(KMPInstrument* parent, RIFF::Chunk* rlp1) |
341 |
: parent(parent), rlp1(rlp1) |
342 |
{ |
343 |
OriginalKey = rlp1->ReadUint8(); |
344 |
Transpose = (OriginalKey >> 7) & 1; |
345 |
OriginalKey &= 0x7F; |
346 |
TopKey = rlp1->ReadUint8() & 0x7F; |
347 |
Tune = rlp1->ReadInt8(); |
348 |
Level = rlp1->ReadInt8(); |
349 |
Pan = rlp1->ReadUint8() & 0x7F; |
350 |
FilterCutoff = rlp1->ReadInt8(); |
351 |
SampleFileName = readText12(rlp1); |
352 |
} |
353 |
|
354 |
KMPRegion::~KMPRegion() { |
355 |
} |
356 |
|
357 |
String KMPRegion::FullSampleFileName() const { |
358 |
return removeFileTypeExtension(rlp1->GetFile()->GetFileName()) + |
359 |
PATH_SEP + SampleFileName; |
360 |
} |
361 |
|
362 |
KMPInstrument* KMPRegion::GetInstrument() const { |
363 |
return parent; |
364 |
} |
365 |
|
366 |
// *************** KMPInstrument *************** |
367 |
// * |
368 |
|
369 |
KMPInstrument::KMPInstrument(const String& filename) { |
370 |
riff = new RIFF::File( |
371 |
filename, CHUNK_ID_MSP1, RIFF::endian_big, RIFF::layout_flat |
372 |
); |
373 |
|
374 |
// read 'MSP1' chunk |
375 |
RIFF::Chunk* msp1 = riff->GetSubChunk(CHUNK_ID_MSP1); |
376 |
if (!msp1) |
377 |
throw Exception("Not a Korg instrument file ('MSP1' chunk not found)"); |
378 |
if (msp1->GetSize() < 18) |
379 |
throw Exception("Not a Korg instrument file ('MSP1' chunk size too small)"); |
380 |
Name16 = readText16(msp1); |
381 |
int nSamples = msp1->ReadUint8(); |
382 |
Attributes = msp1->ReadUint8(); |
383 |
|
384 |
// read optional 'NAME' chunk |
385 |
RIFF::Chunk* name = riff->GetSubChunk(CHUNK_ID_NAME); |
386 |
if (name) { |
387 |
Name24 = readText24(name); |
388 |
} |
389 |
|
390 |
// read 'RLP1' chunk ... |
391 |
RIFF::Chunk* rlp1 = riff->GetSubChunk(CHUNK_ID_RLP1); |
392 |
if (!rlp1) |
393 |
throw Exception("Not a Korg instrument file ('RLP1' chunk not found)"); |
394 |
if (rlp1->GetSize() < 18 * nSamples) |
395 |
throw Exception("Not a Korg instrument file ('RLP1' chunk size too small)"); |
396 |
for (int i = 0; i < nSamples; ++i) { |
397 |
KMPRegion* region = new KMPRegion(this, rlp1); |
398 |
regions.push_back(region); |
399 |
} |
400 |
} |
401 |
|
402 |
KMPInstrument::~KMPInstrument() { |
403 |
if (riff) delete riff; |
404 |
for (int i = 0; i < regions.size(); ++i) |
405 |
delete regions[i]; |
406 |
} |
407 |
|
408 |
KMPRegion* KMPInstrument::GetRegion(int index) { |
409 |
if (index < 0 || index >= regions.size()) |
410 |
return NULL; |
411 |
return regions[index]; |
412 |
} |
413 |
|
414 |
int KMPInstrument::GetRegionCount() const { |
415 |
return (int) regions.size(); |
416 |
} |
417 |
|
418 |
bool KMPInstrument::Use2ndStart() const { |
419 |
return !(Attributes & 1); |
420 |
} |
421 |
|
422 |
/** |
423 |
* Returns the long name (Name24) if it was stored in the file, otherwise |
424 |
* returns the short name (Name16). |
425 |
*/ |
426 |
String KMPInstrument::Name() const { |
427 |
return (!Name24.empty()) ? Name24 : Name16; |
428 |
} |
429 |
|
430 |
String KMPInstrument::FileName() const { |
431 |
return riff->GetFileName(); |
432 |
} |
433 |
|
434 |
// *************** Exception *************** |
435 |
// * |
436 |
|
437 |
Exception::Exception(String Message) : RIFF::Exception(Message) { |
438 |
} |
439 |
|
440 |
void Exception::PrintMessage() { |
441 |
std::cout << "Korg::Exception: " << Message << std::endl; |
442 |
} |
443 |
|
444 |
// *************** functions *************** |
445 |
// * |
446 |
|
447 |
/** |
448 |
* Returns the name of this C++ library. This is usually "libgig" of |
449 |
* course. This call is equivalent to RIFF::libraryName() and |
450 |
* gig::libraryName(). |
451 |
*/ |
452 |
String libraryName() { |
453 |
return PACKAGE; |
454 |
} |
455 |
|
456 |
/** |
457 |
* Returns version of this C++ library. This call is equivalent to |
458 |
* RIFF::libraryVersion() and gig::libraryVersion(). |
459 |
*/ |
460 |
String libraryVersion() { |
461 |
return VERSION; |
462 |
} |
463 |
|
464 |
} // namespace Korg |