/[svn]/libgig/trunk/src/Korg.cpp
ViewVC logotype

Annotation of /libgig/trunk/src/Korg.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3053 - (hide annotations) (download)
Wed Dec 14 18:55:08 2016 UTC (7 years, 4 months ago) by schoenebeck
File size: 17979 byte(s)
* Fixed various compiler warnings.
* Bumped version (4.0.0.svn11).

1 schoenebeck 2543 /***************************************************************************
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 schoenebeck 3053 int n = (int) ck->Read(buf, SZ, 1);
59 schoenebeck 2543 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 schoenebeck 3053 return (int) regions.size();
416 schoenebeck 2543 }
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

  ViewVC Help
Powered by ViewVC