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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1335 - (hide annotations) (download)
Sun Sep 9 21:22:58 2007 UTC (13 years, 2 months ago) by schoenebeck
File size: 66189 byte(s)
* added virtual method SetKeyRange() to the gig and DLS Region classes,
  which automatically take care that the "resized" Region is at the
  correct position and that the lookup table for
  gig::Instrument::GetRegion() is updated (moved code from gigedit)
* MoveRegion() method of DLS::Region class is now private
* bugfix: gig::Instrument::UpdateRegionKeyTable() did not reset unused
  areas

1 schoenebeck 2 /***************************************************************************
2     * *
3 schoenebeck 933 * libgig - C++ cross-platform Gigasampler format file access library *
4 schoenebeck 2 * *
5 schoenebeck 1050 * Copyright (C) 2003-2007 by Christian Schoenebeck *
6 schoenebeck 384 * <cuse@users.sourceforge.net> *
7 schoenebeck 2 * *
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 "DLS.h"
25    
26 schoenebeck 800 #include <time.h>
27    
28 persson 1209 #ifdef __APPLE__
29     #include <CoreFoundation/CFUUID.h>
30     #elif defined(HAVE_UUID_UUID_H)
31     #include <uuid/uuid.h>
32     #endif
33    
34 schoenebeck 800 #include "helper.h"
35    
36     // macros to decode connection transforms
37     #define CONN_TRANSFORM_SRC(x) ((x >> 10) & 0x000F)
38     #define CONN_TRANSFORM_CTL(x) ((x >> 4) & 0x000F)
39     #define CONN_TRANSFORM_DST(x) (x & 0x000F)
40     #define CONN_TRANSFORM_BIPOLAR_SRC(x) (x & 0x4000)
41     #define CONN_TRANSFORM_BIPOLAR_CTL(x) (x & 0x0100)
42     #define CONN_TRANSFORM_INVERT_SRC(x) (x & 0x8000)
43     #define CONN_TRANSFORM_INVERT_CTL(x) (x & 0x0200)
44    
45     // macros to encode connection transforms
46     #define CONN_TRANSFORM_SRC_ENCODE(x) ((x & 0x000F) << 10)
47     #define CONN_TRANSFORM_CTL_ENCODE(x) ((x & 0x000F) << 4)
48     #define CONN_TRANSFORM_DST_ENCODE(x) (x & 0x000F)
49     #define CONN_TRANSFORM_BIPOLAR_SRC_ENCODE(x) ((x) ? 0x4000 : 0)
50     #define CONN_TRANSFORM_BIPOLAR_CTL_ENCODE(x) ((x) ? 0x0100 : 0)
51     #define CONN_TRANSFORM_INVERT_SRC_ENCODE(x) ((x) ? 0x8000 : 0)
52     #define CONN_TRANSFORM_INVERT_CTL_ENCODE(x) ((x) ? 0x0200 : 0)
53    
54 persson 918 #define DRUM_TYPE_MASK 0x80000000
55 schoenebeck 800
56     #define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001
57    
58     #define F_WAVELINK_PHASE_MASTER 0x0001
59     #define F_WAVELINK_MULTICHANNEL 0x0002
60    
61     #define F_WSMP_NO_TRUNCATION 0x0001
62     #define F_WSMP_NO_COMPRESSION 0x0002
63    
64     #define MIDI_BANK_COARSE(x) ((x & 0x00007F00) >> 8) // CC0
65     #define MIDI_BANK_FINE(x) (x & 0x0000007F) // CC32
66     #define MIDI_BANK_MERGE(coarse, fine) ((((uint16_t) coarse) << 7) | fine) // CC0 + CC32
67     #define MIDI_BANK_ENCODE(coarse, fine) (((coarse & 0x0000007F) << 8) | (fine & 0x0000007F))
68    
69 schoenebeck 2 namespace DLS {
70    
71     // *************** Connection ***************
72     // *
73    
74     void Connection::Init(conn_block_t* Header) {
75     Source = (conn_src_t) Header->source;
76     Control = (conn_src_t) Header->control;
77     Destination = (conn_dst_t) Header->destination;
78     Scale = Header->scale;
79     SourceTransform = (conn_trn_t) CONN_TRANSFORM_SRC(Header->transform);
80     ControlTransform = (conn_trn_t) CONN_TRANSFORM_CTL(Header->transform);
81     DestinationTransform = (conn_trn_t) CONN_TRANSFORM_DST(Header->transform);
82     SourceInvert = CONN_TRANSFORM_INVERT_SRC(Header->transform);
83     SourceBipolar = CONN_TRANSFORM_BIPOLAR_SRC(Header->transform);
84     ControlInvert = CONN_TRANSFORM_INVERT_CTL(Header->transform);
85     ControlBipolar = CONN_TRANSFORM_BIPOLAR_CTL(Header->transform);
86     }
87    
88 schoenebeck 800 Connection::conn_block_t Connection::ToConnBlock() {
89     conn_block_t c;
90     c.source = Source;
91     c.control = Control;
92     c.destination = Destination;
93     c.scale = Scale;
94     c.transform = CONN_TRANSFORM_SRC_ENCODE(SourceTransform) |
95     CONN_TRANSFORM_CTL_ENCODE(ControlTransform) |
96     CONN_TRANSFORM_DST_ENCODE(DestinationTransform) |
97     CONN_TRANSFORM_INVERT_SRC_ENCODE(SourceInvert) |
98     CONN_TRANSFORM_BIPOLAR_SRC_ENCODE(SourceBipolar) |
99     CONN_TRANSFORM_INVERT_CTL_ENCODE(ControlInvert) |
100     CONN_TRANSFORM_BIPOLAR_CTL_ENCODE(ControlBipolar);
101     return c;
102     }
103 schoenebeck 2
104    
105 schoenebeck 800
106 schoenebeck 2 // *************** Articulation ***************
107     // *
108    
109 schoenebeck 800 /** @brief Constructor.
110     *
111     * Expects an 'artl' or 'art2' chunk to be given where the articulation
112     * connections will be read from.
113     *
114     * @param artl - pointer to an 'artl' or 'art2' chunk
115     * @throws Exception if no 'artl' or 'art2' chunk was given
116     */
117     Articulation::Articulation(RIFF::Chunk* artl) {
118     pArticulationCk = artl;
119     if (artl->GetChunkID() != CHUNK_ID_ART2 &&
120     artl->GetChunkID() != CHUNK_ID_ARTL) {
121     throw DLS::Exception("<artl-ck> or <art2-ck> chunk expected");
122 schoenebeck 2 }
123 schoenebeck 800 HeaderSize = artl->ReadUint32();
124     Connections = artl->ReadUint32();
125     artl->SetPos(HeaderSize);
126 schoenebeck 2
127     pConnections = new Connection[Connections];
128     Connection::conn_block_t connblock;
129 schoenebeck 800 for (uint32_t i = 0; i < Connections; i++) {
130     artl->Read(&connblock.source, 1, 2);
131     artl->Read(&connblock.control, 1, 2);
132     artl->Read(&connblock.destination, 1, 2);
133     artl->Read(&connblock.transform, 1, 2);
134     artl->Read(&connblock.scale, 1, 4);
135 schoenebeck 2 pConnections[i].Init(&connblock);
136     }
137     }
138    
139     Articulation::~Articulation() {
140     if (pConnections) delete[] pConnections;
141     }
142    
143 schoenebeck 800 /**
144     * Apply articulation connections to the respective RIFF chunks. You
145     * have to call File::Save() to make changes persistent.
146     */
147     void Articulation::UpdateChunks() {
148     const int iEntrySize = 12; // 12 bytes per connection block
149     pArticulationCk->Resize(HeaderSize + Connections * iEntrySize);
150     uint8_t* pData = (uint8_t*) pArticulationCk->LoadChunkData();
151 persson 1179 store16(&pData[0], HeaderSize);
152     store16(&pData[2], Connections);
153 schoenebeck 800 for (uint32_t i = 0; i < Connections; i++) {
154     Connection::conn_block_t c = pConnections[i].ToConnBlock();
155 persson 1179 store16(&pData[HeaderSize + i * iEntrySize], c.source);
156     store16(&pData[HeaderSize + i * iEntrySize + 2], c.control);
157     store16(&pData[HeaderSize + i * iEntrySize + 4], c.destination);
158     store16(&pData[HeaderSize + i * iEntrySize + 6], c.transform);
159     store32(&pData[HeaderSize + i * iEntrySize + 8], c.scale);
160 schoenebeck 800 }
161     }
162 schoenebeck 2
163    
164 schoenebeck 800
165 schoenebeck 2 // *************** Articulator ***************
166     // *
167    
168     Articulator::Articulator(RIFF::List* ParentList) {
169     pParentList = ParentList;
170     pArticulations = NULL;
171     }
172    
173     Articulation* Articulator::GetFirstArticulation() {
174     if (!pArticulations) LoadArticulations();
175     if (!pArticulations) return NULL;
176     ArticulationsIterator = pArticulations->begin();
177     return (ArticulationsIterator != pArticulations->end()) ? *ArticulationsIterator : NULL;
178     }
179    
180     Articulation* Articulator::GetNextArticulation() {
181     if (!pArticulations) return NULL;
182     ArticulationsIterator++;
183     return (ArticulationsIterator != pArticulations->end()) ? *ArticulationsIterator : NULL;
184     }
185    
186     void Articulator::LoadArticulations() {
187     // prefer articulation level 2
188     RIFF::List* lart = pParentList->GetSubList(LIST_TYPE_LAR2);
189     if (!lart) lart = pParentList->GetSubList(LIST_TYPE_LART);
190     if (lart) {
191 schoenebeck 800 uint32_t artCkType = (lart->GetListType() == LIST_TYPE_LAR2) ? CHUNK_ID_ART2
192     : CHUNK_ID_ARTL;
193     RIFF::Chunk* art = lart->GetFirstSubChunk();
194 schoenebeck 2 while (art) {
195 schoenebeck 800 if (art->GetChunkID() == artCkType) {
196 schoenebeck 2 if (!pArticulations) pArticulations = new ArticulationList;
197     pArticulations->push_back(new Articulation(art));
198     }
199 schoenebeck 800 art = lart->GetNextSubChunk();
200 schoenebeck 2 }
201     }
202     }
203    
204     Articulator::~Articulator() {
205     if (pArticulations) {
206     ArticulationList::iterator iter = pArticulations->begin();
207     ArticulationList::iterator end = pArticulations->end();
208     while (iter != end) {
209     delete *iter;
210     iter++;
211     }
212     delete pArticulations;
213     }
214     }
215    
216 schoenebeck 800 /**
217     * Apply all articulations to the respective RIFF chunks. You have to
218     * call File::Save() to make changes persistent.
219     */
220     void Articulator::UpdateChunks() {
221 schoenebeck 804 if (pArticulations) {
222     ArticulationList::iterator iter = pArticulations->begin();
223     ArticulationList::iterator end = pArticulations->end();
224     for (; iter != end; ++iter) {
225     (*iter)->UpdateChunks();
226     }
227 schoenebeck 800 }
228     }
229 schoenebeck 2
230    
231 schoenebeck 800
232 schoenebeck 2 // *************** Info ***************
233     // *
234    
235 schoenebeck 800 /** @brief Constructor.
236     *
237 schoenebeck 929 * Initializes the info strings with values provided by an INFO list chunk.
238 schoenebeck 800 *
239 schoenebeck 929 * @param list - pointer to a list chunk which contains an INFO list chunk
240 schoenebeck 800 */
241 schoenebeck 2 Info::Info(RIFF::List* list) {
242 persson 1180 FixedStringLengths = NULL;
243 schoenebeck 800 pResourceListChunk = list;
244 schoenebeck 2 if (list) {
245     RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO);
246     if (lstINFO) {
247     LoadString(CHUNK_ID_INAM, lstINFO, Name);
248     LoadString(CHUNK_ID_IARL, lstINFO, ArchivalLocation);
249     LoadString(CHUNK_ID_ICRD, lstINFO, CreationDate);
250     LoadString(CHUNK_ID_ICMT, lstINFO, Comments);
251     LoadString(CHUNK_ID_IPRD, lstINFO, Product);
252     LoadString(CHUNK_ID_ICOP, lstINFO, Copyright);
253     LoadString(CHUNK_ID_IART, lstINFO, Artists);
254     LoadString(CHUNK_ID_IGNR, lstINFO, Genre);
255     LoadString(CHUNK_ID_IKEY, lstINFO, Keywords);
256     LoadString(CHUNK_ID_IENG, lstINFO, Engineer);
257     LoadString(CHUNK_ID_ITCH, lstINFO, Technician);
258     LoadString(CHUNK_ID_ISFT, lstINFO, Software);
259     LoadString(CHUNK_ID_IMED, lstINFO, Medium);
260     LoadString(CHUNK_ID_ISRC, lstINFO, Source);
261     LoadString(CHUNK_ID_ISRF, lstINFO, SourceForm);
262     LoadString(CHUNK_ID_ICMS, lstINFO, Commissioned);
263 persson 928 LoadString(CHUNK_ID_ISBJ, lstINFO, Subject);
264 schoenebeck 2 }
265     }
266     }
267    
268 schoenebeck 823 Info::~Info() {
269     }
270    
271 schoenebeck 800 /** @brief Load given INFO field.
272     *
273     * Load INFO field from INFO chunk with chunk ID \a ChunkID from INFO
274     * list chunk \a lstINFO and save value to \a s.
275     */
276     void Info::LoadString(uint32_t ChunkID, RIFF::List* lstINFO, String& s) {
277     RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
278 schoenebeck 929 ::LoadString(ck, s); // function from helper.h
279 schoenebeck 800 }
280 schoenebeck 2
281 schoenebeck 800 /** @brief Apply given INFO field to the respective chunk.
282     *
283     * Apply given info value to info chunk with ID \a ChunkID, which is a
284     * subchunk of INFO list chunk \a lstINFO. If the given chunk already
285 schoenebeck 804 * exists, value \a s will be applied. Otherwise if it doesn't exist yet
286     * and either \a s or \a sDefault is not an empty string, such a chunk
287     * will be created and either \a s or \a sDefault will be applied
288     * (depending on which one is not an empty string, if both are not an
289     * empty string \a s will be preferred).
290 schoenebeck 800 *
291     * @param ChunkID - 32 bit RIFF chunk ID of INFO subchunk
292     * @param lstINFO - parent (INFO) RIFF list chunk
293     * @param s - current value of info field
294     * @param sDefault - default value
295     */
296 persson 1180 void Info::SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault) {
297     int size = 0;
298     if (FixedStringLengths) {
299     for (int i = 0 ; FixedStringLengths[i].length ; i++) {
300     if (FixedStringLengths[i].chunkId == ChunkID) {
301     size = FixedStringLengths[i].length;
302 persson 1209 break;
303 persson 1180 }
304     }
305     }
306 schoenebeck 800 RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
307 persson 1180 ::SaveString(ChunkID, ck, lstINFO, s, sDefault, size != 0, size); // function from helper.h
308 schoenebeck 800 }
309 schoenebeck 2
310 schoenebeck 800 /** @brief Update chunks with current info values.
311     *
312     * Apply current INFO field values to the respective INFO chunks. You
313     * have to call File::Save() to make changes persistent.
314     */
315     void Info::UpdateChunks() {
316     if (!pResourceListChunk) return;
317    
318     // make sure INFO list chunk exists
319     RIFF::List* lstINFO = pResourceListChunk->GetSubList(LIST_TYPE_INFO);
320    
321 persson 918 String defaultName = "";
322     String defaultCreationDate = "";
323     String defaultSoftware = "";
324     String defaultComments = "";
325 schoenebeck 800
326 persson 918 uint32_t resourceType = pResourceListChunk->GetListType();
327    
328     if (!lstINFO) {
329     lstINFO = pResourceListChunk->AddSubList(LIST_TYPE_INFO);
330    
331     // assemble default values
332     defaultName = "NONAME";
333    
334     if (resourceType == RIFF_TYPE_DLS) {
335     // get current date
336     time_t now = time(NULL);
337     tm* pNowBroken = localtime(&now);
338     char buf[11];
339     strftime(buf, 11, "%F", pNowBroken);
340     defaultCreationDate = buf;
341    
342     defaultComments = "Created with " + libraryName() + " " + libraryVersion();
343     }
344     if (resourceType == RIFF_TYPE_DLS || resourceType == LIST_TYPE_INS)
345     {
346     defaultSoftware = libraryName() + " " + libraryVersion();
347     }
348     }
349    
350 schoenebeck 800 // save values
351 persson 918
352 persson 1180 SaveString(CHUNK_ID_IARL, lstINFO, ArchivalLocation, String(""));
353     SaveString(CHUNK_ID_IART, lstINFO, Artists, String(""));
354     SaveString(CHUNK_ID_ICMS, lstINFO, Commissioned, String(""));
355     SaveString(CHUNK_ID_ICMT, lstINFO, Comments, defaultComments);
356     SaveString(CHUNK_ID_ICOP, lstINFO, Copyright, String(""));
357     SaveString(CHUNK_ID_ICRD, lstINFO, CreationDate, defaultCreationDate);
358     SaveString(CHUNK_ID_IENG, lstINFO, Engineer, String(""));
359     SaveString(CHUNK_ID_IGNR, lstINFO, Genre, String(""));
360     SaveString(CHUNK_ID_IKEY, lstINFO, Keywords, String(""));
361     SaveString(CHUNK_ID_IMED, lstINFO, Medium, String(""));
362     SaveString(CHUNK_ID_INAM, lstINFO, Name, defaultName);
363     SaveString(CHUNK_ID_IPRD, lstINFO, Product, String(""));
364     SaveString(CHUNK_ID_ISBJ, lstINFO, Subject, String(""));
365     SaveString(CHUNK_ID_ISFT, lstINFO, Software, defaultSoftware);
366     SaveString(CHUNK_ID_ISRC, lstINFO, Source, String(""));
367     SaveString(CHUNK_ID_ISRF, lstINFO, SourceForm, String(""));
368     SaveString(CHUNK_ID_ITCH, lstINFO, Technician, String(""));
369 schoenebeck 800 }
370    
371    
372    
373 schoenebeck 2 // *************** Resource ***************
374     // *
375    
376 schoenebeck 800 /** @brief Constructor.
377     *
378     * Initializes the 'Resource' object with values provided by a given
379     * INFO list chunk and a DLID chunk (the latter optional).
380     *
381     * @param Parent - pointer to parent 'Resource', NULL if this is
382     * the toplevel 'Resource' object
383     * @param lstResource - pointer to an INFO list chunk
384     */
385 schoenebeck 2 Resource::Resource(Resource* Parent, RIFF::List* lstResource) {
386     pParent = Parent;
387 schoenebeck 800 pResourceList = lstResource;
388 schoenebeck 2
389     pInfo = new Info(lstResource);
390    
391     RIFF::Chunk* ckDLSID = lstResource->GetSubChunk(CHUNK_ID_DLID);
392     if (ckDLSID) {
393     pDLSID = new dlsid_t;
394 schoenebeck 11 ckDLSID->Read(&pDLSID->ulData1, 1, 4);
395     ckDLSID->Read(&pDLSID->usData2, 1, 2);
396     ckDLSID->Read(&pDLSID->usData3, 1, 2);
397     ckDLSID->Read(pDLSID->abData, 8, 1);
398 schoenebeck 2 }
399     else pDLSID = NULL;
400     }
401    
402     Resource::~Resource() {
403     if (pDLSID) delete pDLSID;
404     if (pInfo) delete pInfo;
405     }
406    
407 schoenebeck 800 /** @brief Update chunks with current Resource data.
408     *
409     * Apply Resource data persistently below the previously given resource
410     * list chunk. This will currently only include the INFO data. The DLSID
411     * will not be applied at the moment (yet).
412     *
413     * You have to call File::Save() to make changes persistent.
414     */
415     void Resource::UpdateChunks() {
416     pInfo->UpdateChunks();
417 persson 1209
418     if (pDLSID) {
419     // make sure 'dlid' chunk exists
420     RIFF::Chunk* ckDLSID = pResourceList->GetSubChunk(CHUNK_ID_DLID);
421     if (!ckDLSID) ckDLSID = pResourceList->AddSubChunk(CHUNK_ID_DLID, 16);
422     uint8_t* pData = (uint8_t*)ckDLSID->LoadChunkData();
423     // update 'dlid' chunk
424     store32(&pData[0], pDLSID->ulData1);
425     store16(&pData[4], pDLSID->usData2);
426     store16(&pData[6], pDLSID->usData3);
427     memcpy(&pData[8], pDLSID->abData, 8);
428     }
429 schoenebeck 800 }
430 schoenebeck 2
431 persson 1209 /**
432     * Generates a new DLSID for the resource.
433     */
434     void Resource::GenerateDLSID() {
435     #if defined(WIN32) || defined(__APPLE__) || defined(HAVE_UUID_GENERATE)
436 schoenebeck 2
437 persson 1209 if (!pDLSID) pDLSID = new dlsid_t;
438 schoenebeck 800
439 persson 1209 #ifdef WIN32
440    
441     UUID uuid;
442     UuidCreate(&uuid);
443     pDLSID->ulData1 = uuid.Data1;
444 persson 1301 pDLSID->usData2 = uuid.Data2;
445     pDLSID->usData3 = uuid.Data3;
446 persson 1209 memcpy(pDLSID->abData, uuid.Data4, 8);
447    
448     #elif defined(__APPLE__)
449    
450     CFUUIDRef uuidRef = CFUUIDCreate(NULL);
451     CFUUIDBytes uuid = CFUUIDGetUUIDBytes(uuidRef);
452     CFRelease(uuidRef);
453     pDLSID->ulData1 = uuid.byte0 | uuid.byte1 << 8 | uuid.byte2 << 16 | uuid.byte3 << 24;
454     pDLSID->usData2 = uuid.byte4 | uuid.byte5 << 8;
455     pDLSID->usData3 = uuid.byte6 | uuid.byte7 << 8;
456     pDLSID->abData[0] = uuid.byte8;
457     pDLSID->abData[1] = uuid.byte9;
458     pDLSID->abData[2] = uuid.byte10;
459     pDLSID->abData[3] = uuid.byte11;
460     pDLSID->abData[4] = uuid.byte12;
461     pDLSID->abData[5] = uuid.byte13;
462     pDLSID->abData[6] = uuid.byte14;
463     pDLSID->abData[7] = uuid.byte15;
464     #else
465     uuid_t uuid;
466     uuid_generate(uuid);
467     pDLSID->ulData1 = uuid[0] | uuid[1] << 8 | uuid[2] << 16 | uuid[3] << 24;
468     pDLSID->usData2 = uuid[4] | uuid[5] << 8;
469     pDLSID->usData3 = uuid[6] | uuid[7] << 8;
470     memcpy(pDLSID->abData, &uuid[8], 8);
471     #endif
472     #endif
473     }
474    
475    
476 schoenebeck 2 // *************** Sampler ***************
477     // *
478    
479     Sampler::Sampler(RIFF::List* ParentList) {
480 schoenebeck 800 pParentList = ParentList;
481 schoenebeck 2 RIFF::Chunk* wsmp = ParentList->GetSubChunk(CHUNK_ID_WSMP);
482 schoenebeck 800 if (wsmp) {
483     uiHeaderSize = wsmp->ReadUint32();
484     UnityNote = wsmp->ReadUint16();
485     FineTune = wsmp->ReadInt16();
486     Gain = wsmp->ReadInt32();
487     SamplerOptions = wsmp->ReadUint32();
488     SampleLoops = wsmp->ReadUint32();
489     } else { // 'wsmp' chunk missing
490     uiHeaderSize = 0;
491 persson 1218 UnityNote = 60;
492 schoenebeck 800 FineTune = 0; // +- 0 cents
493     Gain = 0; // 0 dB
494     SamplerOptions = F_WSMP_NO_COMPRESSION;
495     SampleLoops = 0;
496     }
497 schoenebeck 2 NoSampleDepthTruncation = SamplerOptions & F_WSMP_NO_TRUNCATION;
498     NoSampleCompression = SamplerOptions & F_WSMP_NO_COMPRESSION;
499     pSampleLoops = (SampleLoops) ? new sample_loop_t[SampleLoops] : NULL;
500 schoenebeck 800 if (SampleLoops) {
501     wsmp->SetPos(uiHeaderSize);
502     for (uint32_t i = 0; i < SampleLoops; i++) {
503     wsmp->Read(pSampleLoops + i, 4, 4);
504     if (pSampleLoops[i].Size > sizeof(sample_loop_t)) { // if loop struct was extended
505     wsmp->SetPos(pSampleLoops[i].Size - sizeof(sample_loop_t), RIFF::stream_curpos);
506     }
507 schoenebeck 2 }
508     }
509     }
510    
511     Sampler::~Sampler() {
512     if (pSampleLoops) delete[] pSampleLoops;
513     }
514    
515 schoenebeck 800 /**
516     * Apply all sample player options to the respective RIFF chunk. You
517     * have to call File::Save() to make changes persistent.
518     */
519     void Sampler::UpdateChunks() {
520     // make sure 'wsmp' chunk exists
521     RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP);
522     if (!wsmp) {
523     uiHeaderSize = 20;
524     wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, uiHeaderSize + SampleLoops * 16);
525     }
526     uint8_t* pData = (uint8_t*) wsmp->LoadChunkData();
527     // update headers size
528 persson 1179 store32(&pData[0], uiHeaderSize);
529 schoenebeck 800 // update respective sampler options bits
530     SamplerOptions = (NoSampleDepthTruncation) ? SamplerOptions | F_WSMP_NO_TRUNCATION
531     : SamplerOptions & (~F_WSMP_NO_TRUNCATION);
532     SamplerOptions = (NoSampleCompression) ? SamplerOptions | F_WSMP_NO_COMPRESSION
533     : SamplerOptions & (~F_WSMP_NO_COMPRESSION);
534 persson 1179 store16(&pData[4], UnityNote);
535     store16(&pData[6], FineTune);
536     store32(&pData[8], Gain);
537     store32(&pData[12], SamplerOptions);
538     store32(&pData[16], SampleLoops);
539 schoenebeck 800 // update loop definitions
540     for (uint32_t i = 0; i < SampleLoops; i++) {
541     //FIXME: this does not handle extended loop structs correctly
542 persson 1179 store32(&pData[uiHeaderSize + i * 16], pSampleLoops[i].Size);
543     store32(&pData[uiHeaderSize + i * 16 + 4], pSampleLoops[i].LoopType);
544     store32(&pData[uiHeaderSize + i * 16 + 8], pSampleLoops[i].LoopStart);
545     store32(&pData[uiHeaderSize + i * 16 + 12], pSampleLoops[i].LoopLength);
546 schoenebeck 800 }
547     }
548 schoenebeck 2
549 schoenebeck 1154 /**
550     * Adds a new sample loop with the provided loop definition.
551     *
552 schoenebeck 1194 * @param pLoopDef - points to a loop definition that is to be copied
553 schoenebeck 1154 */
554     void Sampler::AddSampleLoop(sample_loop_t* pLoopDef) {
555     sample_loop_t* pNewLoops = new sample_loop_t[SampleLoops + 1];
556     // copy old loops array
557     for (int i = 0; i < SampleLoops; i++) {
558     pNewLoops[i] = pSampleLoops[i];
559     }
560     // add the new loop
561     pNewLoops[SampleLoops] = *pLoopDef;
562 schoenebeck 1155 // auto correct size field
563     pNewLoops[SampleLoops].Size = sizeof(DLS::sample_loop_t);
564 schoenebeck 1154 // free the old array and update the member variables
565     if (SampleLoops) delete[] pSampleLoops;
566     pSampleLoops = pNewLoops;
567     SampleLoops++;
568     }
569 schoenebeck 2
570 schoenebeck 1154 /**
571     * Deletes an existing sample loop.
572     *
573     * @param pLoopDef - pointer to existing loop definition
574     * @throws Exception - if given loop definition does not exist
575     */
576     void Sampler::DeleteSampleLoop(sample_loop_t* pLoopDef) {
577     sample_loop_t* pNewLoops = new sample_loop_t[SampleLoops - 1];
578     // copy old loops array (skipping given loop)
579     for (int i = 0, o = 0; i < SampleLoops; i++) {
580     if (&pSampleLoops[i] == pLoopDef) continue;
581     if (o == SampleLoops - 1)
582     throw Exception("Could not delete Sample Loop, because it does not exist");
583     pNewLoops[o] = pSampleLoops[i];
584     o++;
585     }
586     // free the old array and update the member variables
587     if (SampleLoops) delete[] pSampleLoops;
588     pSampleLoops = pNewLoops;
589     SampleLoops--;
590     }
591 schoenebeck 800
592 schoenebeck 1154
593    
594 schoenebeck 2 // *************** Sample ***************
595     // *
596    
597 schoenebeck 800 /** @brief Constructor.
598     *
599     * Load an existing sample or create a new one. A 'wave' list chunk must
600     * be given to this constructor. In case the given 'wave' list chunk
601     * contains a 'fmt' and 'data' chunk, the format and sample data will be
602     * loaded from there, otherwise default values will be used and those
603     * chunks will be created when File::Save() will be called later on.
604     *
605     * @param pFile - pointer to DLS::File where this sample is
606     * located (or will be located)
607     * @param waveList - pointer to 'wave' list chunk which is (or
608     * will be) associated with this sample
609     * @param WavePoolOffset - offset of this sample data from wave pool
610     * ('wvpl') list chunk
611     */
612 schoenebeck 2 Sample::Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset) : Resource(pFile, waveList) {
613 schoenebeck 800 pWaveList = waveList;
614 schoenebeck 2 ulWavePoolOffset = WavePoolOffset - LIST_HEADER_SIZE;
615     pCkFormat = waveList->GetSubChunk(CHUNK_ID_FMT);
616     pCkData = waveList->GetSubChunk(CHUNK_ID_DATA);
617 schoenebeck 800 if (pCkFormat) {
618     // common fields
619     FormatTag = pCkFormat->ReadUint16();
620     Channels = pCkFormat->ReadUint16();
621     SamplesPerSecond = pCkFormat->ReadUint32();
622     AverageBytesPerSecond = pCkFormat->ReadUint32();
623     BlockAlign = pCkFormat->ReadUint16();
624     // PCM format specific
625 schoenebeck 1050 if (FormatTag == DLS_WAVE_FORMAT_PCM) {
626 schoenebeck 800 BitDepth = pCkFormat->ReadUint16();
627 persson 928 FrameSize = (BitDepth / 8) * Channels;
628 schoenebeck 800 } else { // unsupported sample data format
629     BitDepth = 0;
630     FrameSize = 0;
631     }
632     } else { // 'fmt' chunk missing
633 schoenebeck 1050 FormatTag = DLS_WAVE_FORMAT_PCM;
634 schoenebeck 800 BitDepth = 16;
635     Channels = 1;
636     SamplesPerSecond = 44100;
637     AverageBytesPerSecond = (BitDepth / 8) * SamplesPerSecond * Channels;
638     FrameSize = (BitDepth / 8) * Channels;
639     BlockAlign = FrameSize;
640 schoenebeck 2 }
641 schoenebeck 1050 SamplesTotal = (pCkData) ? (FormatTag == DLS_WAVE_FORMAT_PCM) ? pCkData->GetSize() / FrameSize
642     : 0
643 schoenebeck 800 : 0;
644 schoenebeck 2 }
645    
646 schoenebeck 800 /** @brief Destructor.
647     *
648     * Removes RIFF chunks associated with this Sample and frees all
649     * memory occupied by this sample.
650     */
651     Sample::~Sample() {
652     RIFF::List* pParent = pWaveList->GetParent();
653     pParent->DeleteSubChunk(pWaveList);
654     }
655    
656     /** @brief Load sample data into RAM.
657     *
658     * In case the respective 'data' chunk exists, the sample data will be
659     * loaded into RAM (if not done already) and a pointer to the data in
660     * RAM will be returned. If this is a new sample, you have to call
661     * Resize() with the desired sample size to create the mandatory RIFF
662     * chunk for the sample wave data.
663     *
664     * You can call LoadChunkData() again if you previously scheduled to
665     * enlarge the sample data RIFF chunk with a Resize() call. In that case
666     * the buffer will be enlarged to the new, scheduled size and you can
667     * already place the sample wave data to the buffer and finally call
668     * File::Save() to enlarge the sample data's chunk physically and write
669     * the new sample wave data in one rush. This approach is definitely
670     * recommended if you have to enlarge and write new sample data to a lot
671     * of samples.
672     *
673     * <b>Caution:</b> the buffer pointer will be invalidated once
674     * File::Save() was called. You have to call LoadChunkData() again to
675     * get a new, valid pointer whenever File::Save() was called.
676     *
677     * @returns pointer to sample data in RAM, NULL in case respective
678     * 'data' chunk does not exist (yet)
679     * @throws Exception if data buffer could not be enlarged
680     * @see Resize(), File::Save()
681     */
682 schoenebeck 2 void* Sample::LoadSampleData() {
683 schoenebeck 800 return (pCkData) ? pCkData->LoadChunkData() : NULL;
684 schoenebeck 2 }
685    
686 schoenebeck 800 /** @brief Free sample data from RAM.
687     *
688     * In case sample data was previously successfully loaded into RAM with
689     * LoadSampleData(), this method will free the sample data from RAM.
690     */
691 schoenebeck 2 void Sample::ReleaseSampleData() {
692 schoenebeck 800 if (pCkData) pCkData->ReleaseChunkData();
693 schoenebeck 2 }
694    
695 schoenebeck 800 /** @brief Returns sample size.
696     *
697     * Returns the sample wave form's data size (in sample points). This is
698     * actually the current, physical size (converted to sample points) of
699     * the RIFF chunk which encapsulates the sample's wave data. The
700     * returned value is dependant to the current FrameSize value.
701     *
702 schoenebeck 1050 * @returns number of sample points or 0 if FormatTag != DLS_WAVE_FORMAT_PCM
703 schoenebeck 800 * @see FrameSize, FormatTag
704     */
705     unsigned long Sample::GetSize() {
706 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0;
707 schoenebeck 800 return (pCkData) ? pCkData->GetSize() / FrameSize : 0;
708     }
709    
710     /** @brief Resize sample.
711     *
712     * Resizes the sample's wave form data, that is the actual size of
713     * sample wave data possible to be written for this sample. This call
714     * will return immediately and just schedule the resize operation. You
715     * should call File::Save() to actually perform the resize operation(s)
716     * "physically" to the file. As this can take a while on large files, it
717     * is recommended to call Resize() first on all samples which have to be
718     * resized and finally to call File::Save() to perform all those resize
719     * operations in one rush.
720     *
721     * The actual size (in bytes) is dependant to the current FrameSize
722     * value. You may want to set FrameSize before calling Resize().
723     *
724     * <b>Caution:</b> You cannot directly write to enlarged samples before
725     * calling File::Save() as this might exceed the current sample's
726     * boundary!
727     *
728 schoenebeck 1050 * Also note: only DLS_WAVE_FORMAT_PCM is currently supported, that is
729     * FormatTag must be DLS_WAVE_FORMAT_PCM. Trying to resize samples with
730 schoenebeck 800 * other formats will fail!
731     *
732     * @param iNewSize - new sample wave data size in sample points (must be
733     * greater than zero)
734 schoenebeck 1050 * @throws Excecption if FormatTag != DLS_WAVE_FORMAT_PCM
735 schoenebeck 800 * @throws Exception if \a iNewSize is less than 1
736     * @see File::Save(), FrameSize, FormatTag
737     */
738     void Sample::Resize(int iNewSize) {
739 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) throw Exception("Sample's format is not DLS_WAVE_FORMAT_PCM");
740 schoenebeck 800 if (iNewSize < 1) throw Exception("Sample size must be at least one sample point");
741     const int iSizeInBytes = iNewSize * FrameSize;
742     pCkData = pWaveList->GetSubChunk(CHUNK_ID_DATA);
743     if (pCkData) pCkData->Resize(iSizeInBytes);
744     else pCkData = pWaveList->AddSubChunk(CHUNK_ID_DATA, iSizeInBytes);
745     }
746    
747 schoenebeck 2 /**
748     * Sets the position within the sample (in sample points, not in
749     * bytes). Use this method and <i>Read()</i> if you don't want to load
750     * the sample into RAM, thus for disk streaming.
751     *
752 schoenebeck 1050 * Also note: only DLS_WAVE_FORMAT_PCM is currently supported, that is
753     * FormatTag must be DLS_WAVE_FORMAT_PCM. Trying to reposition the sample
754 schoenebeck 800 * with other formats will fail!
755     *
756 schoenebeck 2 * @param SampleCount number of sample points
757     * @param Whence to which relation \a SampleCount refers to
758 schoenebeck 800 * @returns new position within the sample, 0 if
759 schoenebeck 1050 * FormatTag != DLS_WAVE_FORMAT_PCM
760 schoenebeck 800 * @throws Exception if no data RIFF chunk was created for the sample yet
761     * @see FrameSize, FormatTag
762 schoenebeck 2 */
763     unsigned long Sample::SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence) {
764 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
765 schoenebeck 800 if (!pCkData) throw Exception("No data chunk created for sample yet, call Sample::Resize() to create one");
766 schoenebeck 2 unsigned long orderedBytes = SampleCount * FrameSize;
767     unsigned long result = pCkData->SetPos(orderedBytes, Whence);
768     return (result == orderedBytes) ? SampleCount
769     : result / FrameSize;
770     }
771    
772     /**
773     * Reads \a SampleCount number of sample points from the current
774     * position into the buffer pointed by \a pBuffer and increments the
775     * position within the sample. Use this method and <i>SetPos()</i> if you
776     * don't want to load the sample into RAM, thus for disk streaming.
777     *
778     * @param pBuffer destination buffer
779     * @param SampleCount number of sample points to read
780     */
781     unsigned long Sample::Read(void* pBuffer, unsigned long SampleCount) {
782 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
783 schoenebeck 11 return pCkData->Read(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?
784 schoenebeck 2 }
785    
786 schoenebeck 800 /** @brief Write sample wave data.
787     *
788     * Writes \a SampleCount number of sample points from the buffer pointed
789     * by \a pBuffer and increments the position within the sample. Use this
790     * method to directly write the sample data to disk, i.e. if you don't
791     * want or cannot load the whole sample data into RAM.
792     *
793     * You have to Resize() the sample to the desired size and call
794     * File::Save() <b>before</b> using Write().
795     *
796     * @param pBuffer - source buffer
797     * @param SampleCount - number of sample points to write
798     * @throws Exception if current sample size is too small
799     * @see LoadSampleData()
800     */
801     unsigned long Sample::Write(void* pBuffer, unsigned long SampleCount) {
802 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
803 schoenebeck 800 if (GetSize() < SampleCount) throw Exception("Could not write sample data, current sample size to small");
804     return pCkData->Write(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?
805     }
806 schoenebeck 2
807 schoenebeck 800 /**
808     * Apply sample and its settings to the respective RIFF chunks. You have
809     * to call File::Save() to make changes persistent.
810     *
811 schoenebeck 1050 * @throws Exception if FormatTag != DLS_WAVE_FORMAT_PCM or no sample data
812 schoenebeck 800 * was provided yet
813     */
814     void Sample::UpdateChunks() {
815 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM)
816 schoenebeck 800 throw Exception("Could not save sample, only PCM format is supported");
817     // we refuse to do anything if not sample wave form was provided yet
818     if (!pCkData)
819     throw Exception("Could not save sample, there is no sample data to save");
820     // update chunks of base class as well
821     Resource::UpdateChunks();
822     // make sure 'fmt' chunk exists
823     RIFF::Chunk* pCkFormat = pWaveList->GetSubChunk(CHUNK_ID_FMT);
824     if (!pCkFormat) pCkFormat = pWaveList->AddSubChunk(CHUNK_ID_FMT, 16); // assumes PCM format
825     uint8_t* pData = (uint8_t*) pCkFormat->LoadChunkData();
826     // update 'fmt' chunk
827 persson 1179 store16(&pData[0], FormatTag);
828     store16(&pData[2], Channels);
829     store32(&pData[4], SamplesPerSecond);
830     store32(&pData[8], AverageBytesPerSecond);
831     store16(&pData[12], BlockAlign);
832     store16(&pData[14], BitDepth); // assuming PCM format
833 schoenebeck 800 }
834 schoenebeck 2
835 schoenebeck 800
836    
837 schoenebeck 2 // *************** Region ***************
838     // *
839    
840     Region::Region(Instrument* pInstrument, RIFF::List* rgnList) : Resource(pInstrument, rgnList), Articulator(rgnList), Sampler(rgnList) {
841     pCkRegion = rgnList;
842    
843 schoenebeck 800 // articulation informations
844 schoenebeck 2 RIFF::Chunk* rgnh = rgnList->GetSubChunk(CHUNK_ID_RGNH);
845 schoenebeck 800 if (rgnh) {
846     rgnh->Read(&KeyRange, 2, 2);
847     rgnh->Read(&VelocityRange, 2, 2);
848     FormatOptionFlags = rgnh->ReadUint16();
849     KeyGroup = rgnh->ReadUint16();
850     // Layer is optional
851     if (rgnh->RemainingBytes() >= sizeof(uint16_t)) {
852     rgnh->Read(&Layer, 1, sizeof(uint16_t));
853     } else Layer = 0;
854     } else { // 'rgnh' chunk is missing
855     KeyRange.low = 0;
856     KeyRange.high = 127;
857     VelocityRange.low = 0;
858     VelocityRange.high = 127;
859     FormatOptionFlags = F_RGN_OPTION_SELFNONEXCLUSIVE;
860     KeyGroup = 0;
861     Layer = 0;
862 schoenebeck 2 }
863 schoenebeck 800 SelfNonExclusive = FormatOptionFlags & F_RGN_OPTION_SELFNONEXCLUSIVE;
864 schoenebeck 2
865 schoenebeck 800 // sample informations
866 schoenebeck 2 RIFF::Chunk* wlnk = rgnList->GetSubChunk(CHUNK_ID_WLNK);
867 schoenebeck 800 if (wlnk) {
868     WaveLinkOptionFlags = wlnk->ReadUint16();
869     PhaseGroup = wlnk->ReadUint16();
870     Channel = wlnk->ReadUint32();
871     WavePoolTableIndex = wlnk->ReadUint32();
872     } else { // 'wlnk' chunk is missing
873     WaveLinkOptionFlags = 0;
874     PhaseGroup = 0;
875     Channel = 0; // mono
876     WavePoolTableIndex = 0; // first entry in wave pool table
877     }
878     PhaseMaster = WaveLinkOptionFlags & F_WAVELINK_PHASE_MASTER;
879     MultiChannel = WaveLinkOptionFlags & F_WAVELINK_MULTICHANNEL;
880 schoenebeck 2
881     pSample = NULL;
882     }
883    
884 schoenebeck 800 /** @brief Destructor.
885     *
886     * Removes RIFF chunks associated with this Region.
887     */
888 schoenebeck 2 Region::~Region() {
889 schoenebeck 800 RIFF::List* pParent = pCkRegion->GetParent();
890     pParent->DeleteSubChunk(pCkRegion);
891 schoenebeck 2 }
892    
893     Sample* Region::GetSample() {
894     if (pSample) return pSample;
895     File* file = (File*) GetParent()->GetParent();
896     unsigned long soughtoffset = file->pWavePoolTable[WavePoolTableIndex];
897     Sample* sample = file->GetFirstSample();
898     while (sample) {
899     if (sample->ulWavePoolOffset == soughtoffset) return (pSample = sample);
900     sample = file->GetNextSample();
901     }
902     return NULL;
903     }
904    
905 schoenebeck 800 /**
906     * Assign another sample to this Region.
907     *
908     * @param pSample - sample to be assigned
909     */
910     void Region::SetSample(Sample* pSample) {
911     this->pSample = pSample;
912     WavePoolTableIndex = 0; // we update this offset when we Save()
913     }
914 schoenebeck 2
915 schoenebeck 800 /**
916 schoenebeck 1335 * Modifies the key range of this Region and makes sure the respective
917     * chunks are in correct order.
918     *
919     * @param Low - lower end of key range
920     * @param High - upper end of key range
921     */
922     void Region::SetKeyRange(uint16_t Low, uint16_t High) {
923     KeyRange.low = Low;
924     KeyRange.high = High;
925    
926     // make sure regions are already loaded
927     Instrument* pInstrument = (Instrument*) GetParent();
928     if (!pInstrument->pRegions) pInstrument->LoadRegions();
929     if (!pInstrument->pRegions) return;
930    
931     // find the r which is the first one to the right of this region
932     // at its new position
933     Region* r = NULL;
934     Region* prev_region = NULL;
935     for (
936     Instrument::RegionList::iterator iter = pInstrument->pRegions->begin();
937     iter != pInstrument->pRegions->end(); iter++
938     ) {
939     if ((*iter)->KeyRange.low > this->KeyRange.low) {
940     r = *iter;
941     break;
942     }
943     prev_region = *iter;
944     }
945    
946     // place this region before r if it's not already there
947     if (prev_region != this) pInstrument->MoveRegion(this, r);
948     }
949    
950     /**
951 schoenebeck 800 * Apply Region settings to the respective RIFF chunks. You have to
952     * call File::Save() to make changes persistent.
953     *
954     * @throws Exception - if the Region's sample could not be found
955     */
956     void Region::UpdateChunks() {
957     // make sure 'rgnh' chunk exists
958     RIFF::Chunk* rgnh = pCkRegion->GetSubChunk(CHUNK_ID_RGNH);
959 persson 918 if (!rgnh) rgnh = pCkRegion->AddSubChunk(CHUNK_ID_RGNH, Layer ? 14 : 12);
960 schoenebeck 800 uint8_t* pData = (uint8_t*) rgnh->LoadChunkData();
961     FormatOptionFlags = (SelfNonExclusive)
962     ? FormatOptionFlags | F_RGN_OPTION_SELFNONEXCLUSIVE
963     : FormatOptionFlags & (~F_RGN_OPTION_SELFNONEXCLUSIVE);
964     // update 'rgnh' chunk
965 persson 1179 store16(&pData[0], KeyRange.low);
966     store16(&pData[2], KeyRange.high);
967     store16(&pData[4], VelocityRange.low);
968     store16(&pData[6], VelocityRange.high);
969     store16(&pData[8], FormatOptionFlags);
970     store16(&pData[10], KeyGroup);
971     if (rgnh->GetSize() >= 14) store16(&pData[12], Layer);
972 schoenebeck 2
973 persson 918 // update chunks of base classes as well (but skip Resource,
974     // as a rgn doesn't seem to have dlid and INFO chunks)
975 schoenebeck 800 Articulator::UpdateChunks();
976     Sampler::UpdateChunks();
977    
978     // make sure 'wlnk' chunk exists
979     RIFF::Chunk* wlnk = pCkRegion->GetSubChunk(CHUNK_ID_WLNK);
980     if (!wlnk) wlnk = pCkRegion->AddSubChunk(CHUNK_ID_WLNK, 12);
981     pData = (uint8_t*) wlnk->LoadChunkData();
982     WaveLinkOptionFlags = (PhaseMaster)
983     ? WaveLinkOptionFlags | F_WAVELINK_PHASE_MASTER
984     : WaveLinkOptionFlags & (~F_WAVELINK_PHASE_MASTER);
985     WaveLinkOptionFlags = (MultiChannel)
986     ? WaveLinkOptionFlags | F_WAVELINK_MULTICHANNEL
987     : WaveLinkOptionFlags & (~F_WAVELINK_MULTICHANNEL);
988     // get sample's wave pool table index
989     int index = -1;
990     File* pFile = (File*) GetParent()->GetParent();
991 schoenebeck 804 if (pFile->pSamples) {
992     File::SampleList::iterator iter = pFile->pSamples->begin();
993     File::SampleList::iterator end = pFile->pSamples->end();
994     for (int i = 0; iter != end; ++iter, i++) {
995     if (*iter == pSample) {
996     index = i;
997     break;
998     }
999 schoenebeck 800 }
1000     }
1001     WavePoolTableIndex = index;
1002     // update 'wlnk' chunk
1003 persson 1179 store16(&pData[0], WaveLinkOptionFlags);
1004     store16(&pData[2], PhaseGroup);
1005     store32(&pData[4], Channel);
1006     store32(&pData[8], WavePoolTableIndex);
1007 schoenebeck 800 }
1008    
1009    
1010    
1011 schoenebeck 2 // *************** Instrument ***************
1012     // *
1013    
1014 schoenebeck 800 /** @brief Constructor.
1015     *
1016     * Load an existing instrument definition or create a new one. An 'ins'
1017     * list chunk must be given to this constructor. In case this 'ins' list
1018     * chunk contains a 'insh' chunk, the instrument data fields will be
1019     * loaded from there, otherwise default values will be used and the
1020     * 'insh' chunk will be created once File::Save() was called.
1021     *
1022     * @param pFile - pointer to DLS::File where this instrument is
1023     * located (or will be located)
1024     * @param insList - pointer to 'ins' list chunk which is (or will be)
1025     * associated with this instrument
1026     */
1027 schoenebeck 2 Instrument::Instrument(File* pFile, RIFF::List* insList) : Resource(pFile, insList), Articulator(insList) {
1028     pCkInstrument = insList;
1029    
1030 schoenebeck 800 midi_locale_t locale;
1031 schoenebeck 2 RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
1032 schoenebeck 800 if (insh) {
1033     Regions = insh->ReadUint32();
1034     insh->Read(&locale, 2, 4);
1035     } else { // 'insh' chunk missing
1036     Regions = 0;
1037     locale.bank = 0;
1038     locale.instrument = 0;
1039     }
1040    
1041 schoenebeck 2 MIDIProgram = locale.instrument;
1042     IsDrum = locale.bank & DRUM_TYPE_MASK;
1043     MIDIBankCoarse = (uint8_t) MIDI_BANK_COARSE(locale.bank);
1044     MIDIBankFine = (uint8_t) MIDI_BANK_FINE(locale.bank);
1045     MIDIBank = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine);
1046    
1047 schoenebeck 800 pRegions = NULL;
1048 schoenebeck 2 }
1049    
1050     Region* Instrument::GetFirstRegion() {
1051     if (!pRegions) LoadRegions();
1052     if (!pRegions) return NULL;
1053     RegionsIterator = pRegions->begin();
1054     return (RegionsIterator != pRegions->end()) ? *RegionsIterator : NULL;
1055     }
1056    
1057     Region* Instrument::GetNextRegion() {
1058     if (!pRegions) return NULL;
1059     RegionsIterator++;
1060     return (RegionsIterator != pRegions->end()) ? *RegionsIterator : NULL;
1061     }
1062    
1063     void Instrument::LoadRegions() {
1064 schoenebeck 823 if (!pRegions) pRegions = new RegionList;
1065 schoenebeck 2 RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1066 schoenebeck 823 if (lrgn) {
1067     uint32_t regionCkType = (lrgn->GetSubList(LIST_TYPE_RGN2)) ? LIST_TYPE_RGN2 : LIST_TYPE_RGN; // prefer regions level 2
1068     RIFF::List* rgn = lrgn->GetFirstSubList();
1069     while (rgn) {
1070     if (rgn->GetListType() == regionCkType) {
1071     pRegions->push_back(new Region(this, rgn));
1072     }
1073     rgn = lrgn->GetNextSubList();
1074 schoenebeck 2 }
1075     }
1076     }
1077    
1078 schoenebeck 800 Region* Instrument::AddRegion() {
1079 schoenebeck 823 if (!pRegions) LoadRegions();
1080 schoenebeck 800 RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1081     if (!lrgn) lrgn = pCkInstrument->AddSubList(LIST_TYPE_LRGN);
1082     RIFF::List* rgn = lrgn->AddSubList(LIST_TYPE_RGN);
1083     Region* pNewRegion = new Region(this, rgn);
1084     pRegions->push_back(pNewRegion);
1085 schoenebeck 823 Regions = pRegions->size();
1086 schoenebeck 800 return pNewRegion;
1087     }
1088    
1089 persson 1102 void Instrument::MoveRegion(Region* pSrc, Region* pDst) {
1090     RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1091     lrgn->MoveSubChunk(pSrc->pCkRegion, pDst ? pDst->pCkRegion : 0);
1092    
1093     pRegions->remove(pSrc);
1094     RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pDst);
1095     pRegions->insert(iter, pSrc);
1096     }
1097    
1098 schoenebeck 800 void Instrument::DeleteRegion(Region* pRegion) {
1099 schoenebeck 802 if (!pRegions) return;
1100 schoenebeck 800 RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pRegion);
1101     if (iter == pRegions->end()) return;
1102     pRegions->erase(iter);
1103 schoenebeck 823 Regions = pRegions->size();
1104 schoenebeck 800 delete pRegion;
1105     }
1106    
1107     /**
1108     * Apply Instrument with all its Regions to the respective RIFF chunks.
1109     * You have to call File::Save() to make changes persistent.
1110     *
1111     * @throws Exception - on errors
1112     */
1113     void Instrument::UpdateChunks() {
1114     // first update base classes' chunks
1115     Resource::UpdateChunks();
1116     Articulator::UpdateChunks();
1117     // make sure 'insh' chunk exists
1118     RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
1119     if (!insh) insh = pCkInstrument->AddSubChunk(CHUNK_ID_INSH, 12);
1120     uint8_t* pData = (uint8_t*) insh->LoadChunkData();
1121     // update 'insh' chunk
1122 schoenebeck 804 Regions = (pRegions) ? pRegions->size() : 0;
1123 schoenebeck 800 midi_locale_t locale;
1124     locale.instrument = MIDIProgram;
1125     locale.bank = MIDI_BANK_ENCODE(MIDIBankCoarse, MIDIBankFine);
1126     locale.bank = (IsDrum) ? locale.bank | DRUM_TYPE_MASK : locale.bank & (~DRUM_TYPE_MASK);
1127     MIDIBank = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine); // just a sync, when we're at it
1128 persson 1179 store32(&pData[0], Regions);
1129     store32(&pData[4], locale.bank);
1130     store32(&pData[8], locale.instrument);
1131 schoenebeck 800 // update Region's chunks
1132 schoenebeck 804 if (!pRegions) return;
1133 schoenebeck 800 RegionList::iterator iter = pRegions->begin();
1134     RegionList::iterator end = pRegions->end();
1135     for (; iter != end; ++iter) {
1136     (*iter)->UpdateChunks();
1137     }
1138     }
1139    
1140     /** @brief Destructor.
1141     *
1142     * Removes RIFF chunks associated with this Instrument and frees all
1143     * memory occupied by this instrument.
1144     */
1145 schoenebeck 2 Instrument::~Instrument() {
1146     if (pRegions) {
1147     RegionList::iterator iter = pRegions->begin();
1148     RegionList::iterator end = pRegions->end();
1149     while (iter != end) {
1150     delete *iter;
1151     iter++;
1152     }
1153     delete pRegions;
1154     }
1155 schoenebeck 800 // remove instrument's chunks
1156     RIFF::List* pParent = pCkInstrument->GetParent();
1157     pParent->DeleteSubChunk(pCkInstrument);
1158 schoenebeck 2 }
1159    
1160    
1161    
1162     // *************** File ***************
1163     // *
1164    
1165 schoenebeck 800 /** @brief Constructor.
1166     *
1167     * Default constructor, use this to create an empty DLS file. You have
1168     * to add samples, instruments and finally call Save() to actually write
1169     * a DLS file.
1170     */
1171 persson 1184 File::File() : Resource(NULL, pRIFF = new RIFF::File(RIFF_TYPE_DLS)) {
1172     pRIFF->SetByteOrder(RIFF::endian_little);
1173 schoenebeck 800 pVersion = new version_t;
1174     pVersion->major = 0;
1175     pVersion->minor = 0;
1176     pVersion->release = 0;
1177     pVersion->build = 0;
1178    
1179     Instruments = 0;
1180     WavePoolCount = 0;
1181     pWavePoolTable = NULL;
1182     pWavePoolTableHi = NULL;
1183     WavePoolHeaderSize = 8;
1184    
1185     pSamples = NULL;
1186     pInstruments = NULL;
1187    
1188     b64BitWavePoolOffsets = false;
1189     }
1190    
1191     /** @brief Constructor.
1192     *
1193     * Load an existing DLS file.
1194     *
1195     * @param pRIFF - pointer to a RIFF file which is actually the DLS file
1196     * to load
1197     * @throws Exception if given file is not a DLS file, expected chunks
1198     * are missing
1199     */
1200 schoenebeck 2 File::File(RIFF::File* pRIFF) : Resource(NULL, pRIFF) {
1201     if (!pRIFF) throw DLS::Exception("NULL pointer reference to RIFF::File object.");
1202     this->pRIFF = pRIFF;
1203    
1204     RIFF::Chunk* ckVersion = pRIFF->GetSubChunk(CHUNK_ID_VERS);
1205     if (ckVersion) {
1206     pVersion = new version_t;
1207 schoenebeck 11 ckVersion->Read(pVersion, 4, 2);
1208 schoenebeck 2 }
1209     else pVersion = NULL;
1210    
1211     RIFF::Chunk* colh = pRIFF->GetSubChunk(CHUNK_ID_COLH);
1212     if (!colh) throw DLS::Exception("Mandatory chunks in RIFF list chunk not found.");
1213     Instruments = colh->ReadUint32();
1214    
1215     RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1216 persson 902 if (!ptbl) { // pool table is missing - this is probably an ".art" file
1217     WavePoolCount = 0;
1218     pWavePoolTable = NULL;
1219     pWavePoolTableHi = NULL;
1220     WavePoolHeaderSize = 8;
1221     b64BitWavePoolOffsets = false;
1222     } else {
1223     WavePoolHeaderSize = ptbl->ReadUint32();
1224     WavePoolCount = ptbl->ReadUint32();
1225     pWavePoolTable = new uint32_t[WavePoolCount];
1226     pWavePoolTableHi = new uint32_t[WavePoolCount];
1227     ptbl->SetPos(WavePoolHeaderSize);
1228 schoenebeck 2
1229 persson 902 // Check for 64 bit offsets (used in gig v3 files)
1230     b64BitWavePoolOffsets = (ptbl->GetSize() - WavePoolHeaderSize == WavePoolCount * 8);
1231     if (b64BitWavePoolOffsets) {
1232     for (int i = 0 ; i < WavePoolCount ; i++) {
1233     pWavePoolTableHi[i] = ptbl->ReadUint32();
1234     pWavePoolTable[i] = ptbl->ReadUint32();
1235     if (pWavePoolTable[i] & 0x80000000)
1236     throw DLS::Exception("Files larger than 2 GB not yet supported");
1237     }
1238     } else { // conventional 32 bit offsets
1239     ptbl->Read(pWavePoolTable, WavePoolCount, sizeof(uint32_t));
1240     for (int i = 0 ; i < WavePoolCount ; i++) pWavePoolTableHi[i] = 0;
1241 schoenebeck 317 }
1242 persson 666 }
1243 schoenebeck 317
1244 schoenebeck 2 pSamples = NULL;
1245     pInstruments = NULL;
1246     }
1247    
1248     File::~File() {
1249     if (pInstruments) {
1250     InstrumentList::iterator iter = pInstruments->begin();
1251     InstrumentList::iterator end = pInstruments->end();
1252     while (iter != end) {
1253     delete *iter;
1254     iter++;
1255     }
1256     delete pInstruments;
1257     }
1258    
1259     if (pSamples) {
1260     SampleList::iterator iter = pSamples->begin();
1261     SampleList::iterator end = pSamples->end();
1262     while (iter != end) {
1263     delete *iter;
1264     iter++;
1265     }
1266     delete pSamples;
1267     }
1268    
1269     if (pWavePoolTable) delete[] pWavePoolTable;
1270 persson 666 if (pWavePoolTableHi) delete[] pWavePoolTableHi;
1271 schoenebeck 2 if (pVersion) delete pVersion;
1272 persson 834 for (std::list<RIFF::File*>::iterator i = ExtensionFiles.begin() ; i != ExtensionFiles.end() ; i++)
1273     delete *i;
1274 schoenebeck 2 }
1275    
1276     Sample* File::GetFirstSample() {
1277     if (!pSamples) LoadSamples();
1278     if (!pSamples) return NULL;
1279     SamplesIterator = pSamples->begin();
1280     return (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL;
1281     }
1282    
1283     Sample* File::GetNextSample() {
1284     if (!pSamples) return NULL;
1285     SamplesIterator++;
1286     return (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL;
1287     }
1288    
1289     void File::LoadSamples() {
1290 schoenebeck 823 if (!pSamples) pSamples = new SampleList;
1291 schoenebeck 2 RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1292     if (wvpl) {
1293     unsigned long wvplFileOffset = wvpl->GetFilePos();
1294     RIFF::List* wave = wvpl->GetFirstSubList();
1295     while (wave) {
1296     if (wave->GetListType() == LIST_TYPE_WAVE) {
1297     unsigned long waveFileOffset = wave->GetFilePos();
1298     pSamples->push_back(new Sample(this, wave, waveFileOffset - wvplFileOffset));
1299     }
1300     wave = wvpl->GetNextSubList();
1301     }
1302     }
1303     else { // Seen a dwpl list chunk instead of a wvpl list chunk in some file (officially not DLS compliant)
1304     RIFF::List* dwpl = pRIFF->GetSubList(LIST_TYPE_DWPL);
1305     if (dwpl) {
1306     unsigned long dwplFileOffset = dwpl->GetFilePos();
1307     RIFF::List* wave = dwpl->GetFirstSubList();
1308     while (wave) {
1309     if (wave->GetListType() == LIST_TYPE_WAVE) {
1310     unsigned long waveFileOffset = wave->GetFilePos();
1311     pSamples->push_back(new Sample(this, wave, waveFileOffset - dwplFileOffset));
1312     }
1313     wave = dwpl->GetNextSubList();
1314     }
1315     }
1316     }
1317     }
1318    
1319 schoenebeck 800 /** @brief Add a new sample.
1320     *
1321     * This will create a new Sample object for the DLS file. You have to
1322     * call Save() to make this persistent to the file.
1323     *
1324     * @returns pointer to new Sample object
1325     */
1326     Sample* File::AddSample() {
1327 schoenebeck 809 if (!pSamples) LoadSamples();
1328 schoenebeck 800 __ensureMandatoryChunksExist();
1329     RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1330     // create new Sample object and its respective 'wave' list chunk
1331     RIFF::List* wave = wvpl->AddSubList(LIST_TYPE_WAVE);
1332     Sample* pSample = new Sample(this, wave, 0 /*arbitrary value, we update offsets when we save*/);
1333     pSamples->push_back(pSample);
1334     return pSample;
1335     }
1336    
1337     /** @brief Delete a sample.
1338     *
1339     * This will delete the given Sample object from the DLS file. You have
1340     * to call Save() to make this persistent to the file.
1341     *
1342     * @param pSample - sample to delete
1343     */
1344     void File::DeleteSample(Sample* pSample) {
1345 schoenebeck 802 if (!pSamples) return;
1346 schoenebeck 800 SampleList::iterator iter = find(pSamples->begin(), pSamples->end(), pSample);
1347     if (iter == pSamples->end()) return;
1348     pSamples->erase(iter);
1349     delete pSample;
1350     }
1351    
1352 schoenebeck 2 Instrument* File::GetFirstInstrument() {
1353     if (!pInstruments) LoadInstruments();
1354     if (!pInstruments) return NULL;
1355     InstrumentsIterator = pInstruments->begin();
1356     return (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL;
1357     }
1358    
1359     Instrument* File::GetNextInstrument() {
1360     if (!pInstruments) return NULL;
1361     InstrumentsIterator++;
1362     return (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL;
1363     }
1364    
1365     void File::LoadInstruments() {
1366 schoenebeck 823 if (!pInstruments) pInstruments = new InstrumentList;
1367 schoenebeck 2 RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1368     if (lstInstruments) {
1369     RIFF::List* lstInstr = lstInstruments->GetFirstSubList();
1370     while (lstInstr) {
1371     if (lstInstr->GetListType() == LIST_TYPE_INS) {
1372     pInstruments->push_back(new Instrument(this, lstInstr));
1373     }
1374     lstInstr = lstInstruments->GetNextSubList();
1375     }
1376     }
1377     }
1378    
1379 schoenebeck 800 /** @brief Add a new instrument definition.
1380     *
1381     * This will create a new Instrument object for the DLS file. You have
1382     * to call Save() to make this persistent to the file.
1383     *
1384     * @returns pointer to new Instrument object
1385     */
1386     Instrument* File::AddInstrument() {
1387 schoenebeck 809 if (!pInstruments) LoadInstruments();
1388 schoenebeck 800 __ensureMandatoryChunksExist();
1389     RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1390     RIFF::List* lstInstr = lstInstruments->AddSubList(LIST_TYPE_INS);
1391     Instrument* pInstrument = new Instrument(this, lstInstr);
1392     pInstruments->push_back(pInstrument);
1393     return pInstrument;
1394     }
1395 schoenebeck 2
1396 schoenebeck 809 /** @brief Delete an instrument.
1397 schoenebeck 800 *
1398     * This will delete the given Instrument object from the DLS file. You
1399     * have to call Save() to make this persistent to the file.
1400     *
1401     * @param pInstrument - instrument to delete
1402     */
1403     void File::DeleteInstrument(Instrument* pInstrument) {
1404 schoenebeck 802 if (!pInstruments) return;
1405 schoenebeck 800 InstrumentList::iterator iter = find(pInstruments->begin(), pInstruments->end(), pInstrument);
1406     if (iter == pInstruments->end()) return;
1407     pInstruments->erase(iter);
1408     delete pInstrument;
1409     }
1410 schoenebeck 2
1411 schoenebeck 800 /**
1412     * Apply all the DLS file's current instruments, samples and settings to
1413     * the respective RIFF chunks. You have to call Save() to make changes
1414     * persistent.
1415     *
1416     * @throws Exception - on errors
1417     */
1418     void File::UpdateChunks() {
1419     // first update base class's chunks
1420     Resource::UpdateChunks();
1421    
1422     // if version struct exists, update 'vers' chunk
1423     if (pVersion) {
1424     RIFF::Chunk* ckVersion = pRIFF->GetSubChunk(CHUNK_ID_VERS);
1425     if (!ckVersion) ckVersion = pRIFF->AddSubChunk(CHUNK_ID_VERS, 8);
1426     uint8_t* pData = (uint8_t*) ckVersion->LoadChunkData();
1427 persson 1179 store16(&pData[0], pVersion->minor);
1428     store16(&pData[2], pVersion->major);
1429     store16(&pData[4], pVersion->build);
1430     store16(&pData[6], pVersion->release);
1431 schoenebeck 800 }
1432    
1433     // update 'colh' chunk
1434     Instruments = (pInstruments) ? pInstruments->size() : 0;
1435     RIFF::Chunk* colh = pRIFF->GetSubChunk(CHUNK_ID_COLH);
1436     if (!colh) colh = pRIFF->AddSubChunk(CHUNK_ID_COLH, 4);
1437     uint8_t* pData = (uint8_t*) colh->LoadChunkData();
1438 persson 1179 store32(pData, Instruments);
1439 schoenebeck 800
1440     // update instrument's chunks
1441     if (pInstruments) {
1442     InstrumentList::iterator iter = pInstruments->begin();
1443     InstrumentList::iterator end = pInstruments->end();
1444     for (; iter != end; ++iter) {
1445     (*iter)->UpdateChunks();
1446     }
1447     }
1448    
1449     // update 'ptbl' chunk
1450     const int iSamples = (pSamples) ? pSamples->size() : 0;
1451     const int iPtblOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1452     RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1453     if (!ptbl) ptbl = pRIFF->AddSubChunk(CHUNK_ID_PTBL, 1 /*anything, we'll resize*/);
1454     const int iPtblSize = WavePoolHeaderSize + iPtblOffsetSize * iSamples;
1455     ptbl->Resize(iPtblSize);
1456     pData = (uint8_t*) ptbl->LoadChunkData();
1457     WavePoolCount = iSamples;
1458 persson 1179 store32(&pData[4], WavePoolCount);
1459 schoenebeck 800 // we actually update the sample offsets in the pool table when we Save()
1460     memset(&pData[WavePoolHeaderSize], 0, iPtblSize - WavePoolHeaderSize);
1461    
1462     // update sample's chunks
1463     if (pSamples) {
1464     SampleList::iterator iter = pSamples->begin();
1465     SampleList::iterator end = pSamples->end();
1466     for (; iter != end; ++iter) {
1467     (*iter)->UpdateChunks();
1468     }
1469     }
1470     }
1471    
1472     /** @brief Save changes to another file.
1473     *
1474     * Make all changes persistent by writing them to another file.
1475     * <b>Caution:</b> this method is optimized for writing to
1476     * <b>another</b> file, do not use it to save the changes to the same
1477     * file! Use Save() (without path argument) in that case instead!
1478     * Ignoring this might result in a corrupted file!
1479     *
1480     * After calling this method, this File object will be associated with
1481     * the new file (given by \a Path) afterwards.
1482     *
1483     * @param Path - path and file name where everything should be written to
1484     */
1485     void File::Save(const String& Path) {
1486     UpdateChunks();
1487     pRIFF->Save(Path);
1488     __UpdateWavePoolTableChunk();
1489     }
1490    
1491     /** @brief Save changes to same file.
1492     *
1493     * Make all changes persistent by writing them to the actual (same)
1494     * file. The file might temporarily grow to a higher size than it will
1495     * have at the end of the saving process.
1496     *
1497     * @throws RIFF::Exception if any kind of IO error occured
1498     * @throws DLS::Exception if any kind of DLS specific error occured
1499     */
1500     void File::Save() {
1501     UpdateChunks();
1502     pRIFF->Save();
1503     __UpdateWavePoolTableChunk();
1504     }
1505    
1506     /**
1507     * Checks if all (for DLS) mandatory chunks exist, if not they will be
1508     * created. Note that those chunks will not be made persistent until
1509     * Save() was called.
1510     */
1511     void File::__ensureMandatoryChunksExist() {
1512     // enusre 'lins' list chunk exists (mandatory for instrument definitions)
1513     RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1514     if (!lstInstruments) pRIFF->AddSubList(LIST_TYPE_LINS);
1515     // ensure 'ptbl' chunk exists (mandatory for samples)
1516     RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1517     if (!ptbl) {
1518     const int iOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1519     ptbl = pRIFF->AddSubChunk(CHUNK_ID_PTBL, WavePoolHeaderSize + iOffsetSize);
1520     }
1521     // enusre 'wvpl' list chunk exists (mandatory for samples)
1522     RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1523     if (!wvpl) pRIFF->AddSubList(LIST_TYPE_WVPL);
1524     }
1525    
1526     /**
1527     * Updates (persistently) the wave pool table with offsets to all
1528     * currently available samples. <b>Caution:</b> this method assumes the
1529 schoenebeck 804 * 'ptbl' chunk to be already of the correct size and the file to be
1530     * writable, so usually this method is only called after a Save() call.
1531 schoenebeck 800 *
1532     * @throws Exception - if 'ptbl' chunk is too small (should only occur
1533     * if there's a bug)
1534     */
1535     void File::__UpdateWavePoolTableChunk() {
1536     __UpdateWavePoolTable();
1537     RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1538     const int iOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1539     // check if 'ptbl' chunk is large enough
1540 schoenebeck 802 WavePoolCount = (pSamples) ? pSamples->size() : 0;
1541 schoenebeck 800 const unsigned long ulRequiredSize = WavePoolHeaderSize + iOffsetSize * WavePoolCount;
1542     if (ptbl->GetSize() < ulRequiredSize) throw Exception("Fatal error, 'ptbl' chunk too small");
1543 schoenebeck 804 // save the 'ptbl' chunk's current read/write position
1544     unsigned long ulOriginalPos = ptbl->GetPos();
1545 schoenebeck 800 // update headers
1546 schoenebeck 804 ptbl->SetPos(0);
1547 persson 1179 uint32_t tmp = WavePoolHeaderSize;
1548     ptbl->WriteUint32(&tmp);
1549     tmp = WavePoolCount;
1550     ptbl->WriteUint32(&tmp);
1551 schoenebeck 800 // update offsets
1552 schoenebeck 804 ptbl->SetPos(WavePoolHeaderSize);
1553 schoenebeck 800 if (b64BitWavePoolOffsets) {
1554     for (int i = 0 ; i < WavePoolCount ; i++) {
1555 persson 1179 tmp = pWavePoolTableHi[i];
1556     ptbl->WriteUint32(&tmp);
1557     tmp = pWavePoolTable[i];
1558     ptbl->WriteUint32(&tmp);
1559 schoenebeck 800 }
1560     } else { // conventional 32 bit offsets
1561 persson 1179 for (int i = 0 ; i < WavePoolCount ; i++) {
1562     tmp = pWavePoolTable[i];
1563     ptbl->WriteUint32(&tmp);
1564     }
1565 schoenebeck 800 }
1566 schoenebeck 804 // restore 'ptbl' chunk's original read/write position
1567     ptbl->SetPos(ulOriginalPos);
1568 schoenebeck 800 }
1569    
1570     /**
1571     * Updates the wave pool table with offsets to all currently available
1572     * samples. <b>Caution:</b> this method assumes the 'wvpl' list chunk
1573     * exists already.
1574     */
1575     void File::__UpdateWavePoolTable() {
1576 schoenebeck 802 WavePoolCount = (pSamples) ? pSamples->size() : 0;
1577 schoenebeck 800 // resize wave pool table arrays
1578     if (pWavePoolTable) delete[] pWavePoolTable;
1579     if (pWavePoolTableHi) delete[] pWavePoolTableHi;
1580     pWavePoolTable = new uint32_t[WavePoolCount];
1581     pWavePoolTableHi = new uint32_t[WavePoolCount];
1582 schoenebeck 802 if (!pSamples) return;
1583 schoenebeck 800 // update offsets int wave pool table
1584     RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1585     uint64_t wvplFileOffset = wvpl->GetFilePos();
1586     if (b64BitWavePoolOffsets) {
1587     SampleList::iterator iter = pSamples->begin();
1588     SampleList::iterator end = pSamples->end();
1589     for (int i = 0 ; iter != end ; ++iter, i++) {
1590 schoenebeck 804 uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE;
1591 schoenebeck 800 (*iter)->ulWavePoolOffset = _64BitOffset;
1592     pWavePoolTableHi[i] = (uint32_t) (_64BitOffset >> 32);
1593     pWavePoolTable[i] = (uint32_t) _64BitOffset;
1594     }
1595     } else { // conventional 32 bit offsets
1596     SampleList::iterator iter = pSamples->begin();
1597     SampleList::iterator end = pSamples->end();
1598     for (int i = 0 ; iter != end ; ++iter, i++) {
1599 schoenebeck 804 uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE;
1600 schoenebeck 800 (*iter)->ulWavePoolOffset = _64BitOffset;
1601     pWavePoolTable[i] = (uint32_t) _64BitOffset;
1602     }
1603     }
1604     }
1605    
1606    
1607    
1608 schoenebeck 2 // *************** Exception ***************
1609     // *
1610    
1611     Exception::Exception(String Message) : RIFF::Exception(Message) {
1612     }
1613    
1614     void Exception::PrintMessage() {
1615     std::cout << "DLS::Exception: " << Message << std::endl;
1616     }
1617    
1618 schoenebeck 518
1619     // *************** functions ***************
1620     // *
1621    
1622     /**
1623     * Returns the name of this C++ library. This is usually "libgig" of
1624     * course. This call is equivalent to RIFF::libraryName() and
1625     * gig::libraryName().
1626     */
1627     String libraryName() {
1628     return PACKAGE;
1629     }
1630    
1631     /**
1632     * Returns version of this C++ library. This call is equivalent to
1633     * RIFF::libraryVersion() and gig::libraryVersion().
1634     */
1635     String libraryVersion() {
1636     return VERSION;
1637     }
1638    
1639 schoenebeck 2 } // namespace DLS

  ViewVC Help
Powered by ViewVC