Korg.cpp
/***************************************************************************
* *
* Copyright (C) 2014 - 2025 Christian Schoenebeck *
* <cuse@users.sourceforge.net> *
* *
* This library is part of libgig. *
* *
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this library; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
* MA 02111-1307 USA *
***************************************************************************/
#include "Korg.h"
#include <string.h> // for memset()
#if WORDS_BIGENDIAN
# define CHUNK_ID_MSP1 0x4d535031
# define CHUNK_ID_RLP1 0x524c5031
# define CHUNK_ID_SMP1 0x534d5031
# define CHUNK_ID_SMD1 0x534d4431
# define CHUNK_ID_NAME 0x4e414d45
#else // little endian
# define CHUNK_ID_MSP1 0x3150534d
# define CHUNK_ID_RLP1 0x31504c52
# define CHUNK_ID_SMP1 0x31504d53
# define CHUNK_ID_SMD1 0x31444d53
# define CHUNK_ID_NAME 0x454d414e
#endif // WORDS_BIGENDIAN
#define SMD1_CHUNK_HEADER_SZ 12
namespace Korg {
#if defined(WIN32)
static const String PATH_SEP = "\\";
#else
static const String PATH_SEP = "/";
#endif
// *************** Internal functions **************
// *
template<unsigned int SZ>
inline String readText(RIFF::Chunk* ck) {
char buf[SZ+1] = {};
int n = (int) ck->Read(buf, SZ, 1);
if (n != SZ)
throw Exception("Premature end while reading text field");
String s = buf;
return s;
}
inline String readText24(RIFF::Chunk* ck) {
return readText<24>(ck);
}
inline String readText16(RIFF::Chunk* ck) {
return readText<16>(ck);
}
inline String readText12(RIFF::Chunk* ck) {
return readText<12>(ck);
}
inline String removeFileTypeExtension(const String& filename) {
size_t pos = filename.find_last_of('.');
if (pos == String::npos) return filename;
return filename.substr(0, pos);
}
// *************** KSFSample ***************
// *
KSFSample::KSFSample(const String& filename) {
RAMCache.Size = 0;
RAMCache.pStart = NULL;
RAMCache.NullExtensionSize = 0;
riff = new RIFF::File(
filename, CHUNK_ID_SMP1, RIFF::endian_big, RIFF::layout_flat
);
// read 'SMP1' chunk
RIFF::Chunk* smp1 = riff->GetSubChunk(CHUNK_ID_SMP1);
if (!smp1)
throw Exception("Not a Korg sample file ('SMP1' chunk not found)");
if (smp1->GetSize() < 32)
throw Exception("Not a Korg sample file ('SMP1' chunk size too small)");
Name = readText16(smp1);
DefaultBank = smp1->ReadUint8();
Start = smp1->ReadUint8() << 16 | smp1->ReadUint8() << 8 | smp1->ReadUint8();
Start2 = smp1->ReadUint32();
LoopStart = smp1->ReadUint32();
LoopEnd = smp1->ReadUint32();
// read 'SMD1' chunk
RIFF::Chunk* smd1 = riff->GetSubChunk(CHUNK_ID_SMD1);
if (!smd1)
throw Exception("Not a Korg sample file ('SMD1' chunk not found)");
if (smd1->GetSize() < 12)
throw Exception("Not a Korg sample file ('SMD1' chunk size too small)");
SampleRate = smd1->ReadUint32();
Attributes = smd1->ReadUint8();
LoopTune = smd1->ReadInt8();
Channels = smd1->ReadUint8();
BitDepth = smd1->ReadUint8();
SamplePoints = smd1->ReadUint32();
}
KSFSample::~KSFSample() {
if (RAMCache.pStart) delete[] (int8_t*) RAMCache.pStart;
if (riff) delete riff;
}
buffer_t KSFSample::LoadSampleData() {
return LoadSampleDataWithNullSamplesExtension(this->SamplePoints, 0); // 0 amount of NullSamples
}
buffer_t KSFSample::LoadSampleData(unsigned long SampleCount) {
return LoadSampleDataWithNullSamplesExtension(SampleCount, 0); // 0 amount of NullSamples
}
buffer_t KSFSample::LoadSampleDataWithNullSamplesExtension(uint NullSamplesCount) {
return LoadSampleDataWithNullSamplesExtension(this->SamplePoints, NullSamplesCount);
}
buffer_t KSFSample::LoadSampleDataWithNullSamplesExtension(unsigned long SampleCount, uint NullSamplesCount) {
if (SampleCount > this->SamplePoints) SampleCount = this->SamplePoints;
if (RAMCache.pStart) delete[] (int8_t*) RAMCache.pStart;
unsigned long allocationsize = (SampleCount + NullSamplesCount) * FrameSize();
SetPos(0); // reset read position to beginning of sample
RAMCache.pStart = new int8_t[allocationsize];
RAMCache.Size = Read(RAMCache.pStart, SampleCount) * FrameSize();
RAMCache.NullExtensionSize = allocationsize - RAMCache.Size;
// fill the remaining buffer space with silence samples
memset((int8_t*)RAMCache.pStart + RAMCache.Size, 0, RAMCache.NullExtensionSize);
return GetCache();
}
buffer_t KSFSample::GetCache() const {
// return a copy of the buffer_t structure
buffer_t result;
result.Size = this->RAMCache.Size;
result.pStart = this->RAMCache.pStart;
result.NullExtensionSize = this->RAMCache.NullExtensionSize;
return result;
}
void KSFSample::ReleaseSampleData() {
if (RAMCache.pStart) delete[] (int8_t*) RAMCache.pStart;
RAMCache.pStart = NULL;
RAMCache.Size = 0;
RAMCache.NullExtensionSize = 0;
}
unsigned long KSFSample::SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence) {
unsigned long samplePos = GetPos();
switch (Whence) {
case RIFF::stream_curpos:
samplePos += SampleCount;
break;
case RIFF::stream_end:
samplePos = this->SamplePoints - 1 - SampleCount;
break;
case RIFF::stream_backward:
samplePos -= SampleCount;
break;
case RIFF::stream_start:
default:
samplePos = SampleCount;
break;
}
if (samplePos > this->SamplePoints) samplePos = this->SamplePoints;
unsigned long bytes = samplePos * FrameSize();
RIFF::Chunk* smd1 = riff->GetSubChunk(CHUNK_ID_SMD1);
unsigned long result = smd1->SetPos(SMD1_CHUNK_HEADER_SZ + bytes);
return (result - SMD1_CHUNK_HEADER_SZ) / FrameSize();
}
unsigned long KSFSample::GetPos() const {
RIFF::Chunk* smd1 = riff->GetSubChunk(CHUNK_ID_SMD1);
return (smd1->GetPos() - SMD1_CHUNK_HEADER_SZ) / FrameSize();
}
unsigned long KSFSample::Read(void* pBuffer, unsigned long SampleCount) {
RIFF::Chunk* smd1 = riff->GetSubChunk(CHUNK_ID_SMD1);
unsigned long samplestoread = SampleCount, totalreadsamples = 0, readsamples;
if (samplestoread) do {
readsamples = smd1->Read(pBuffer, SampleCount, FrameSize()); // FIXME: channel inversion due to endian correction?
samplestoread -= readsamples;
totalreadsamples += readsamples;
} while (readsamples && samplestoread);
return totalreadsamples;
}
int KSFSample::FrameSize() const {
return BitDepth / 8 * Channels;
}
uint8_t KSFSample::CompressionID() const {
return Attributes & 0x04;
}
bool KSFSample::IsCompressed() const {
return Attributes & 0x10;
}
bool KSFSample::Use2ndStart() const {
return !(Attributes & 0x20);
}
String KSFSample::FileName() const {
return riff->GetFileName();
}
// *************** KMPRegion ***************
// *
KMPRegion::KMPRegion(KMPInstrument* parent, RIFF::Chunk* rlp1)
: parent(parent), rlp1(rlp1)
{
OriginalKey = rlp1->ReadUint8();
Transpose = (OriginalKey >> 7) & 1;
OriginalKey &= 0x7F;
TopKey = rlp1->ReadUint8() & 0x7F;
Tune = rlp1->ReadInt8();
Level = rlp1->ReadInt8();
Pan = rlp1->ReadUint8() & 0x7F;
FilterCutoff = rlp1->ReadInt8();
SampleFileName = readText12(rlp1);
}
KMPRegion::~KMPRegion() {
}
String KMPRegion::FullSampleFileName() const {
return removeFileTypeExtension(rlp1->GetFile()->GetFileName()) +
PATH_SEP + SampleFileName;
}
KMPInstrument* KMPRegion::GetInstrument() const {
return parent;
}
// *************** KMPInstrument ***************
// *
KMPInstrument::KMPInstrument(const String& filename) {
riff = new RIFF::File(
filename, CHUNK_ID_MSP1, RIFF::endian_big, RIFF::layout_flat
);
// read 'MSP1' chunk
RIFF::Chunk* msp1 = riff->GetSubChunk(CHUNK_ID_MSP1);
if (!msp1)
throw Exception("Not a Korg instrument file ('MSP1' chunk not found)");
if (msp1->GetSize() < 18)
throw Exception("Not a Korg instrument file ('MSP1' chunk size too small)");
Name16 = readText16(msp1);
int nSamples = msp1->ReadUint8();
Attributes = msp1->ReadUint8();
// read optional 'NAME' chunk
RIFF::Chunk* name = riff->GetSubChunk(CHUNK_ID_NAME);
if (name) {
Name24 = readText24(name);
}
// read 'RLP1' chunk ...
RIFF::Chunk* rlp1 = riff->GetSubChunk(CHUNK_ID_RLP1);
if (!rlp1)
throw Exception("Not a Korg instrument file ('RLP1' chunk not found)");
if (rlp1->GetSize() < 18 * nSamples)
throw Exception("Not a Korg instrument file ('RLP1' chunk size too small)");
for (int i = 0; i < nSamples; ++i) {
KMPRegion* region = new KMPRegion(this, rlp1);
regions.push_back(region);
}
}
KMPInstrument::~KMPInstrument() {
if (riff) delete riff;
for (int i = 0; i < regions.size(); ++i)
delete regions[i];
}
KMPRegion* KMPInstrument::GetRegion(int index) {
if (index < 0 || index >= regions.size())
return NULL;
return regions[index];
}
int KMPInstrument::GetRegionCount() const {
return (int) regions.size();
}
bool KMPInstrument::Use2ndStart() const {
return !(Attributes & 1);
}
String KMPInstrument::Name() const {
return (!Name24.empty()) ? Name24 : Name16;
}
String KMPInstrument::FileName() const {
return riff->GetFileName();
}
// *************** Exception ***************
// *
Exception::Exception(String Message) : RIFF::Exception(Message) {
}
void Exception::PrintMessage() const {
std::cout << "Korg::Exception: " << Message << std::endl;
}
// *************** functions ***************
// *
String libraryName() {
return PACKAGE;
}
String libraryVersion() {
return VERSION;
}
} // namespace Korg
Korg::Exception
Korg format specific exception.
Definition Korg.h:199
Korg::KMPInstrument
.KMP multi sample file
Definition Korg.h:171
Korg::KMPInstrument::Name16
String Name16
Human readable name of the instrument for display purposes (not the file name). Since this name is al...
Definition Korg.h:173
Korg::KMPInstrument::Attributes
uint8_t Attributes
Bit field of attribute flags (ATM only bit 0 is used, better call Use2ndStart() instead of accessing ...
Definition Korg.h:175
Korg::KMPInstrument::Name24
String Name24
Longer Human readable name (24 characters) of the instrument for display purposes (not the file name)...
Definition Korg.h:174
Korg::KMPInstrument::Name
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
Korg::KMPRegion
Region of a .KMP multi sample file.
Definition Korg.h:133
Korg::KMPRegion::SampleFileName
String SampleFileName
Base file name of sample file (12 bytes). Call FullSampleFileName() for getting the file name with pa...
Definition Korg.h:142
Korg::KSFSample::BitDepth
uint8_t BitDepth
i.e. 8 or 16
Definition Korg.h:102
Korg::KSFSample::Channels
uint8_t Channels
Number of audio channels (seems to be always 1, thus Mono for all Korg sound files ATM).
Definition Korg.h:101
Korg::KSFSample::GetCache
buffer_t GetCache() const
Returns current cached sample points.
Definition Korg.cpp:219
Korg::KSFSample::Read
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::KSFSample::LoopTune
int8_t LoopTune
-99..+99
Definition Korg.h:100
Korg::KSFSample::DefaultBank
uint8_t DefaultBank
0..3
Definition Korg.h:93
Korg::KSFSample::SetPos
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
Korg::KSFSample::SamplePoints
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
Korg::KSFSample::Name
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
Korg::KSFSample::LoadSampleData
buffer_t LoadSampleData()
Loads the whole sample wave into RAM.
Definition Korg.cpp:139
Korg::KSFSample::FrameSize
int FrameSize() const
Returns the size of one sample point of this sample in bytes.
Definition Korg.cpp:317
Korg::KSFSample::SampleRate
uint32_t SampleRate
i.e. 44100
Definition Korg.h:98
Korg::KSFSample::LoadSampleDataWithNullSamplesExtension
buffer_t LoadSampleDataWithNullSamplesExtension(uint NullSamplesCount)
Loads the whole sample wave into RAM.
Definition Korg.cpp:174
Korg::KSFSample::ReleaseSampleData
void ReleaseSampleData()
Frees the cached sample from RAM if loaded with LoadSampleData() previously.
Definition Korg.cpp:234
Korg::KSFSample::Attributes
uint8_t Attributes
Bit field of flags, better call IsCompressed(), CompressionID() and Use2ndStart() instead of accessin...
Definition Korg.h:99
Korg::KSFSample::GetPos
unsigned long GetPos() const
Returns the current position in the sample (in sample points).
Definition Korg.cpp:279
RIFF::Chunk
Ordinary RIFF Chunk.
Definition RIFF.h:186
RIFF::Chunk::ReadInt8
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:617
RIFF::Chunk::SetPos
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:365
RIFF::Chunk::ReadUint32
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:812
RIFF::Chunk::GetPos
file_offset_t GetPos() const
Current read/write position within the chunk data body (starting with 0).
Definition RIFF.cpp:335
RIFF::Chunk::Read
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:465
RIFF::Chunk::ReadUint8
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:656
RIFF::Chunk::GetSize
file_offset_t GetSize() const
Chunk size in bytes (without header, thus the chunk data body).
Definition RIFF.h:193
RIFF::File
RIFF File.
Definition RIFF.h:314
RIFF::List::GetSubChunk
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition RIFF.cpp:1273
Korg
KORG sound format specific classes and definitions.
Definition Korg.h:74
Korg::libraryVersion
String libraryVersion()
Returns version of this C++ library.
Definition Korg.cpp:460
Korg::libraryName
String libraryName()
Returns the name of this C++ library.
Definition Korg.cpp:452
Korg::readText16
String readText16(RIFF::Chunk *ck)
Read 16 bytes of ASCII text from given chunk and return it as String.
Definition Korg.cpp:71
Korg::readText24
String readText24(RIFF::Chunk *ck)
Read 24 bytes of ASCII text from given chunk and return it as String.
Definition Korg.cpp:66
Korg::readText12
String readText12(RIFF::Chunk *ck)
Read 12 bytes of ASCII text from given chunk and return it as String.
Definition Korg.cpp:76
Korg::removeFileTypeExtension
String removeFileTypeExtension(const String &filename)
For example passing "FOO.KMP" will return "FOO".
Definition Korg.cpp:81
RIFF
RIFF specific classes and definitions.
Definition RIFF.h:101
RIFF::stream_whence_t
stream_whence_t
File stream position dependent to these relations.
Definition RIFF.h:128
RIFF::layout_flat
@ layout_flat
Not a "real" RIFF file: First chunk in file is an ordinary data chunk, not a List chunk,...
Definition RIFF.h:145
gig::buffer_t::NullExtensionSize
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
gig::buffer_t::pStart
void * pStart
Points to the beginning of the buffer.
Definition gig.h:115
gig::buffer_t::Size
file_offset_t Size
Size of the actual data in the buffer in bytes.
Definition gig.h:116