libgig 4.4.1
Korg.cpp
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
44namespace 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
66 inline String readText24(RIFF::Chunk* ck) {
67 return readText<24>(ck);
68 }
69
71 inline String readText16(RIFF::Chunk* ck) {
72 return readText<16>(ck);
73 }
74
76 inline String readText12(RIFF::Chunk* ck) {
77 return readText<12>(ck);
78 }
79
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
140 return LoadSampleDataWithNullSamplesExtension(this->SamplePoints, 0); // 0 amount of NullSamples
141 }
142
153 buffer_t KSFSample::LoadSampleData(unsigned long SampleCount) {
154 return LoadSampleDataWithNullSamplesExtension(SampleCount, 0); // 0 amount of NullSamples
155 }
156
177
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
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
235 if (RAMCache.pStart) delete[] (int8_t*) RAMCache.pStart;
236 RAMCache.pStart = NULL;
237 RAMCache.Size = 0;
238 RAMCache.NullExtensionSize = 0;
239 }
240
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
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
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
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
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
452 String libraryName() {
453 return PACKAGE;
454 }
455
460 String libraryVersion() {
461 return VERSION;
462 }
463
464} // namespace Korg
String Name16
Human readable name of the instrument for display purposes (not the file name). Since this name is al...
Definition Korg.h:173
uint8_t Attributes
Bit field of attribute flags (ATM only bit 0 is used, better call Use2ndStart() instead of accessing ...
Definition Korg.h:175
String Name24
Longer Human readable name (24 characters) of the instrument for display purposes (not the file name)...
Definition Korg.h:174
String Name() const
Returns the long name (Name24) if it was stored in the file, otherwise returns the short name (Name16...
Definition Korg.cpp:426
String SampleFileName
Base file name of sample file (12 bytes). Call FullSampleFileName() for getting the file name with pa...
Definition Korg.h:142
uint8_t BitDepth
i.e. 8 or 16
Definition Korg.h:102
uint8_t Channels
Number of audio channels (seems to be always 1, thus Mono for all Korg sound files ATM).
Definition Korg.h:101
buffer_t GetCache() const
Returns current cached sample points.
Definition Korg.cpp:219
unsigned long Read(void *pBuffer, unsigned long SampleCount)
Reads SampleCount number of sample points from the current position into the buffer pointed by pBuffe...
Definition Korg.cpp:300
int8_t LoopTune
-99..+99
Definition Korg.h:100
uint8_t DefaultBank
0..3
Definition Korg.h:93
unsigned long SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence=RIFF::stream_start)
Sets the position within the sample (in sample points, not in bytes).
Definition Korg.cpp:252
uint32_t SamplePoints
Currently the library expects all Korg samples to be Mono, thus the value here might be incorrect in ...
Definition Korg.h:103
String Name
Sample name for drums (since this name is always stored with 16 bytes, this name must never be longer...
Definition Korg.h:92
buffer_t LoadSampleData()
Loads the whole sample wave into RAM.
Definition Korg.cpp:139
int FrameSize() const
Returns the size of one sample point of this sample in bytes.
Definition Korg.cpp:317
uint32_t SampleRate
i.e. 44100
Definition Korg.h:98
buffer_t LoadSampleDataWithNullSamplesExtension(uint NullSamplesCount)
Loads the whole sample wave into RAM.
Definition Korg.cpp:174
void ReleaseSampleData()
Frees the cached sample from RAM if loaded with LoadSampleData() previously.
Definition Korg.cpp:234
uint8_t Attributes
Bit field of flags, better call IsCompressed(), CompressionID() and Use2ndStart() instead of accessin...
Definition Korg.h:99
unsigned long GetPos() const
Returns the current position in the sample (in sample points).
Definition Korg.cpp:279
Ordinary RIFF Chunk.
Definition RIFF.h:179
File * GetFile() const
Returns pointer to the chunk's File object.
Definition RIFF.h:184
file_offset_t ReadInt8(int8_t *pData, file_offset_t WordCount=1)
Reads WordCount number of 8 Bit signed integer words and copies it into the buffer pointed by pData.
Definition RIFF.cpp:593
file_offset_t SetPos(file_offset_t Where, stream_whence_t Whence=stream_start)
Sets the position within the chunk body, thus within the data portion of the chunk (in bytes).
Definition RIFF.cpp:341
file_offset_t ReadUint32(uint32_t *pData, file_offset_t WordCount=1)
Reads WordCount number of 32 Bit unsigned integer words and copies it into the buffer pointed by pDat...
Definition RIFF.cpp:788
file_offset_t GetPos() const
Current read/write position within the chunk data body (starting with 0).
Definition RIFF.cpp:311
file_offset_t Read(void *pData, file_offset_t WordCount, file_offset_t WordSize)
Reads WordCount number of data words with given WordSize and copies it into a buffer pointed by pData...
Definition RIFF.cpp:441
file_offset_t ReadUint8(uint8_t *pData, file_offset_t WordCount=1)
Reads WordCount number of 8 Bit unsigned integer words and copies it into the buffer pointed by pData...
Definition RIFF.cpp:632
file_offset_t GetSize() const
Chunk size in bytes (without header, thus the chunk data body)
Definition RIFF.h:186
RIFF File.
Definition RIFF.h:313
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition RIFF.cpp:1246
KORG sound format specific classes and definitions.
Definition Korg.h:74
String libraryVersion()
Returns version of this C++ library.
Definition Korg.cpp:460
String libraryName()
Returns the name of this C++ library.
Definition Korg.cpp:452
String readText16(RIFF::Chunk *ck)
Read 16 bytes of ASCII text from given chunk and return it as String.
Definition Korg.cpp:71
String readText24(RIFF::Chunk *ck)
Read 24 bytes of ASCII text from given chunk and return it as String.
Definition Korg.cpp:66
String readText12(RIFF::Chunk *ck)
Read 12 bytes of ASCII text from given chunk and return it as String.
Definition Korg.cpp:76
String removeFileTypeExtension(const String &filename)
For example passing "FOO.KMP" will return "FOO".
Definition Korg.cpp:81
RIFF specific classes and definitions.
Definition RIFF.h:97
stream_whence_t
File stream position dependent to these relations.
Definition RIFF.h:124
@ layout_flat
Not a "real" RIFF file: First chunk in file is an ordinary data chunk, not a List chunk,...
Definition RIFF.h:141
Pointer address and size of a buffer.
Definition gig.h:114
file_offset_t NullExtensionSize
The buffer might be bigger than the actual data, if that's the case that unused space at the end of t...
Definition gig.h:117
void * pStart
Points to the beginning of the buffer.
Definition gig.h:115
file_offset_t Size
Size of the actual data in the buffer in bytes.
Definition gig.h:116