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 
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 
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 
175  return LoadSampleDataWithNullSamplesExtension(this->SamplePoints, NullSamplesCount);
176  }
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 
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 
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
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
uint8_t DefaultBank
0..3
Definition: Korg.h:93
stream_whence_t
File stream position dependent to these relations.
Definition: RIFF.h:124
String readText16(RIFF::Chunk *ck)
Read 16 bytes of ASCII text from given chunk and return it as String.
Definition: Korg.cpp:71
String readText12(RIFF::Chunk *ck)
Read 12 bytes of ASCII text from given chunk and return it as String.
Definition: Korg.cpp:76
file_offset_t GetSize() const
Chunk size in bytes (without header, thus the chunk data body)
Definition: RIFF.h:186
String removeFileTypeExtension(const String &filename)
For example passing "FOO.KMP" will return "FOO".
Definition: Korg.cpp:81
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
file_offset_t Size
Size of the actual data in the buffer in bytes.
Definition: gig.h:116
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
uint8_t Attributes
Bit field of flags, better call IsCompressed(), CompressionID() and Use2ndStart() instead of accessin...
Definition: Korg.h:99
String readText24(RIFF::Chunk *ck)
Read 24 bytes of ASCII text from given chunk and return it as String.
Definition: Korg.cpp:66
uint8_t Channels
Number of audio channels (seems to be always 1, thus Mono for all Korg sound files ATM)...
Definition: Korg.h:101
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 LoadSampleDataWithNullSamplesExtension(uint NullSamplesCount)
Loads the whole sample wave into RAM.
Definition: Korg.cpp:174
.KMP multi sample file
Definition: Korg.h:171
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
Pointer address and size of a buffer.
Definition: gig.h:114
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
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
KORG sound format specific classes and definitions.
Definition: Korg.h:74
String libraryVersion()
Returns version of this C++ library.
Definition: Korg.cpp:460
void * pStart
Points to the beginning of the buffer.
Definition: gig.h:115
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
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition: RIFF.cpp:1246
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
void ReleaseSampleData()
Frees the cached sample from RAM if loaded with LoadSampleData() previously.
Definition: Korg.cpp:234
Region of a .KMP multi sample file.
Definition: Korg.h:133
Not a "real" RIFF file: First chunk in file is an ordinary data chunk, not a List chunk...
Definition: RIFF.h:141
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
int FrameSize() const
Returns the size of one sample point of this sample in bytes.
Definition: Korg.cpp:317
file_offset_t NullExtensionSize
The buffer might be bigger than the actual data, if that&#39;s the case that unused space at the end of t...
Definition: gig.h:117
int8_t LoopTune
-99..+99
Definition: Korg.h:100
String libraryName()
Returns the name of this C++ library.
Definition: Korg.cpp:452
uint32_t SampleRate
i.e. 44100
Definition: Korg.h:98
RIFF File.
Definition: RIFF.h:313
uint8_t BitDepth
i.e. 8 or 16
Definition: Korg.h:102
File * GetFile() const
Returns pointer to the chunk&#39;s File object.
Definition: RIFF.h:184
Will be thrown whenever an error occurs while handling a RIFF file.
Definition: RIFF.h:391
buffer_t LoadSampleData()
Loads the whole sample wave into RAM.
Definition: Korg.cpp:139
file_offset_t GetPos() const
Current read/write position within the chunk data body (starting with 0).
Definition: RIFF.cpp:311
Korg format specific exception.
Definition: Korg.h:199
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
buffer_t GetCache() const
Returns current cached sample points.
Definition: Korg.cpp:219