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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3474 - (show annotations) (download)
Wed Feb 20 16:04:19 2019 UTC (5 years, 1 month ago) by schoenebeck
File size: 93230 byte(s)
* WIP: Introduced support for writing extension files (.gx01, .gx02, ...)
  (original patch by Ivan Maguidhir).
* Bumped version (4.1.0.svn11).

1 /***************************************************************************
2 * *
3 * libgig - C++ cross-platform Gigasampler format file access library *
4 * *
5 * Copyright (C) 2003-2019 by Christian Schoenebeck *
6 * <cuse@users.sourceforge.net> *
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 "DLS.h"
25
26 #include <algorithm>
27 #include <vector>
28 #include <time.h>
29
30 #ifdef __APPLE__
31 #include <CoreFoundation/CFUUID.h>
32 #elif defined(HAVE_UUID_UUID_H)
33 #include <uuid/uuid.h>
34 #endif
35
36 #include "helper.h"
37
38 // macros to decode connection transforms
39 #define CONN_TRANSFORM_SRC(x) ((x >> 10) & 0x000F)
40 #define CONN_TRANSFORM_CTL(x) ((x >> 4) & 0x000F)
41 #define CONN_TRANSFORM_DST(x) (x & 0x000F)
42 #define CONN_TRANSFORM_BIPOLAR_SRC(x) (x & 0x4000)
43 #define CONN_TRANSFORM_BIPOLAR_CTL(x) (x & 0x0100)
44 #define CONN_TRANSFORM_INVERT_SRC(x) (x & 0x8000)
45 #define CONN_TRANSFORM_INVERT_CTL(x) (x & 0x0200)
46
47 // macros to encode connection transforms
48 #define CONN_TRANSFORM_SRC_ENCODE(x) ((x & 0x000F) << 10)
49 #define CONN_TRANSFORM_CTL_ENCODE(x) ((x & 0x000F) << 4)
50 #define CONN_TRANSFORM_DST_ENCODE(x) (x & 0x000F)
51 #define CONN_TRANSFORM_BIPOLAR_SRC_ENCODE(x) ((x) ? 0x4000 : 0)
52 #define CONN_TRANSFORM_BIPOLAR_CTL_ENCODE(x) ((x) ? 0x0100 : 0)
53 #define CONN_TRANSFORM_INVERT_SRC_ENCODE(x) ((x) ? 0x8000 : 0)
54 #define CONN_TRANSFORM_INVERT_CTL_ENCODE(x) ((x) ? 0x0200 : 0)
55
56 #define DRUM_TYPE_MASK 0x80000000
57
58 #define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001
59
60 #define F_WAVELINK_PHASE_MASTER 0x0001
61 #define F_WAVELINK_MULTICHANNEL 0x0002
62
63 #define F_WSMP_NO_TRUNCATION 0x0001
64 #define F_WSMP_NO_COMPRESSION 0x0002
65
66 #define MIDI_BANK_COARSE(x) ((x & 0x00007F00) >> 8) // CC0
67 #define MIDI_BANK_FINE(x) (x & 0x0000007F) // CC32
68 #define MIDI_BANK_MERGE(coarse, fine) ((((uint16_t) coarse) << 7) | fine) // CC0 + CC32
69 #define MIDI_BANK_ENCODE(coarse, fine) (((coarse & 0x0000007F) << 8) | (fine & 0x0000007F))
70
71 namespace DLS {
72
73 // *************** Connection ***************
74 // *
75
76 void Connection::Init(conn_block_t* Header) {
77 Source = (conn_src_t) Header->source;
78 Control = (conn_src_t) Header->control;
79 Destination = (conn_dst_t) Header->destination;
80 Scale = Header->scale;
81 SourceTransform = (conn_trn_t) CONN_TRANSFORM_SRC(Header->transform);
82 ControlTransform = (conn_trn_t) CONN_TRANSFORM_CTL(Header->transform);
83 DestinationTransform = (conn_trn_t) CONN_TRANSFORM_DST(Header->transform);
84 SourceInvert = CONN_TRANSFORM_INVERT_SRC(Header->transform);
85 SourceBipolar = CONN_TRANSFORM_BIPOLAR_SRC(Header->transform);
86 ControlInvert = CONN_TRANSFORM_INVERT_CTL(Header->transform);
87 ControlBipolar = CONN_TRANSFORM_BIPOLAR_CTL(Header->transform);
88 }
89
90 Connection::conn_block_t Connection::ToConnBlock() {
91 conn_block_t c;
92 c.source = Source;
93 c.control = Control;
94 c.destination = Destination;
95 c.scale = Scale;
96 c.transform = CONN_TRANSFORM_SRC_ENCODE(SourceTransform) |
97 CONN_TRANSFORM_CTL_ENCODE(ControlTransform) |
98 CONN_TRANSFORM_DST_ENCODE(DestinationTransform) |
99 CONN_TRANSFORM_INVERT_SRC_ENCODE(SourceInvert) |
100 CONN_TRANSFORM_BIPOLAR_SRC_ENCODE(SourceBipolar) |
101 CONN_TRANSFORM_INVERT_CTL_ENCODE(ControlInvert) |
102 CONN_TRANSFORM_BIPOLAR_CTL_ENCODE(ControlBipolar);
103 return c;
104 }
105
106
107
108 // *************** Articulation ***************
109 // *
110
111 /** @brief Constructor.
112 *
113 * Expects an 'artl' or 'art2' chunk to be given where the articulation
114 * connections will be read from.
115 *
116 * @param artl - pointer to an 'artl' or 'art2' chunk
117 * @throws Exception if no 'artl' or 'art2' chunk was given
118 */
119 Articulation::Articulation(RIFF::Chunk* artl) {
120 pArticulationCk = artl;
121 if (artl->GetChunkID() != CHUNK_ID_ART2 &&
122 artl->GetChunkID() != CHUNK_ID_ARTL) {
123 throw DLS::Exception("<artl-ck> or <art2-ck> chunk expected");
124 }
125 HeaderSize = artl->ReadUint32();
126 Connections = artl->ReadUint32();
127 artl->SetPos(HeaderSize);
128
129 pConnections = new Connection[Connections];
130 Connection::conn_block_t connblock;
131 for (uint32_t i = 0; i < Connections; i++) {
132 artl->Read(&connblock.source, 1, 2);
133 artl->Read(&connblock.control, 1, 2);
134 artl->Read(&connblock.destination, 1, 2);
135 artl->Read(&connblock.transform, 1, 2);
136 artl->Read(&connblock.scale, 1, 4);
137 pConnections[i].Init(&connblock);
138 }
139 }
140
141 Articulation::~Articulation() {
142 if (pConnections) delete[] pConnections;
143 }
144
145 /**
146 * Apply articulation connections to the respective RIFF chunks. You
147 * have to call File::Save() to make changes persistent.
148 *
149 * @param pProgress - callback function for progress notification
150 */
151 void Articulation::UpdateChunks(progress_t* pProgress) {
152 const int iEntrySize = 12; // 12 bytes per connection block
153 pArticulationCk->Resize(HeaderSize + Connections * iEntrySize);
154 uint8_t* pData = (uint8_t*) pArticulationCk->LoadChunkData();
155 store16(&pData[0], HeaderSize);
156 store16(&pData[2], Connections);
157 for (uint32_t i = 0; i < Connections; i++) {
158 Connection::conn_block_t c = pConnections[i].ToConnBlock();
159 store16(&pData[HeaderSize + i * iEntrySize], c.source);
160 store16(&pData[HeaderSize + i * iEntrySize + 2], c.control);
161 store16(&pData[HeaderSize + i * iEntrySize + 4], c.destination);
162 store16(&pData[HeaderSize + i * iEntrySize + 6], c.transform);
163 store32(&pData[HeaderSize + i * iEntrySize + 8], c.scale);
164 }
165 }
166
167
168
169 // *************** Articulator ***************
170 // *
171
172 Articulator::Articulator(RIFF::List* ParentList) {
173 pParentList = ParentList;
174 pArticulations = NULL;
175 }
176
177 Articulation* Articulator::GetFirstArticulation() {
178 if (!pArticulations) LoadArticulations();
179 if (!pArticulations) return NULL;
180 ArticulationsIterator = pArticulations->begin();
181 return (ArticulationsIterator != pArticulations->end()) ? *ArticulationsIterator : NULL;
182 }
183
184 Articulation* Articulator::GetNextArticulation() {
185 if (!pArticulations) return NULL;
186 ArticulationsIterator++;
187 return (ArticulationsIterator != pArticulations->end()) ? *ArticulationsIterator : NULL;
188 }
189
190 void Articulator::LoadArticulations() {
191 // prefer articulation level 2
192 RIFF::List* lart = pParentList->GetSubList(LIST_TYPE_LAR2);
193 if (!lart) lart = pParentList->GetSubList(LIST_TYPE_LART);
194 if (lart) {
195 uint32_t artCkType = (lart->GetListType() == LIST_TYPE_LAR2) ? CHUNK_ID_ART2
196 : CHUNK_ID_ARTL;
197 RIFF::Chunk* art = lart->GetFirstSubChunk();
198 while (art) {
199 if (art->GetChunkID() == artCkType) {
200 if (!pArticulations) pArticulations = new ArticulationList;
201 pArticulations->push_back(new Articulation(art));
202 }
203 art = lart->GetNextSubChunk();
204 }
205 }
206 }
207
208 Articulator::~Articulator() {
209 if (pArticulations) {
210 ArticulationList::iterator iter = pArticulations->begin();
211 ArticulationList::iterator end = pArticulations->end();
212 while (iter != end) {
213 delete *iter;
214 iter++;
215 }
216 delete pArticulations;
217 }
218 }
219
220 /**
221 * Apply all articulations to the respective RIFF chunks. You have to
222 * call File::Save() to make changes persistent.
223 *
224 * @param pProgress - callback function for progress notification
225 */
226 void Articulator::UpdateChunks(progress_t* pProgress) {
227 if (pArticulations) {
228 ArticulationList::iterator iter = pArticulations->begin();
229 ArticulationList::iterator end = pArticulations->end();
230 for (; iter != end; ++iter) {
231 (*iter)->UpdateChunks(pProgress);
232 }
233 }
234 }
235
236 /**
237 * Not yet implemented in this version, since the .gig format does
238 * not need to copy DLS articulators and so far nobody used pure
239 * DLS instrument AFAIK.
240 */
241 void Articulator::CopyAssign(const Articulator* orig) {
242 //TODO: implement deep copy assignment for this class
243 }
244
245
246
247 // *************** Info ***************
248 // *
249
250 /** @brief Constructor.
251 *
252 * Initializes the info strings with values provided by an INFO list chunk.
253 *
254 * @param list - pointer to a list chunk which contains an INFO list chunk
255 */
256 Info::Info(RIFF::List* list) {
257 pFixedStringLengths = NULL;
258 pResourceListChunk = list;
259 if (list) {
260 RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO);
261 if (lstINFO) {
262 LoadString(CHUNK_ID_INAM, lstINFO, Name);
263 LoadString(CHUNK_ID_IARL, lstINFO, ArchivalLocation);
264 LoadString(CHUNK_ID_ICRD, lstINFO, CreationDate);
265 LoadString(CHUNK_ID_ICMT, lstINFO, Comments);
266 LoadString(CHUNK_ID_IPRD, lstINFO, Product);
267 LoadString(CHUNK_ID_ICOP, lstINFO, Copyright);
268 LoadString(CHUNK_ID_IART, lstINFO, Artists);
269 LoadString(CHUNK_ID_IGNR, lstINFO, Genre);
270 LoadString(CHUNK_ID_IKEY, lstINFO, Keywords);
271 LoadString(CHUNK_ID_IENG, lstINFO, Engineer);
272 LoadString(CHUNK_ID_ITCH, lstINFO, Technician);
273 LoadString(CHUNK_ID_ISFT, lstINFO, Software);
274 LoadString(CHUNK_ID_IMED, lstINFO, Medium);
275 LoadString(CHUNK_ID_ISRC, lstINFO, Source);
276 LoadString(CHUNK_ID_ISRF, lstINFO, SourceForm);
277 LoadString(CHUNK_ID_ICMS, lstINFO, Commissioned);
278 LoadString(CHUNK_ID_ISBJ, lstINFO, Subject);
279 }
280 }
281 }
282
283 Info::~Info() {
284 }
285
286 /**
287 * Forces specific Info fields to be of a fixed length when being saved
288 * to a file. By default the respective RIFF chunk of an Info field
289 * will have a size analogue to its actual string length. With this
290 * method however this behavior can be overridden, allowing to force an
291 * arbitrary fixed size individually for each Info field.
292 *
293 * This method is used as a workaround for the gig format, not for DLS.
294 *
295 * @param lengths - NULL terminated array of string_length_t elements
296 */
297 void Info::SetFixedStringLengths(const string_length_t* lengths) {
298 pFixedStringLengths = lengths;
299 }
300
301 /** @brief Load given INFO field.
302 *
303 * Load INFO field from INFO chunk with chunk ID \a ChunkID from INFO
304 * list chunk \a lstINFO and save value to \a s.
305 */
306 void Info::LoadString(uint32_t ChunkID, RIFF::List* lstINFO, String& s) {
307 RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
308 ::LoadString(ck, s); // function from helper.h
309 }
310
311 /** @brief Apply given INFO field to the respective chunk.
312 *
313 * Apply given info value to info chunk with ID \a ChunkID, which is a
314 * subchunk of INFO list chunk \a lstINFO. If the given chunk already
315 * exists, value \a s will be applied. Otherwise if it doesn't exist yet
316 * and either \a s or \a sDefault is not an empty string, such a chunk
317 * will be created and either \a s or \a sDefault will be applied
318 * (depending on which one is not an empty string, if both are not an
319 * empty string \a s will be preferred).
320 *
321 * @param ChunkID - 32 bit RIFF chunk ID of INFO subchunk
322 * @param lstINFO - parent (INFO) RIFF list chunk
323 * @param s - current value of info field
324 * @param sDefault - default value
325 */
326 void Info::SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault) {
327 int size = 0;
328 if (pFixedStringLengths) {
329 for (int i = 0 ; pFixedStringLengths[i].length ; i++) {
330 if (pFixedStringLengths[i].chunkId == ChunkID) {
331 size = pFixedStringLengths[i].length;
332 break;
333 }
334 }
335 }
336 RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
337 ::SaveString(ChunkID, ck, lstINFO, s, sDefault, size != 0, size); // function from helper.h
338 }
339
340 /** @brief Update chunks with current info values.
341 *
342 * Apply current INFO field values to the respective INFO chunks. You
343 * have to call File::Save() to make changes persistent.
344 *
345 * @param pProgress - callback function for progress notification
346 */
347 void Info::UpdateChunks(progress_t* pProgress) {
348 if (!pResourceListChunk) return;
349
350 // make sure INFO list chunk exists
351 RIFF::List* lstINFO = pResourceListChunk->GetSubList(LIST_TYPE_INFO);
352
353 String defaultName = "";
354 String defaultCreationDate = "";
355 String defaultSoftware = "";
356 String defaultComments = "";
357
358 uint32_t resourceType = pResourceListChunk->GetListType();
359
360 if (!lstINFO) {
361 lstINFO = pResourceListChunk->AddSubList(LIST_TYPE_INFO);
362
363 // assemble default values
364 defaultName = "NONAME";
365
366 if (resourceType == RIFF_TYPE_DLS) {
367 // get current date
368 time_t now = time(NULL);
369 tm* pNowBroken = localtime(&now);
370 char buf[11];
371 strftime(buf, 11, "%F", pNowBroken);
372 defaultCreationDate = buf;
373
374 defaultComments = "Created with " + libraryName() + " " + libraryVersion();
375 }
376 if (resourceType == RIFF_TYPE_DLS || resourceType == LIST_TYPE_INS)
377 {
378 defaultSoftware = libraryName() + " " + libraryVersion();
379 }
380 }
381
382 // save values
383
384 SaveString(CHUNK_ID_IARL, lstINFO, ArchivalLocation, String(""));
385 SaveString(CHUNK_ID_IART, lstINFO, Artists, String(""));
386 SaveString(CHUNK_ID_ICMS, lstINFO, Commissioned, String(""));
387 SaveString(CHUNK_ID_ICMT, lstINFO, Comments, defaultComments);
388 SaveString(CHUNK_ID_ICOP, lstINFO, Copyright, String(""));
389 SaveString(CHUNK_ID_ICRD, lstINFO, CreationDate, defaultCreationDate);
390 SaveString(CHUNK_ID_IENG, lstINFO, Engineer, String(""));
391 SaveString(CHUNK_ID_IGNR, lstINFO, Genre, String(""));
392 SaveString(CHUNK_ID_IKEY, lstINFO, Keywords, String(""));
393 SaveString(CHUNK_ID_IMED, lstINFO, Medium, String(""));
394 SaveString(CHUNK_ID_INAM, lstINFO, Name, defaultName);
395 SaveString(CHUNK_ID_IPRD, lstINFO, Product, String(""));
396 SaveString(CHUNK_ID_ISBJ, lstINFO, Subject, String(""));
397 SaveString(CHUNK_ID_ISFT, lstINFO, Software, defaultSoftware);
398 SaveString(CHUNK_ID_ISRC, lstINFO, Source, String(""));
399 SaveString(CHUNK_ID_ISRF, lstINFO, SourceForm, String(""));
400 SaveString(CHUNK_ID_ITCH, lstINFO, Technician, String(""));
401 }
402
403 /**
404 * Make a deep copy of the Info object given by @a orig and assign it to
405 * this object.
406 *
407 * @param orig - original Info object to be copied from
408 */
409 void Info::CopyAssign(const Info* orig) {
410 Name = orig->Name;
411 ArchivalLocation = orig->ArchivalLocation;
412 CreationDate = orig->CreationDate;
413 Comments = orig->Comments;
414 Product = orig->Product;
415 Copyright = orig->Copyright;
416 Artists = orig->Artists;
417 Genre = orig->Genre;
418 Keywords = orig->Keywords;
419 Engineer = orig->Engineer;
420 Technician = orig->Technician;
421 Software = orig->Software;
422 Medium = orig->Medium;
423 Source = orig->Source;
424 SourceForm = orig->SourceForm;
425 Commissioned = orig->Commissioned;
426 Subject = orig->Subject;
427 //FIXME: hmm, is copying this pointer a good idea?
428 pFixedStringLengths = orig->pFixedStringLengths;
429 }
430
431
432
433 // *************** Resource ***************
434 // *
435
436 /** @brief Constructor.
437 *
438 * Initializes the 'Resource' object with values provided by a given
439 * INFO list chunk and a DLID chunk (the latter optional).
440 *
441 * @param Parent - pointer to parent 'Resource', NULL if this is
442 * the toplevel 'Resource' object
443 * @param lstResource - pointer to an INFO list chunk
444 */
445 Resource::Resource(Resource* Parent, RIFF::List* lstResource) {
446 pParent = Parent;
447 pResourceList = lstResource;
448
449 pInfo = new Info(lstResource);
450
451 RIFF::Chunk* ckDLSID = lstResource->GetSubChunk(CHUNK_ID_DLID);
452 if (ckDLSID) {
453 pDLSID = new dlsid_t;
454 ckDLSID->Read(&pDLSID->ulData1, 1, 4);
455 ckDLSID->Read(&pDLSID->usData2, 1, 2);
456 ckDLSID->Read(&pDLSID->usData3, 1, 2);
457 ckDLSID->Read(pDLSID->abData, 8, 1);
458 }
459 else pDLSID = NULL;
460 }
461
462 Resource::~Resource() {
463 if (pDLSID) delete pDLSID;
464 if (pInfo) delete pInfo;
465 }
466
467 /** @brief Update chunks with current Resource data.
468 *
469 * Apply Resource data persistently below the previously given resource
470 * list chunk. This will currently only include the INFO data. The DLSID
471 * will not be applied at the moment (yet).
472 *
473 * You have to call File::Save() to make changes persistent.
474 *
475 * @param pProgress - callback function for progress notification
476 */
477 void Resource::UpdateChunks(progress_t* pProgress) {
478 pInfo->UpdateChunks(pProgress);
479
480 if (pDLSID) {
481 // make sure 'dlid' chunk exists
482 RIFF::Chunk* ckDLSID = pResourceList->GetSubChunk(CHUNK_ID_DLID);
483 if (!ckDLSID) ckDLSID = pResourceList->AddSubChunk(CHUNK_ID_DLID, 16);
484 uint8_t* pData = (uint8_t*)ckDLSID->LoadChunkData();
485 // update 'dlid' chunk
486 store32(&pData[0], pDLSID->ulData1);
487 store16(&pData[4], pDLSID->usData2);
488 store16(&pData[6], pDLSID->usData3);
489 memcpy(&pData[8], pDLSID->abData, 8);
490 }
491 }
492
493 /**
494 * Generates a new DLSID for the resource.
495 */
496 void Resource::GenerateDLSID() {
497 #if defined(WIN32) || defined(__APPLE__) || defined(HAVE_UUID_GENERATE)
498 if (!pDLSID) pDLSID = new dlsid_t;
499 GenerateDLSID(pDLSID);
500 #endif
501 }
502
503 void Resource::GenerateDLSID(dlsid_t* pDLSID) {
504 #if defined(WIN32) || defined(__APPLE__) || defined(HAVE_UUID_GENERATE)
505 #ifdef WIN32
506 UUID uuid;
507 UuidCreate(&uuid);
508 pDLSID->ulData1 = uuid.Data1;
509 pDLSID->usData2 = uuid.Data2;
510 pDLSID->usData3 = uuid.Data3;
511 memcpy(pDLSID->abData, uuid.Data4, 8);
512
513 #elif defined(__APPLE__)
514
515 CFUUIDRef uuidRef = CFUUIDCreate(NULL);
516 CFUUIDBytes uuid = CFUUIDGetUUIDBytes(uuidRef);
517 CFRelease(uuidRef);
518 pDLSID->ulData1 = uuid.byte0 | uuid.byte1 << 8 | uuid.byte2 << 16 | uuid.byte3 << 24;
519 pDLSID->usData2 = uuid.byte4 | uuid.byte5 << 8;
520 pDLSID->usData3 = uuid.byte6 | uuid.byte7 << 8;
521 pDLSID->abData[0] = uuid.byte8;
522 pDLSID->abData[1] = uuid.byte9;
523 pDLSID->abData[2] = uuid.byte10;
524 pDLSID->abData[3] = uuid.byte11;
525 pDLSID->abData[4] = uuid.byte12;
526 pDLSID->abData[5] = uuid.byte13;
527 pDLSID->abData[6] = uuid.byte14;
528 pDLSID->abData[7] = uuid.byte15;
529 #else
530 uuid_t uuid;
531 uuid_generate(uuid);
532 pDLSID->ulData1 = uuid[0] | uuid[1] << 8 | uuid[2] << 16 | uuid[3] << 24;
533 pDLSID->usData2 = uuid[4] | uuid[5] << 8;
534 pDLSID->usData3 = uuid[6] | uuid[7] << 8;
535 memcpy(pDLSID->abData, &uuid[8], 8);
536 #endif
537 #endif
538 }
539
540 /**
541 * Make a deep copy of the Resource object given by @a orig and assign it
542 * to this object.
543 *
544 * @param orig - original Resource object to be copied from
545 */
546 void Resource::CopyAssign(const Resource* orig) {
547 pInfo->CopyAssign(orig->pInfo);
548 }
549
550
551 // *************** Sampler ***************
552 // *
553
554 Sampler::Sampler(RIFF::List* ParentList) {
555 pParentList = ParentList;
556 RIFF::Chunk* wsmp = ParentList->GetSubChunk(CHUNK_ID_WSMP);
557 if (wsmp) {
558 uiHeaderSize = wsmp->ReadUint32();
559 UnityNote = wsmp->ReadUint16();
560 FineTune = wsmp->ReadInt16();
561 Gain = wsmp->ReadInt32();
562 SamplerOptions = wsmp->ReadUint32();
563 SampleLoops = wsmp->ReadUint32();
564 } else { // 'wsmp' chunk missing
565 uiHeaderSize = 20;
566 UnityNote = 60;
567 FineTune = 0; // +- 0 cents
568 Gain = 0; // 0 dB
569 SamplerOptions = F_WSMP_NO_COMPRESSION;
570 SampleLoops = 0;
571 }
572 NoSampleDepthTruncation = SamplerOptions & F_WSMP_NO_TRUNCATION;
573 NoSampleCompression = SamplerOptions & F_WSMP_NO_COMPRESSION;
574 pSampleLoops = (SampleLoops) ? new sample_loop_t[SampleLoops] : NULL;
575 if (SampleLoops) {
576 wsmp->SetPos(uiHeaderSize);
577 for (uint32_t i = 0; i < SampleLoops; i++) {
578 wsmp->Read(pSampleLoops + i, 4, 4);
579 if (pSampleLoops[i].Size > sizeof(sample_loop_t)) { // if loop struct was extended
580 wsmp->SetPos(pSampleLoops[i].Size - sizeof(sample_loop_t), RIFF::stream_curpos);
581 }
582 }
583 }
584 }
585
586 Sampler::~Sampler() {
587 if (pSampleLoops) delete[] pSampleLoops;
588 }
589
590 void Sampler::SetGain(int32_t gain) {
591 Gain = gain;
592 }
593
594 /**
595 * Apply all sample player options to the respective RIFF chunk. You
596 * have to call File::Save() to make changes persistent.
597 *
598 * @param pProgress - callback function for progress notification
599 */
600 void Sampler::UpdateChunks(progress_t* pProgress) {
601 // make sure 'wsmp' chunk exists
602 RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP);
603 int wsmpSize = uiHeaderSize + SampleLoops * 16;
604 if (!wsmp) {
605 wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, wsmpSize);
606 } else if (wsmp->GetSize() != wsmpSize) {
607 wsmp->Resize(wsmpSize);
608 }
609 uint8_t* pData = (uint8_t*) wsmp->LoadChunkData();
610 // update headers size
611 store32(&pData[0], uiHeaderSize);
612 // update respective sampler options bits
613 SamplerOptions = (NoSampleDepthTruncation) ? SamplerOptions | F_WSMP_NO_TRUNCATION
614 : SamplerOptions & (~F_WSMP_NO_TRUNCATION);
615 SamplerOptions = (NoSampleCompression) ? SamplerOptions | F_WSMP_NO_COMPRESSION
616 : SamplerOptions & (~F_WSMP_NO_COMPRESSION);
617 store16(&pData[4], UnityNote);
618 store16(&pData[6], FineTune);
619 store32(&pData[8], Gain);
620 store32(&pData[12], SamplerOptions);
621 store32(&pData[16], SampleLoops);
622 // update loop definitions
623 for (uint32_t i = 0; i < SampleLoops; i++) {
624 //FIXME: this does not handle extended loop structs correctly
625 store32(&pData[uiHeaderSize + i * 16], pSampleLoops[i].Size);
626 store32(&pData[uiHeaderSize + i * 16 + 4], pSampleLoops[i].LoopType);
627 store32(&pData[uiHeaderSize + i * 16 + 8], pSampleLoops[i].LoopStart);
628 store32(&pData[uiHeaderSize + i * 16 + 12], pSampleLoops[i].LoopLength);
629 }
630 }
631
632 /**
633 * Adds a new sample loop with the provided loop definition.
634 *
635 * @param pLoopDef - points to a loop definition that is to be copied
636 */
637 void Sampler::AddSampleLoop(sample_loop_t* pLoopDef) {
638 sample_loop_t* pNewLoops = new sample_loop_t[SampleLoops + 1];
639 // copy old loops array
640 for (int i = 0; i < SampleLoops; i++) {
641 pNewLoops[i] = pSampleLoops[i];
642 }
643 // add the new loop
644 pNewLoops[SampleLoops] = *pLoopDef;
645 // auto correct size field
646 pNewLoops[SampleLoops].Size = sizeof(DLS::sample_loop_t);
647 // free the old array and update the member variables
648 if (SampleLoops) delete[] pSampleLoops;
649 pSampleLoops = pNewLoops;
650 SampleLoops++;
651 }
652
653 /**
654 * Deletes an existing sample loop.
655 *
656 * @param pLoopDef - pointer to existing loop definition
657 * @throws Exception - if given loop definition does not exist
658 */
659 void Sampler::DeleteSampleLoop(sample_loop_t* pLoopDef) {
660 sample_loop_t* pNewLoops = new sample_loop_t[SampleLoops - 1];
661 // copy old loops array (skipping given loop)
662 for (int i = 0, o = 0; i < SampleLoops; i++) {
663 if (&pSampleLoops[i] == pLoopDef) continue;
664 if (o == SampleLoops - 1) {
665 delete[] pNewLoops;
666 throw Exception("Could not delete Sample Loop, because it does not exist");
667 }
668 pNewLoops[o] = pSampleLoops[i];
669 o++;
670 }
671 // free the old array and update the member variables
672 if (SampleLoops) delete[] pSampleLoops;
673 pSampleLoops = pNewLoops;
674 SampleLoops--;
675 }
676
677 /**
678 * Make a deep copy of the Sampler object given by @a orig and assign it
679 * to this object.
680 *
681 * @param orig - original Sampler object to be copied from
682 */
683 void Sampler::CopyAssign(const Sampler* orig) {
684 // copy trivial scalars
685 UnityNote = orig->UnityNote;
686 FineTune = orig->FineTune;
687 Gain = orig->Gain;
688 NoSampleDepthTruncation = orig->NoSampleDepthTruncation;
689 NoSampleCompression = orig->NoSampleCompression;
690 SamplerOptions = orig->SamplerOptions;
691
692 // copy sample loops
693 if (SampleLoops) delete[] pSampleLoops;
694 pSampleLoops = new sample_loop_t[orig->SampleLoops];
695 memcpy(pSampleLoops, orig->pSampleLoops, orig->SampleLoops * sizeof(sample_loop_t));
696 SampleLoops = orig->SampleLoops;
697 }
698
699
700 // *************** Sample ***************
701 // *
702
703 /** @brief Constructor.
704 *
705 * Load an existing sample or create a new one. A 'wave' list chunk must
706 * be given to this constructor. In case the given 'wave' list chunk
707 * contains a 'fmt' and 'data' chunk, the format and sample data will be
708 * loaded from there, otherwise default values will be used and those
709 * chunks will be created when File::Save() will be called later on.
710 *
711 * @param pFile - pointer to DLS::File where this sample is
712 * located (or will be located)
713 * @param waveList - pointer to 'wave' list chunk which is (or
714 * will be) associated with this sample
715 * @param WavePoolOffset - offset of this sample data from wave pool
716 * ('wvpl') list chunk
717 */
718 Sample::Sample(File* pFile, RIFF::List* waveList, file_offset_t WavePoolOffset) : Resource(pFile, waveList) {
719 pWaveList = waveList;
720 ullWavePoolOffset = WavePoolOffset - LIST_HEADER_SIZE(waveList->GetFile()->GetFileOffsetSize());
721 pCkFormat = waveList->GetSubChunk(CHUNK_ID_FMT);
722 pCkData = waveList->GetSubChunk(CHUNK_ID_DATA);
723 if (pCkFormat) {
724 // common fields
725 FormatTag = pCkFormat->ReadUint16();
726 Channels = pCkFormat->ReadUint16();
727 SamplesPerSecond = pCkFormat->ReadUint32();
728 AverageBytesPerSecond = pCkFormat->ReadUint32();
729 BlockAlign = pCkFormat->ReadUint16();
730 // PCM format specific
731 if (FormatTag == DLS_WAVE_FORMAT_PCM) {
732 BitDepth = pCkFormat->ReadUint16();
733 FrameSize = (BitDepth / 8) * Channels;
734 } else { // unsupported sample data format
735 BitDepth = 0;
736 FrameSize = 0;
737 }
738 } else { // 'fmt' chunk missing
739 FormatTag = DLS_WAVE_FORMAT_PCM;
740 BitDepth = 16;
741 Channels = 1;
742 SamplesPerSecond = 44100;
743 AverageBytesPerSecond = (BitDepth / 8) * SamplesPerSecond * Channels;
744 FrameSize = (BitDepth / 8) * Channels;
745 BlockAlign = FrameSize;
746 }
747 SamplesTotal = (pCkData) ? (FormatTag == DLS_WAVE_FORMAT_PCM) ? pCkData->GetSize() / FrameSize
748 : 0
749 : 0;
750 }
751
752 /** @brief Destructor.
753 *
754 * Removes RIFF chunks associated with this Sample and frees all
755 * memory occupied by this sample.
756 */
757 Sample::~Sample() {
758 RIFF::List* pParent = pWaveList->GetParent();
759 pParent->DeleteSubChunk(pWaveList);
760 }
761
762 /**
763 * Make a deep copy of the Sample object given by @a orig (without the
764 * actual sample waveform data however) and assign it to this object.
765 *
766 * This is a special internal variant of CopyAssign() which only copies the
767 * most mandatory member variables. It will be called by gig::Sample
768 * descendent instead of CopyAssign() since gig::Sample has its own
769 * implementation to access and copy the actual sample waveform data.
770 *
771 * @param orig - original Sample object to be copied from
772 */
773 void Sample::CopyAssignCore(const Sample* orig) {
774 // handle base classes
775 Resource::CopyAssign(orig);
776 // handle actual own attributes of this class
777 FormatTag = orig->FormatTag;
778 Channels = orig->Channels;
779 SamplesPerSecond = orig->SamplesPerSecond;
780 AverageBytesPerSecond = orig->AverageBytesPerSecond;
781 BlockAlign = orig->BlockAlign;
782 BitDepth = orig->BitDepth;
783 SamplesTotal = orig->SamplesTotal;
784 FrameSize = orig->FrameSize;
785 }
786
787 /**
788 * Make a deep copy of the Sample object given by @a orig and assign it to
789 * this object.
790 *
791 * @param orig - original Sample object to be copied from
792 */
793 void Sample::CopyAssign(const Sample* orig) {
794 CopyAssignCore(orig);
795
796 // copy sample waveform data (reading directly from disc)
797 Resize(orig->GetSize());
798 char* buf = (char*) LoadSampleData();
799 Sample* pOrig = (Sample*) orig; //HACK: circumventing the constness here for now
800 const file_offset_t restorePos = pOrig->pCkData->GetPos();
801 pOrig->SetPos(0);
802 for (file_offset_t todo = pOrig->GetSize(), i = 0; todo; ) {
803 const int iReadAtOnce = 64*1024;
804 file_offset_t n = (iReadAtOnce < todo) ? iReadAtOnce : todo;
805 n = pOrig->Read(&buf[i], n);
806 if (!n) break;
807 todo -= n;
808 i += (n * pOrig->FrameSize);
809 }
810 pOrig->pCkData->SetPos(restorePos);
811 }
812
813 /** @brief Load sample data into RAM.
814 *
815 * In case the respective 'data' chunk exists, the sample data will be
816 * loaded into RAM (if not done already) and a pointer to the data in
817 * RAM will be returned. If this is a new sample, you have to call
818 * Resize() with the desired sample size to create the mandatory RIFF
819 * chunk for the sample wave data.
820 *
821 * You can call LoadChunkData() again if you previously scheduled to
822 * enlarge the sample data RIFF chunk with a Resize() call. In that case
823 * the buffer will be enlarged to the new, scheduled size and you can
824 * already place the sample wave data to the buffer and finally call
825 * File::Save() to enlarge the sample data's chunk physically and write
826 * the new sample wave data in one rush. This approach is definitely
827 * recommended if you have to enlarge and write new sample data to a lot
828 * of samples.
829 *
830 * <b>Caution:</b> the buffer pointer will be invalidated once
831 * File::Save() was called. You have to call LoadChunkData() again to
832 * get a new, valid pointer whenever File::Save() was called.
833 *
834 * @returns pointer to sample data in RAM, NULL in case respective
835 * 'data' chunk does not exist (yet)
836 * @throws Exception if data buffer could not be enlarged
837 * @see Resize(), File::Save()
838 */
839 void* Sample::LoadSampleData() {
840 return (pCkData) ? pCkData->LoadChunkData() : NULL;
841 }
842
843 /** @brief Free sample data from RAM.
844 *
845 * In case sample data was previously successfully loaded into RAM with
846 * LoadSampleData(), this method will free the sample data from RAM.
847 */
848 void Sample::ReleaseSampleData() {
849 if (pCkData) pCkData->ReleaseChunkData();
850 }
851
852 /** @brief Returns sample size.
853 *
854 * Returns the sample wave form's data size (in sample points). This is
855 * actually the current, physical size (converted to sample points) of
856 * the RIFF chunk which encapsulates the sample's wave data. The
857 * returned value is dependant to the current FrameSize value.
858 *
859 * @returns number of sample points or 0 if FormatTag != DLS_WAVE_FORMAT_PCM
860 * @see FrameSize, FormatTag
861 */
862 file_offset_t Sample::GetSize() const {
863 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0;
864 return (pCkData) ? pCkData->GetSize() / FrameSize : 0;
865 }
866
867 /** @brief Resize sample.
868 *
869 * Resizes the sample's wave form data, that is the actual size of
870 * sample wave data possible to be written for this sample. This call
871 * will return immediately and just schedule the resize operation. You
872 * should call File::Save() to actually perform the resize operation(s)
873 * "physically" to the file. As this can take a while on large files, it
874 * is recommended to call Resize() first on all samples which have to be
875 * resized and finally to call File::Save() to perform all those resize
876 * operations in one rush.
877 *
878 * The actual size (in bytes) is dependant to the current FrameSize
879 * value. You may want to set FrameSize before calling Resize().
880 *
881 * <b>Caution:</b> You cannot directly write to enlarged samples before
882 * calling File::Save() as this might exceed the current sample's
883 * boundary!
884 *
885 * Also note: only DLS_WAVE_FORMAT_PCM is currently supported, that is
886 * FormatTag must be DLS_WAVE_FORMAT_PCM. Trying to resize samples with
887 * other formats will fail!
888 *
889 * @param NewSize - new sample wave data size in sample points (must be
890 * greater than zero)
891 * @throws Exception if FormatTag != DLS_WAVE_FORMAT_PCM
892 * @throws Exception if \a NewSize is less than 1 or unrealistic large
893 * @see File::Save(), FrameSize, FormatTag
894 */
895 void Sample::Resize(file_offset_t NewSize) {
896 if (FormatTag != DLS_WAVE_FORMAT_PCM) throw Exception("Sample's format is not DLS_WAVE_FORMAT_PCM");
897 if (NewSize < 1) throw Exception("Sample size must be at least one sample point");
898 if ((NewSize >> 48) != 0)
899 throw Exception("Unrealistic high DLS sample size detected");
900 const file_offset_t sizeInBytes = NewSize * FrameSize;
901 pCkData = pWaveList->GetSubChunk(CHUNK_ID_DATA);
902 if (pCkData) pCkData->Resize(sizeInBytes);
903 else pCkData = pWaveList->AddSubChunk(CHUNK_ID_DATA, sizeInBytes);
904 }
905
906 /**
907 * Sets the position within the sample (in sample points, not in
908 * bytes). Use this method and <i>Read()</i> if you don't want to load
909 * the sample into RAM, thus for disk streaming.
910 *
911 * Also note: only DLS_WAVE_FORMAT_PCM is currently supported, that is
912 * FormatTag must be DLS_WAVE_FORMAT_PCM. Trying to reposition the sample
913 * with other formats will fail!
914 *
915 * @param SampleCount number of sample points
916 * @param Whence to which relation \a SampleCount refers to
917 * @returns new position within the sample, 0 if
918 * FormatTag != DLS_WAVE_FORMAT_PCM
919 * @throws Exception if no data RIFF chunk was created for the sample yet
920 * @see FrameSize, FormatTag
921 */
922 file_offset_t Sample::SetPos(file_offset_t SampleCount, RIFF::stream_whence_t Whence) {
923 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
924 if (!pCkData) throw Exception("No data chunk created for sample yet, call Sample::Resize() to create one");
925 file_offset_t orderedBytes = SampleCount * FrameSize;
926 file_offset_t result = pCkData->SetPos(orderedBytes, Whence);
927 return (result == orderedBytes) ? SampleCount
928 : result / FrameSize;
929 }
930
931 /**
932 * Reads \a SampleCount number of sample points from the current
933 * position into the buffer pointed by \a pBuffer and increments the
934 * position within the sample. Use this method and <i>SetPos()</i> if you
935 * don't want to load the sample into RAM, thus for disk streaming.
936 *
937 * @param pBuffer destination buffer
938 * @param SampleCount number of sample points to read
939 */
940 file_offset_t Sample::Read(void* pBuffer, file_offset_t SampleCount) {
941 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
942 return pCkData->Read(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?
943 }
944
945 /** @brief Write sample wave data.
946 *
947 * Writes \a SampleCount number of sample points from the buffer pointed
948 * by \a pBuffer and increments the position within the sample. Use this
949 * method to directly write the sample data to disk, i.e. if you don't
950 * want or cannot load the whole sample data into RAM.
951 *
952 * You have to Resize() the sample to the desired size and call
953 * File::Save() <b>before</b> using Write().
954 *
955 * @param pBuffer - source buffer
956 * @param SampleCount - number of sample points to write
957 * @throws Exception if current sample size is too small
958 * @see LoadSampleData()
959 */
960 file_offset_t Sample::Write(void* pBuffer, file_offset_t SampleCount) {
961 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
962 if (GetSize() < SampleCount) throw Exception("Could not write sample data, current sample size to small");
963 return pCkData->Write(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?
964 }
965
966 /**
967 * Apply sample and its settings to the respective RIFF chunks. You have
968 * to call File::Save() to make changes persistent.
969 *
970 * @param pProgress - callback function for progress notification
971 * @throws Exception if FormatTag != DLS_WAVE_FORMAT_PCM or no sample data
972 * was provided yet
973 */
974 void Sample::UpdateChunks(progress_t* pProgress) {
975 if (FormatTag != DLS_WAVE_FORMAT_PCM)
976 throw Exception("Could not save sample, only PCM format is supported");
977 // we refuse to do anything if not sample wave form was provided yet
978 if (!pCkData)
979 throw Exception("Could not save sample, there is no sample data to save");
980 // update chunks of base class as well
981 Resource::UpdateChunks(pProgress);
982 // make sure 'fmt' chunk exists
983 RIFF::Chunk* pCkFormat = pWaveList->GetSubChunk(CHUNK_ID_FMT);
984 if (!pCkFormat) pCkFormat = pWaveList->AddSubChunk(CHUNK_ID_FMT, 16); // assumes PCM format
985 uint8_t* pData = (uint8_t*) pCkFormat->LoadChunkData();
986 // update 'fmt' chunk
987 store16(&pData[0], FormatTag);
988 store16(&pData[2], Channels);
989 store32(&pData[4], SamplesPerSecond);
990 store32(&pData[8], AverageBytesPerSecond);
991 store16(&pData[12], BlockAlign);
992 store16(&pData[14], BitDepth); // assuming PCM format
993 }
994
995
996
997 // *************** Region ***************
998 // *
999
1000 Region::Region(Instrument* pInstrument, RIFF::List* rgnList) : Resource(pInstrument, rgnList), Articulator(rgnList), Sampler(rgnList) {
1001 pCkRegion = rgnList;
1002
1003 // articulation information
1004 RIFF::Chunk* rgnh = rgnList->GetSubChunk(CHUNK_ID_RGNH);
1005 if (rgnh) {
1006 rgnh->Read(&KeyRange, 2, 2);
1007 rgnh->Read(&VelocityRange, 2, 2);
1008 FormatOptionFlags = rgnh->ReadUint16();
1009 KeyGroup = rgnh->ReadUint16();
1010 // Layer is optional
1011 if (rgnh->RemainingBytes() >= sizeof(uint16_t)) {
1012 rgnh->Read(&Layer, 1, sizeof(uint16_t));
1013 } else Layer = 0;
1014 } else { // 'rgnh' chunk is missing
1015 KeyRange.low = 0;
1016 KeyRange.high = 127;
1017 VelocityRange.low = 0;
1018 VelocityRange.high = 127;
1019 FormatOptionFlags = F_RGN_OPTION_SELFNONEXCLUSIVE;
1020 KeyGroup = 0;
1021 Layer = 0;
1022 }
1023 SelfNonExclusive = FormatOptionFlags & F_RGN_OPTION_SELFNONEXCLUSIVE;
1024
1025 // sample information
1026 RIFF::Chunk* wlnk = rgnList->GetSubChunk(CHUNK_ID_WLNK);
1027 if (wlnk) {
1028 WaveLinkOptionFlags = wlnk->ReadUint16();
1029 PhaseGroup = wlnk->ReadUint16();
1030 Channel = wlnk->ReadUint32();
1031 WavePoolTableIndex = wlnk->ReadUint32();
1032 } else { // 'wlnk' chunk is missing
1033 WaveLinkOptionFlags = 0;
1034 PhaseGroup = 0;
1035 Channel = 0; // mono
1036 WavePoolTableIndex = 0; // first entry in wave pool table
1037 }
1038 PhaseMaster = WaveLinkOptionFlags & F_WAVELINK_PHASE_MASTER;
1039 MultiChannel = WaveLinkOptionFlags & F_WAVELINK_MULTICHANNEL;
1040
1041 pSample = NULL;
1042 }
1043
1044 /** @brief Destructor.
1045 *
1046 * Removes RIFF chunks associated with this Region.
1047 */
1048 Region::~Region() {
1049 RIFF::List* pParent = pCkRegion->GetParent();
1050 pParent->DeleteSubChunk(pCkRegion);
1051 }
1052
1053 Sample* Region::GetSample() {
1054 if (pSample) return pSample;
1055 File* file = (File*) GetParent()->GetParent();
1056 uint64_t soughtoffset = file->pWavePoolTable[WavePoolTableIndex];
1057 Sample* sample = file->GetFirstSample();
1058 while (sample) {
1059 if (sample->ullWavePoolOffset == soughtoffset) return (pSample = sample);
1060 sample = file->GetNextSample();
1061 }
1062 return NULL;
1063 }
1064
1065 /**
1066 * Assign another sample to this Region.
1067 *
1068 * @param pSample - sample to be assigned
1069 */
1070 void Region::SetSample(Sample* pSample) {
1071 this->pSample = pSample;
1072 WavePoolTableIndex = 0; // we update this offset when we Save()
1073 }
1074
1075 /**
1076 * Modifies the key range of this Region and makes sure the respective
1077 * chunks are in correct order.
1078 *
1079 * @param Low - lower end of key range
1080 * @param High - upper end of key range
1081 */
1082 void Region::SetKeyRange(uint16_t Low, uint16_t High) {
1083 KeyRange.low = Low;
1084 KeyRange.high = High;
1085
1086 // make sure regions are already loaded
1087 Instrument* pInstrument = (Instrument*) GetParent();
1088 if (!pInstrument->pRegions) pInstrument->LoadRegions();
1089 if (!pInstrument->pRegions) return;
1090
1091 // find the r which is the first one to the right of this region
1092 // at its new position
1093 Region* r = NULL;
1094 Region* prev_region = NULL;
1095 for (
1096 Instrument::RegionList::iterator iter = pInstrument->pRegions->begin();
1097 iter != pInstrument->pRegions->end(); iter++
1098 ) {
1099 if ((*iter)->KeyRange.low > this->KeyRange.low) {
1100 r = *iter;
1101 break;
1102 }
1103 prev_region = *iter;
1104 }
1105
1106 // place this region before r if it's not already there
1107 if (prev_region != this) pInstrument->MoveRegion(this, r);
1108 }
1109
1110 /**
1111 * Apply Region settings to the respective RIFF chunks. You have to
1112 * call File::Save() to make changes persistent.
1113 *
1114 * @param pProgress - callback function for progress notification
1115 * @throws Exception - if the Region's sample could not be found
1116 */
1117 void Region::UpdateChunks(progress_t* pProgress) {
1118 // make sure 'rgnh' chunk exists
1119 RIFF::Chunk* rgnh = pCkRegion->GetSubChunk(CHUNK_ID_RGNH);
1120 if (!rgnh) rgnh = pCkRegion->AddSubChunk(CHUNK_ID_RGNH, Layer ? 14 : 12);
1121 uint8_t* pData = (uint8_t*) rgnh->LoadChunkData();
1122 FormatOptionFlags = (SelfNonExclusive)
1123 ? FormatOptionFlags | F_RGN_OPTION_SELFNONEXCLUSIVE
1124 : FormatOptionFlags & (~F_RGN_OPTION_SELFNONEXCLUSIVE);
1125 // update 'rgnh' chunk
1126 store16(&pData[0], KeyRange.low);
1127 store16(&pData[2], KeyRange.high);
1128 store16(&pData[4], VelocityRange.low);
1129 store16(&pData[6], VelocityRange.high);
1130 store16(&pData[8], FormatOptionFlags);
1131 store16(&pData[10], KeyGroup);
1132 if (rgnh->GetSize() >= 14) store16(&pData[12], Layer);
1133
1134 // update chunks of base classes as well (but skip Resource,
1135 // as a rgn doesn't seem to have dlid and INFO chunks)
1136 Articulator::UpdateChunks(pProgress);
1137 Sampler::UpdateChunks(pProgress);
1138
1139 // make sure 'wlnk' chunk exists
1140 RIFF::Chunk* wlnk = pCkRegion->GetSubChunk(CHUNK_ID_WLNK);
1141 if (!wlnk) wlnk = pCkRegion->AddSubChunk(CHUNK_ID_WLNK, 12);
1142 pData = (uint8_t*) wlnk->LoadChunkData();
1143 WaveLinkOptionFlags = (PhaseMaster)
1144 ? WaveLinkOptionFlags | F_WAVELINK_PHASE_MASTER
1145 : WaveLinkOptionFlags & (~F_WAVELINK_PHASE_MASTER);
1146 WaveLinkOptionFlags = (MultiChannel)
1147 ? WaveLinkOptionFlags | F_WAVELINK_MULTICHANNEL
1148 : WaveLinkOptionFlags & (~F_WAVELINK_MULTICHANNEL);
1149 // get sample's wave pool table index
1150 int index = -1;
1151 File* pFile = (File*) GetParent()->GetParent();
1152 if (pFile->pSamples) {
1153 File::SampleList::iterator iter = pFile->pSamples->begin();
1154 File::SampleList::iterator end = pFile->pSamples->end();
1155 for (int i = 0; iter != end; ++iter, i++) {
1156 if (*iter == pSample) {
1157 index = i;
1158 break;
1159 }
1160 }
1161 }
1162 WavePoolTableIndex = index;
1163 // update 'wlnk' chunk
1164 store16(&pData[0], WaveLinkOptionFlags);
1165 store16(&pData[2], PhaseGroup);
1166 store32(&pData[4], Channel);
1167 store32(&pData[8], WavePoolTableIndex);
1168 }
1169
1170 /**
1171 * Make a (semi) deep copy of the Region object given by @a orig and assign
1172 * it to this object.
1173 *
1174 * Note that the sample pointer referenced by @a orig is simply copied as
1175 * memory address. Thus the respective sample is shared, not duplicated!
1176 *
1177 * @param orig - original Region object to be copied from
1178 */
1179 void Region::CopyAssign(const Region* orig) {
1180 // handle base classes
1181 Resource::CopyAssign(orig);
1182 Articulator::CopyAssign(orig);
1183 Sampler::CopyAssign(orig);
1184 // handle actual own attributes of this class
1185 // (the trivial ones)
1186 VelocityRange = orig->VelocityRange;
1187 KeyGroup = orig->KeyGroup;
1188 Layer = orig->Layer;
1189 SelfNonExclusive = orig->SelfNonExclusive;
1190 PhaseMaster = orig->PhaseMaster;
1191 PhaseGroup = orig->PhaseGroup;
1192 MultiChannel = orig->MultiChannel;
1193 Channel = orig->Channel;
1194 // only take the raw sample reference if the two Region objects are
1195 // part of the same file
1196 if (GetParent()->GetParent() == orig->GetParent()->GetParent()) {
1197 WavePoolTableIndex = orig->WavePoolTableIndex;
1198 pSample = orig->pSample;
1199 } else {
1200 WavePoolTableIndex = -1;
1201 pSample = NULL;
1202 }
1203 FormatOptionFlags = orig->FormatOptionFlags;
1204 WaveLinkOptionFlags = orig->WaveLinkOptionFlags;
1205 // handle the last, a bit sensible attribute
1206 SetKeyRange(orig->KeyRange.low, orig->KeyRange.high);
1207 }
1208
1209
1210 // *************** Instrument ***************
1211 // *
1212
1213 /** @brief Constructor.
1214 *
1215 * Load an existing instrument definition or create a new one. An 'ins'
1216 * list chunk must be given to this constructor. In case this 'ins' list
1217 * chunk contains a 'insh' chunk, the instrument data fields will be
1218 * loaded from there, otherwise default values will be used and the
1219 * 'insh' chunk will be created once File::Save() was called.
1220 *
1221 * @param pFile - pointer to DLS::File where this instrument is
1222 * located (or will be located)
1223 * @param insList - pointer to 'ins' list chunk which is (or will be)
1224 * associated with this instrument
1225 */
1226 Instrument::Instrument(File* pFile, RIFF::List* insList) : Resource(pFile, insList), Articulator(insList) {
1227 pCkInstrument = insList;
1228
1229 midi_locale_t locale;
1230 RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
1231 if (insh) {
1232 Regions = insh->ReadUint32();
1233 insh->Read(&locale, 2, 4);
1234 } else { // 'insh' chunk missing
1235 Regions = 0;
1236 locale.bank = 0;
1237 locale.instrument = 0;
1238 }
1239
1240 MIDIProgram = locale.instrument;
1241 IsDrum = locale.bank & DRUM_TYPE_MASK;
1242 MIDIBankCoarse = (uint8_t) MIDI_BANK_COARSE(locale.bank);
1243 MIDIBankFine = (uint8_t) MIDI_BANK_FINE(locale.bank);
1244 MIDIBank = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine);
1245
1246 pRegions = NULL;
1247 }
1248
1249 Region* Instrument::GetFirstRegion() {
1250 if (!pRegions) LoadRegions();
1251 if (!pRegions) return NULL;
1252 RegionsIterator = pRegions->begin();
1253 return (RegionsIterator != pRegions->end()) ? *RegionsIterator : NULL;
1254 }
1255
1256 Region* Instrument::GetNextRegion() {
1257 if (!pRegions) return NULL;
1258 RegionsIterator++;
1259 return (RegionsIterator != pRegions->end()) ? *RegionsIterator : NULL;
1260 }
1261
1262 void Instrument::LoadRegions() {
1263 if (!pRegions) pRegions = new RegionList;
1264 RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1265 if (lrgn) {
1266 uint32_t regionCkType = (lrgn->GetSubList(LIST_TYPE_RGN2)) ? LIST_TYPE_RGN2 : LIST_TYPE_RGN; // prefer regions level 2
1267 RIFF::List* rgn = lrgn->GetFirstSubList();
1268 while (rgn) {
1269 if (rgn->GetListType() == regionCkType) {
1270 pRegions->push_back(new Region(this, rgn));
1271 }
1272 rgn = lrgn->GetNextSubList();
1273 }
1274 }
1275 }
1276
1277 Region* Instrument::AddRegion() {
1278 if (!pRegions) LoadRegions();
1279 RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1280 if (!lrgn) lrgn = pCkInstrument->AddSubList(LIST_TYPE_LRGN);
1281 RIFF::List* rgn = lrgn->AddSubList(LIST_TYPE_RGN);
1282 Region* pNewRegion = new Region(this, rgn);
1283 pRegions->push_back(pNewRegion);
1284 Regions = (uint32_t) pRegions->size();
1285 return pNewRegion;
1286 }
1287
1288 void Instrument::MoveRegion(Region* pSrc, Region* pDst) {
1289 RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1290 lrgn->MoveSubChunk(pSrc->pCkRegion, (RIFF::Chunk*) (pDst ? pDst->pCkRegion : 0));
1291
1292 pRegions->remove(pSrc);
1293 RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pDst);
1294 pRegions->insert(iter, pSrc);
1295 }
1296
1297 void Instrument::DeleteRegion(Region* pRegion) {
1298 if (!pRegions) return;
1299 RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pRegion);
1300 if (iter == pRegions->end()) return;
1301 pRegions->erase(iter);
1302 Regions = (uint32_t) pRegions->size();
1303 delete pRegion;
1304 }
1305
1306 /**
1307 * Apply Instrument with all its Regions to the respective RIFF chunks.
1308 * You have to call File::Save() to make changes persistent.
1309 *
1310 * @param pProgress - callback function for progress notification
1311 * @throws Exception - on errors
1312 */
1313 void Instrument::UpdateChunks(progress_t* pProgress) {
1314 // first update base classes' chunks
1315 Resource::UpdateChunks(pProgress);
1316 Articulator::UpdateChunks(pProgress);
1317 // make sure 'insh' chunk exists
1318 RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
1319 if (!insh) insh = pCkInstrument->AddSubChunk(CHUNK_ID_INSH, 12);
1320 uint8_t* pData = (uint8_t*) insh->LoadChunkData();
1321 // update 'insh' chunk
1322 Regions = (pRegions) ? uint32_t(pRegions->size()) : 0;
1323 midi_locale_t locale;
1324 locale.instrument = MIDIProgram;
1325 locale.bank = MIDI_BANK_ENCODE(MIDIBankCoarse, MIDIBankFine);
1326 locale.bank = (IsDrum) ? locale.bank | DRUM_TYPE_MASK : locale.bank & (~DRUM_TYPE_MASK);
1327 MIDIBank = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine); // just a sync, when we're at it
1328 store32(&pData[0], Regions);
1329 store32(&pData[4], locale.bank);
1330 store32(&pData[8], locale.instrument);
1331 // update Region's chunks
1332 if (!pRegions) return;
1333 RegionList::iterator iter = pRegions->begin();
1334 RegionList::iterator end = pRegions->end();
1335 for (int i = 0; iter != end; ++iter, ++i) {
1336 // divide local progress into subprogress
1337 progress_t subprogress;
1338 __divide_progress(pProgress, &subprogress, pRegions->size(), i);
1339 // do the actual work
1340 (*iter)->UpdateChunks(&subprogress);
1341 }
1342 __notify_progress(pProgress, 1.0); // notify done
1343 }
1344
1345 /** @brief Destructor.
1346 *
1347 * Removes RIFF chunks associated with this Instrument and frees all
1348 * memory occupied by this instrument.
1349 */
1350 Instrument::~Instrument() {
1351 if (pRegions) {
1352 RegionList::iterator iter = pRegions->begin();
1353 RegionList::iterator end = pRegions->end();
1354 while (iter != end) {
1355 delete *iter;
1356 iter++;
1357 }
1358 delete pRegions;
1359 }
1360 // remove instrument's chunks
1361 RIFF::List* pParent = pCkInstrument->GetParent();
1362 pParent->DeleteSubChunk(pCkInstrument);
1363 }
1364
1365 void Instrument::CopyAssignCore(const Instrument* orig) {
1366 // handle base classes
1367 Resource::CopyAssign(orig);
1368 Articulator::CopyAssign(orig);
1369 // handle actual own attributes of this class
1370 // (the trivial ones)
1371 IsDrum = orig->IsDrum;
1372 MIDIBank = orig->MIDIBank;
1373 MIDIBankCoarse = orig->MIDIBankCoarse;
1374 MIDIBankFine = orig->MIDIBankFine;
1375 MIDIProgram = orig->MIDIProgram;
1376 }
1377
1378 /**
1379 * Make a (semi) deep copy of the Instrument object given by @a orig and assign
1380 * it to this object.
1381 *
1382 * Note that all sample pointers referenced by @a orig are simply copied as
1383 * memory address. Thus the respective samples are shared, not duplicated!
1384 *
1385 * @param orig - original Instrument object to be copied from
1386 */
1387 void Instrument::CopyAssign(const Instrument* orig) {
1388 CopyAssignCore(orig);
1389 // delete all regions first
1390 while (Regions) DeleteRegion(GetFirstRegion());
1391 // now recreate and copy regions
1392 {
1393 RegionList::const_iterator it = orig->pRegions->begin();
1394 for (int i = 0; i < orig->Regions; ++i, ++it) {
1395 Region* dstRgn = AddRegion();
1396 //NOTE: Region does semi-deep copy !
1397 dstRgn->CopyAssign(*it);
1398 }
1399 }
1400 }
1401
1402
1403 // *************** File ***************
1404 // *
1405
1406 /** @brief Constructor.
1407 *
1408 * Default constructor, use this to create an empty DLS file. You have
1409 * to add samples, instruments and finally call Save() to actually write
1410 * a DLS file.
1411 */
1412 File::File() : Resource(NULL, pRIFF = new RIFF::File(RIFF_TYPE_DLS)) {
1413 pRIFF->SetByteOrder(RIFF::endian_little);
1414 bOwningRiff = true;
1415 pVersion = new version_t;
1416 pVersion->major = 0;
1417 pVersion->minor = 0;
1418 pVersion->release = 0;
1419 pVersion->build = 0;
1420
1421 Instruments = 0;
1422 WavePoolCount = 0;
1423 pWavePoolTable = NULL;
1424 pWavePoolTableHi = NULL;
1425 WavePoolHeaderSize = 8;
1426
1427 pSamples = NULL;
1428 pInstruments = NULL;
1429
1430 b64BitWavePoolOffsets = false;
1431 }
1432
1433 /** @brief Constructor.
1434 *
1435 * Load an existing DLS file.
1436 *
1437 * @param pRIFF - pointer to a RIFF file which is actually the DLS file
1438 * to load
1439 * @throws Exception if given file is not a DLS file, expected chunks
1440 * are missing
1441 */
1442 File::File(RIFF::File* pRIFF) : Resource(NULL, pRIFF) {
1443 if (!pRIFF) throw DLS::Exception("NULL pointer reference to RIFF::File object.");
1444 this->pRIFF = pRIFF;
1445 bOwningRiff = false;
1446 RIFF::Chunk* ckVersion = pRIFF->GetSubChunk(CHUNK_ID_VERS);
1447 if (ckVersion) {
1448 pVersion = new version_t;
1449 ckVersion->Read(pVersion, 4, 2);
1450 }
1451 else pVersion = NULL;
1452
1453 RIFF::Chunk* colh = pRIFF->GetSubChunk(CHUNK_ID_COLH);
1454 if (!colh) throw DLS::Exception("Mandatory chunks in RIFF list chunk not found.");
1455 Instruments = colh->ReadUint32();
1456
1457 RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1458 if (!ptbl) { // pool table is missing - this is probably an ".art" file
1459 WavePoolCount = 0;
1460 pWavePoolTable = NULL;
1461 pWavePoolTableHi = NULL;
1462 WavePoolHeaderSize = 8;
1463 b64BitWavePoolOffsets = false;
1464 } else {
1465 WavePoolHeaderSize = ptbl->ReadUint32();
1466 WavePoolCount = ptbl->ReadUint32();
1467 pWavePoolTable = new uint32_t[WavePoolCount];
1468 pWavePoolTableHi = new uint32_t[WavePoolCount];
1469 ptbl->SetPos(WavePoolHeaderSize);
1470
1471 // Check for 64 bit offsets (used in gig v3 files)
1472 b64BitWavePoolOffsets = (ptbl->GetSize() - WavePoolHeaderSize == WavePoolCount * 8);
1473 if (b64BitWavePoolOffsets) {
1474 for (int i = 0 ; i < WavePoolCount ; i++) {
1475 pWavePoolTableHi[i] = ptbl->ReadUint32();
1476 pWavePoolTable[i] = ptbl->ReadUint32();
1477 //NOTE: disabled this 2GB check, not sure why this check was still left here (Christian, 2016-05-12)
1478 //if (pWavePoolTable[i] & 0x80000000)
1479 // throw DLS::Exception("Files larger than 2 GB not yet supported");
1480 }
1481 } else { // conventional 32 bit offsets
1482 ptbl->Read(pWavePoolTable, WavePoolCount, sizeof(uint32_t));
1483 for (int i = 0 ; i < WavePoolCount ; i++) pWavePoolTableHi[i] = 0;
1484 }
1485 }
1486
1487 pSamples = NULL;
1488 pInstruments = NULL;
1489 }
1490
1491 File::~File() {
1492 if (pInstruments) {
1493 InstrumentList::iterator iter = pInstruments->begin();
1494 InstrumentList::iterator end = pInstruments->end();
1495 while (iter != end) {
1496 delete *iter;
1497 iter++;
1498 }
1499 delete pInstruments;
1500 }
1501
1502 if (pSamples) {
1503 SampleList::iterator iter = pSamples->begin();
1504 SampleList::iterator end = pSamples->end();
1505 while (iter != end) {
1506 delete *iter;
1507 iter++;
1508 }
1509 delete pSamples;
1510 }
1511
1512 if (pWavePoolTable) delete[] pWavePoolTable;
1513 if (pWavePoolTableHi) delete[] pWavePoolTableHi;
1514 if (pVersion) delete pVersion;
1515 for (std::list<RIFF::File*>::iterator i = ExtensionFiles.begin() ; i != ExtensionFiles.end() ; i++)
1516 delete *i;
1517 if (bOwningRiff)
1518 delete pRIFF;
1519 }
1520
1521 Sample* File::GetFirstSample() {
1522 if (!pSamples) LoadSamples();
1523 if (!pSamples) return NULL;
1524 SamplesIterator = pSamples->begin();
1525 return (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL;
1526 }
1527
1528 Sample* File::GetNextSample() {
1529 if (!pSamples) return NULL;
1530 SamplesIterator++;
1531 return (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL;
1532 }
1533
1534 void File::LoadSamples() {
1535 if (!pSamples) pSamples = new SampleList;
1536 RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1537 if (wvpl) {
1538 file_offset_t wvplFileOffset = wvpl->GetFilePos();
1539 RIFF::List* wave = wvpl->GetFirstSubList();
1540 while (wave) {
1541 if (wave->GetListType() == LIST_TYPE_WAVE) {
1542 file_offset_t waveFileOffset = wave->GetFilePos();
1543 pSamples->push_back(new Sample(this, wave, waveFileOffset - wvplFileOffset));
1544 }
1545 wave = wvpl->GetNextSubList();
1546 }
1547 }
1548 else { // Seen a dwpl list chunk instead of a wvpl list chunk in some file (officially not DLS compliant)
1549 RIFF::List* dwpl = pRIFF->GetSubList(LIST_TYPE_DWPL);
1550 if (dwpl) {
1551 file_offset_t dwplFileOffset = dwpl->GetFilePos();
1552 RIFF::List* wave = dwpl->GetFirstSubList();
1553 while (wave) {
1554 if (wave->GetListType() == LIST_TYPE_WAVE) {
1555 file_offset_t waveFileOffset = wave->GetFilePos();
1556 pSamples->push_back(new Sample(this, wave, waveFileOffset - dwplFileOffset));
1557 }
1558 wave = dwpl->GetNextSubList();
1559 }
1560 }
1561 }
1562 }
1563
1564 /** @brief Add a new sample.
1565 *
1566 * This will create a new Sample object for the DLS file. You have to
1567 * call Save() to make this persistent to the file.
1568 *
1569 * @returns pointer to new Sample object
1570 */
1571 Sample* File::AddSample() {
1572 if (!pSamples) LoadSamples();
1573 __ensureMandatoryChunksExist();
1574 RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1575 // create new Sample object and its respective 'wave' list chunk
1576 RIFF::List* wave = wvpl->AddSubList(LIST_TYPE_WAVE);
1577 Sample* pSample = new Sample(this, wave, 0 /*arbitrary value, we update offsets when we save*/);
1578 pSamples->push_back(pSample);
1579 return pSample;
1580 }
1581
1582 /** @brief Delete a sample.
1583 *
1584 * This will delete the given Sample object from the DLS file. You have
1585 * to call Save() to make this persistent to the file.
1586 *
1587 * @param pSample - sample to delete
1588 */
1589 void File::DeleteSample(Sample* pSample) {
1590 if (!pSamples) return;
1591 SampleList::iterator iter = find(pSamples->begin(), pSamples->end(), pSample);
1592 if (iter == pSamples->end()) return;
1593 pSamples->erase(iter);
1594 delete pSample;
1595 }
1596
1597 Instrument* File::GetFirstInstrument() {
1598 if (!pInstruments) LoadInstruments();
1599 if (!pInstruments) return NULL;
1600 InstrumentsIterator = pInstruments->begin();
1601 return (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL;
1602 }
1603
1604 Instrument* File::GetNextInstrument() {
1605 if (!pInstruments) return NULL;
1606 InstrumentsIterator++;
1607 return (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL;
1608 }
1609
1610 void File::LoadInstruments() {
1611 if (!pInstruments) pInstruments = new InstrumentList;
1612 RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1613 if (lstInstruments) {
1614 RIFF::List* lstInstr = lstInstruments->GetFirstSubList();
1615 while (lstInstr) {
1616 if (lstInstr->GetListType() == LIST_TYPE_INS) {
1617 pInstruments->push_back(new Instrument(this, lstInstr));
1618 }
1619 lstInstr = lstInstruments->GetNextSubList();
1620 }
1621 }
1622 }
1623
1624 /** @brief Add a new instrument definition.
1625 *
1626 * This will create a new Instrument object for the DLS file. You have
1627 * to call Save() to make this persistent to the file.
1628 *
1629 * @returns pointer to new Instrument object
1630 */
1631 Instrument* File::AddInstrument() {
1632 if (!pInstruments) LoadInstruments();
1633 __ensureMandatoryChunksExist();
1634 RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1635 RIFF::List* lstInstr = lstInstruments->AddSubList(LIST_TYPE_INS);
1636 Instrument* pInstrument = new Instrument(this, lstInstr);
1637 pInstruments->push_back(pInstrument);
1638 return pInstrument;
1639 }
1640
1641 /** @brief Delete an instrument.
1642 *
1643 * This will delete the given Instrument object from the DLS file. You
1644 * have to call Save() to make this persistent to the file.
1645 *
1646 * @param pInstrument - instrument to delete
1647 */
1648 void File::DeleteInstrument(Instrument* pInstrument) {
1649 if (!pInstruments) return;
1650 InstrumentList::iterator iter = find(pInstruments->begin(), pInstruments->end(), pInstrument);
1651 if (iter == pInstruments->end()) return;
1652 pInstruments->erase(iter);
1653 delete pInstrument;
1654 }
1655
1656 /**
1657 * Returns extension file of given index. Extension files are used
1658 * sometimes to circumvent the 2 GB file size limit of the RIFF format and
1659 * of certain operating systems in general. In this case, instead of just
1660 * using one file, the content is spread among several files with similar
1661 * file name scheme. This is especially used by some GigaStudio sound
1662 * libraries.
1663 *
1664 * @param index - index of extension file
1665 * @returns sought extension file, NULL if index out of bounds
1666 * @see GetFileName()
1667 */
1668 RIFF::File* File::GetExtensionFile(int index) {
1669 if (index < 0 || index >= ExtensionFiles.size()) return NULL;
1670 std::list<RIFF::File*>::iterator iter = ExtensionFiles.begin();
1671 for (int i = 0; iter != ExtensionFiles.end(); ++iter, ++i)
1672 if (i == index) return *iter;
1673 return NULL;
1674 }
1675
1676 /** @brief File name of this DLS file.
1677 *
1678 * This method returns the file name as it was provided when loading
1679 * the respective DLS file. However in case the File object associates
1680 * an empty, that is new DLS file, which was not yet saved to disk,
1681 * this method will return an empty string.
1682 *
1683 * @see GetExtensionFile()
1684 */
1685 String File::GetFileName() {
1686 return pRIFF->GetFileName();
1687 }
1688
1689 /**
1690 * You may call this method store a future file name, so you don't have to
1691 * to pass it to the Save() call later on.
1692 */
1693 void File::SetFileName(const String& name) {
1694 pRIFF->SetFileName(name);
1695 }
1696
1697 /**
1698 * Apply all the DLS file's current instruments, samples and settings to
1699 * the respective RIFF chunks. You have to call Save() to make changes
1700 * persistent.
1701 *
1702 * @param pProgress - callback function for progress notification
1703 * @throws Exception - on errors
1704 */
1705 void File::UpdateChunks(progress_t* pProgress) {
1706 // first update base class's chunks
1707 Resource::UpdateChunks(pProgress);
1708
1709 // if version struct exists, update 'vers' chunk
1710 if (pVersion) {
1711 RIFF::Chunk* ckVersion = pRIFF->GetSubChunk(CHUNK_ID_VERS);
1712 if (!ckVersion) ckVersion = pRIFF->AddSubChunk(CHUNK_ID_VERS, 8);
1713 uint8_t* pData = (uint8_t*) ckVersion->LoadChunkData();
1714 store16(&pData[0], pVersion->minor);
1715 store16(&pData[2], pVersion->major);
1716 store16(&pData[4], pVersion->build);
1717 store16(&pData[6], pVersion->release);
1718 }
1719
1720 // update 'colh' chunk
1721 Instruments = (pInstruments) ? uint32_t(pInstruments->size()) : 0;
1722 RIFF::Chunk* colh = pRIFF->GetSubChunk(CHUNK_ID_COLH);
1723 if (!colh) colh = pRIFF->AddSubChunk(CHUNK_ID_COLH, 4);
1724 uint8_t* pData = (uint8_t*) colh->LoadChunkData();
1725 store32(pData, Instruments);
1726
1727 // update instrument's chunks
1728 if (pInstruments) {
1729 // divide local progress into subprogress
1730 progress_t subprogress;
1731 __divide_progress(pProgress, &subprogress, 20.f, 0.f); // arbitrarily subdivided into 5% of total progress
1732
1733 // do the actual work
1734 InstrumentList::iterator iter = pInstruments->begin();
1735 InstrumentList::iterator end = pInstruments->end();
1736 for (int i = 0; iter != end; ++iter, ++i) {
1737 // divide subprogress into sub-subprogress
1738 progress_t subsubprogress;
1739 __divide_progress(&subprogress, &subsubprogress, pInstruments->size(), i);
1740 // do the actual work
1741 (*iter)->UpdateChunks(&subsubprogress);
1742 }
1743
1744 __notify_progress(&subprogress, 1.0); // notify subprogress done
1745 }
1746
1747 // update 'ptbl' chunk
1748 const int iSamples = (pSamples) ? int(pSamples->size()) : 0;
1749 int iPtblOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1750 RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1751 if (!ptbl) ptbl = pRIFF->AddSubChunk(CHUNK_ID_PTBL, 1 /*anything, we'll resize*/);
1752 int iPtblSize = WavePoolHeaderSize + iPtblOffsetSize * iSamples;
1753 ptbl->Resize(iPtblSize);
1754 pData = (uint8_t*) ptbl->LoadChunkData();
1755 WavePoolCount = iSamples;
1756 store32(&pData[4], WavePoolCount);
1757 // we actually update the sample offsets in the pool table when we Save()
1758 memset(&pData[WavePoolHeaderSize], 0, iPtblSize - WavePoolHeaderSize);
1759
1760 // update sample's chunks
1761 if (pSamples) {
1762 // divide local progress into subprogress
1763 progress_t subprogress;
1764 __divide_progress(pProgress, &subprogress, 20.f, 1.f); // arbitrarily subdivided into 95% of total progress
1765
1766 // do the actual work
1767 SampleList::iterator iter = pSamples->begin();
1768 SampleList::iterator end = pSamples->end();
1769 for (int i = 0; iter != end; ++iter, ++i) {
1770 // divide subprogress into sub-subprogress
1771 progress_t subsubprogress;
1772 __divide_progress(&subprogress, &subsubprogress, pSamples->size(), i);
1773 // do the actual work
1774 (*iter)->UpdateChunks(&subsubprogress);
1775 }
1776
1777 __notify_progress(&subprogress, 1.0); // notify subprogress done
1778 }
1779
1780 // if there are any extension files, gather which ones are regular
1781 // extension files used as wave pool files (.gx00, .gx01, ... , .gx98)
1782 // and which one is probably a convolution (GigaPulse) file (always to
1783 // be saved as .gx99)
1784 std::list<RIFF::File*> poolFiles; // < for (.gx00, .gx01, ... , .gx98) files
1785 RIFF::File* pGigaPulseFile = NULL; // < for .gx99 file
1786 if (!ExtensionFiles.empty()) {
1787 std::list<RIFF::File*>::iterator it = ExtensionFiles.begin();
1788 for (; it != ExtensionFiles.end(); ++it) {
1789 //FIXME: the .gx99 file is always used by GSt for convolution
1790 // data (GigaPulse); so we should better detect by subchunk
1791 // whether the extension file is intended for convolution
1792 // instead of checkking for a file name, because the latter does
1793 // not work for saving new gigs created from scratch
1794 const std::string oldName = (*it)->GetFileName();
1795 const bool isGigaPulseFile = (extensionOfPath(oldName) == "gx99");
1796 if (isGigaPulseFile)
1797 pGigaPulseFile = *it;
1798 else
1799 poolFiles.push_back(*it);
1800 }
1801 }
1802
1803 // update the 'xfil' chunk which describes all extension files (wave
1804 // pool files) except the .gx99 file
1805 if (!poolFiles.empty()) {
1806 const int n = poolFiles.size();
1807 const int iHeaderSize = 4;
1808 const int iEntrySize = 144;
1809
1810 // make sure chunk exists, and with correct size
1811 RIFF::Chunk* ckXfil = pRIFF->GetSubChunk(CHUNK_ID_XFIL);
1812 if (ckXfil)
1813 ckXfil->Resize(iHeaderSize + n * iEntrySize);
1814 else
1815 ckXfil = pRIFF->AddSubChunk(CHUNK_ID_XFIL, iHeaderSize + n * iEntrySize);
1816
1817 uint8_t* pData = (uint8_t*) ckXfil->LoadChunkData();
1818
1819 // re-assemble the chunk's content
1820 store32(pData, n);
1821 std::list<RIFF::File*>::iterator itExtFile = poolFiles.begin();
1822 for (int i = 0, iOffset = 4; i < n;
1823 ++itExtFile, ++i, iOffset += iEntrySize)
1824 {
1825 // update the filename string and 5 byte extension of each extension file
1826 std::string file = lastPathComponent(
1827 (*itExtFile)->GetFileName()
1828 );
1829 if (file.length() + 6 > 128)
1830 throw Exception("Fatal error, extension filename length exceeds 122 byte maximum");
1831 uint8_t* pStrings = &pData[iOffset];
1832 memset(pStrings, 0, 128);
1833 memcpy(pStrings, file.c_str(), file.length());
1834 pStrings += file.length() + 1;
1835 std::string ext = file.substr(file.length()-5);
1836 memcpy(pStrings, ext.c_str(), 5);
1837 // update the dlsid of the extension file
1838 uint8_t* pId = &pData[iOffset + 128];
1839 dlsid_t id;
1840 RIFF::Chunk* ckDLSID = (*itExtFile)->GetSubChunk(CHUNK_ID_DLID);
1841 if (ckDLSID) {
1842 ckDLSID->Read(&id.ulData1, 1, 4);
1843 ckDLSID->Read(&id.usData2, 1, 2);
1844 ckDLSID->Read(&id.usData3, 1, 2);
1845 ckDLSID->Read(id.abData, 8, 1);
1846 } else {
1847 ckDLSID = (*itExtFile)->AddSubChunk(CHUNK_ID_DLID, 16);
1848 Resource::GenerateDLSID(&id);
1849 uint8_t* pData = (uint8_t*)ckDLSID->LoadChunkData();
1850 store32(&pData[0], id.ulData1);
1851 store16(&pData[4], id.usData2);
1852 store16(&pData[6], id.usData3);
1853 memcpy(&pData[8], id.abData, 8);
1854 }
1855 store32(&pId[0], id.ulData1);
1856 store16(&pId[4], id.usData2);
1857 store16(&pId[6], id.usData3);
1858 memcpy(&pId[8], id.abData, 8);
1859 }
1860 } else {
1861 // in case there was a 'xfil' chunk, remove it
1862 RIFF::Chunk* ckXfil = pRIFF->GetSubChunk(CHUNK_ID_XFIL);
1863 if (ckXfil) pRIFF->DeleteSubChunk(ckXfil);
1864 }
1865
1866 // update the 'doxf' chunk which describes a .gx99 extension file
1867 // which contains convolution data (GigaPulse)
1868 if (pGigaPulseFile) {
1869 RIFF::Chunk* ckDoxf = pRIFF->GetSubChunk(CHUNK_ID_DOXF);
1870 if (!ckDoxf) ckDoxf = pRIFF->AddSubChunk(CHUNK_ID_DOXF, 148);
1871
1872 uint8_t* pData = (uint8_t*) ckDoxf->LoadChunkData();
1873
1874 // update the dlsid from the extension file
1875 uint8_t* pId = &pData[132];
1876 RIFF::Chunk* ckDLSID = pGigaPulseFile->GetSubChunk(CHUNK_ID_DLID);
1877 if (!ckDLSID) { //TODO: auto generate DLS ID if missing
1878 throw Exception("Fatal error, GigaPulse file does not contain a DLS ID chunk");
1879 } else {
1880 dlsid_t id;
1881 // read DLS ID from extension files's DLS ID chunk
1882 uint8_t* pData = (uint8_t*) ckDLSID->LoadChunkData();
1883 id.ulData1 = load32(&pData[0]);
1884 id.usData2 = load16(&pData[4]);
1885 id.usData3 = load16(&pData[6]);
1886 memcpy(id.abData, &pData[8], 8);
1887 // store DLS ID to 'doxf' chunk
1888 store32(&pId[0], id.ulData1);
1889 store16(&pId[4], id.usData2);
1890 store16(&pId[6], id.usData3);
1891 memcpy(&pId[8], id.abData, 8);
1892 }
1893 } else {
1894 // in case there was a 'doxf' chunk, remove it
1895 RIFF::Chunk* ckDoxf = pRIFF->GetSubChunk(CHUNK_ID_DOXF);
1896 if (ckDoxf) pRIFF->DeleteSubChunk(ckDoxf);
1897 }
1898
1899 // the RIFF file to be written might now been grown >= 4GB or might
1900 // been shrunk < 4GB, so we might need to update the wave pool offset
1901 // size and thus accordingly we would need to resize the wave pool
1902 // chunk
1903 const file_offset_t finalFileSize = pRIFF->GetRequiredFileSize();
1904 const bool bRequires64Bit = (finalFileSize >> 32) != 0 || // < native 64 bit gig file
1905 poolFiles.size() > 0; // < 32 bit gig file where the hi 32 bits are used as extension file nr
1906 if (b64BitWavePoolOffsets != bRequires64Bit) {
1907 b64BitWavePoolOffsets = bRequires64Bit;
1908 iPtblOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1909 iPtblSize = WavePoolHeaderSize + iPtblOffsetSize * iSamples;
1910 ptbl->Resize(iPtblSize);
1911 }
1912
1913 __notify_progress(pProgress, 1.0); // notify done
1914 }
1915
1916 /** @brief Save changes to another file.
1917 *
1918 * Make all changes persistent by writing them to another file.
1919 * <b>Caution:</b> this method is optimized for writing to
1920 * <b>another</b> file, do not use it to save the changes to the same
1921 * file! Use Save() (without path argument) in that case instead!
1922 * Ignoring this might result in a corrupted file!
1923 *
1924 * After calling this method, this File object will be associated with
1925 * the new file (given by \a Path) afterwards.
1926 *
1927 * @param Path - path and file name where everything should be written to
1928 * @param pProgress - optional: callback function for progress notification
1929 */
1930 void File::Save(const String& Path, progress_t* pProgress) {
1931 // calculate number of tasks to notify progress appropriately
1932 const size_t nExtFiles = ExtensionFiles.size();
1933 const float tasks = 2.f + nExtFiles;
1934
1935 // save extension files (if required)
1936 if (!ExtensionFiles.empty()) {
1937 // for assembling path of extension files to be saved to
1938 const std::string folder = parentPath(Path);
1939 const std::string baseName = pathWithoutExtension(Path);
1940 // save the individual extension files
1941 std::list<RIFF::File*>::iterator it = ExtensionFiles.begin();
1942 for (int i = 0; it != ExtensionFiles.end(); ++i, ++it) {
1943 // divide local progress into subprogress
1944 progress_t subprogress;
1945 __divide_progress(pProgress, &subprogress, tasks, 0.f + i); // subdivided into amount of extension files
1946 //FIXME: the .gx99 file is always used by GSt for convolution
1947 // data (GigaPulse); so we should better detect by subchunk
1948 // whether the extension file is intended for convolution
1949 // instead of checkking for a file name, because the latter does
1950 // not work for saving new gigs created from scratch
1951 const std::string oldName = (*it)->GetFileName();
1952 const bool isGigaPulseFile = (extensionOfPath(oldName) == "gx99");
1953 std::string ext = (isGigaPulseFile) ? ".gx99" : strPrint(".gx02d", i+1);
1954 std::string newPath = concatPath(folder, baseName) + ext;
1955 // save extension file to its new location
1956 (*it)->Save(newPath, &subprogress);
1957 }
1958 }
1959
1960 {
1961 // divide local progress into subprogress
1962 progress_t subprogress;
1963 __divide_progress(pProgress, &subprogress, tasks, 1.f + nExtFiles); // arbitrarily subdivided into 50% (minus extension files progress)
1964 // do the actual work
1965 UpdateChunks(&subprogress);
1966 }
1967 {
1968 // divide local progress into subprogress
1969 progress_t subprogress;
1970 __divide_progress(pProgress, &subprogress, tasks, 2.f + nExtFiles); // arbitrarily subdivided into 50% (minus extension files progress)
1971 // do the actual work
1972 pRIFF->Save(Path, &subprogress);
1973 }
1974 UpdateFileOffsets();
1975 __notify_progress(pProgress, 1.0); // notify done
1976 }
1977
1978 /** @brief Save changes to same file.
1979 *
1980 * Make all changes persistent by writing them to the actual (same)
1981 * file. The file might temporarily grow to a higher size than it will
1982 * have at the end of the saving process.
1983 *
1984 * @param pProgress - optional: callback function for progress notification
1985 * @throws RIFF::Exception if any kind of IO error occurred
1986 * @throws DLS::Exception if any kind of DLS specific error occurred
1987 */
1988 void File::Save(progress_t* pProgress) {
1989 // calculate number of tasks to notify progress appropriately
1990 const size_t nExtFiles = ExtensionFiles.size();
1991 const float tasks = 2.f + nExtFiles;
1992
1993 // save extension files (if required)
1994 if (!ExtensionFiles.empty()) {
1995 std::list<RIFF::File*>::iterator it = ExtensionFiles.begin();
1996 for (int i = 0; it != ExtensionFiles.end(); ++i, ++it) {
1997 // divide local progress into subprogress
1998 progress_t subprogress;
1999 __divide_progress(pProgress, &subprogress, tasks, 0.f + i); // subdivided into amount of extension files
2000 // save extension file
2001 (*it)->Save(&subprogress);
2002 }
2003 }
2004
2005 {
2006 // divide local progress into subprogress
2007 progress_t subprogress;
2008 __divide_progress(pProgress, &subprogress, tasks, 1.f + nExtFiles); // arbitrarily subdivided into 50% (minus extension files progress)
2009 // do the actual work
2010 UpdateChunks(&subprogress);
2011 }
2012 {
2013 // divide local progress into subprogress
2014 progress_t subprogress;
2015 __divide_progress(pProgress, &subprogress, tasks, 2.f + nExtFiles); // arbitrarily subdivided into 50% (minus extension files progress)
2016 // do the actual work
2017 pRIFF->Save(&subprogress);
2018 }
2019 UpdateFileOffsets();
2020 __notify_progress(pProgress, 1.0); // notify done
2021 }
2022
2023 /** @brief Updates all file offsets stored all over the file.
2024 *
2025 * This virtual method is called whenever the overall file layout has been
2026 * changed (i.e. file or individual RIFF chunks have been resized). It is
2027 * then the responsibility of this method to update all file offsets stored
2028 * in the file format. For example samples are referenced by instruments by
2029 * file offsets. The gig format also stores references to instrument
2030 * scripts as file offsets, and thus it overrides this method to update
2031 * those file offsets as well.
2032 */
2033 void File::UpdateFileOffsets() {
2034 __UpdateWavePoolTableChunk();
2035 }
2036
2037 /**
2038 * Checks if all (for DLS) mandatory chunks exist, if not they will be
2039 * created. Note that those chunks will not be made persistent until
2040 * Save() was called.
2041 */
2042 void File::__ensureMandatoryChunksExist() {
2043 // enusre 'lins' list chunk exists (mandatory for instrument definitions)
2044 RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
2045 if (!lstInstruments) pRIFF->AddSubList(LIST_TYPE_LINS);
2046 // ensure 'ptbl' chunk exists (mandatory for samples)
2047 RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
2048 if (!ptbl) {
2049 const int iOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
2050 ptbl = pRIFF->AddSubChunk(CHUNK_ID_PTBL, WavePoolHeaderSize + iOffsetSize);
2051 }
2052 // enusre 'wvpl' list chunk exists (mandatory for samples)
2053 RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
2054 if (!wvpl) pRIFF->AddSubList(LIST_TYPE_WVPL);
2055 }
2056
2057 /**
2058 * Updates (persistently) the wave pool table with offsets to all
2059 * currently available samples. <b>Caution:</b> this method assumes the
2060 * 'ptbl' chunk to be already of the correct size and the file to be
2061 * writable, so usually this method is only called after a Save() call.
2062 *
2063 * @throws Exception - if 'ptbl' chunk is too small (should only occur
2064 * if there's a bug)
2065 */
2066 void File::__UpdateWavePoolTableChunk() {
2067 __UpdateWavePoolTable();
2068 RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
2069 const int iOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
2070 // check if 'ptbl' chunk is large enough
2071 WavePoolCount = (pSamples) ? uint32_t(pSamples->size()) : 0;
2072 const file_offset_t ulRequiredSize = WavePoolHeaderSize + iOffsetSize * WavePoolCount;
2073 if (ptbl->GetSize() < ulRequiredSize) throw Exception("Fatal error, 'ptbl' chunk too small");
2074 // save the 'ptbl' chunk's current read/write position
2075 file_offset_t ullOriginalPos = ptbl->GetPos();
2076 // update headers
2077 ptbl->SetPos(0);
2078 uint32_t tmp = WavePoolHeaderSize;
2079 ptbl->WriteUint32(&tmp);
2080 tmp = WavePoolCount;
2081 ptbl->WriteUint32(&tmp);
2082 // update offsets
2083 ptbl->SetPos(WavePoolHeaderSize);
2084 if (b64BitWavePoolOffsets) {
2085 for (int i = 0 ; i < WavePoolCount ; i++) {
2086 tmp = pWavePoolTableHi[i];
2087 ptbl->WriteUint32(&tmp);
2088 tmp = pWavePoolTable[i];
2089 ptbl->WriteUint32(&tmp);
2090 }
2091 } else { // conventional 32 bit offsets
2092 for (int i = 0 ; i < WavePoolCount ; i++) {
2093 tmp = pWavePoolTable[i];
2094 ptbl->WriteUint32(&tmp);
2095 }
2096 }
2097 // restore 'ptbl' chunk's original read/write position
2098 ptbl->SetPos(ullOriginalPos);
2099 }
2100
2101 /**
2102 * Updates the wave pool table with offsets to all currently available
2103 * samples. <b>Caution:</b> this method assumes the 'wvpl' list chunk
2104 * exists already.
2105 */
2106 void File::__UpdateWavePoolTable() {
2107 WavePoolCount = (pSamples) ? uint32_t(pSamples->size()) : 0;
2108 // resize wave pool table arrays
2109 if (pWavePoolTable) delete[] pWavePoolTable;
2110 if (pWavePoolTableHi) delete[] pWavePoolTableHi;
2111 pWavePoolTable = new uint32_t[WavePoolCount];
2112 pWavePoolTableHi = new uint32_t[WavePoolCount];
2113 if (!pSamples) return;
2114 // update offsets in wave pool table
2115 RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
2116 uint64_t wvplFileOffset = wvpl->GetFilePos();
2117 if (!b64BitWavePoolOffsets) { // conventional 32 bit offsets (and no extension files) ...
2118 SampleList::iterator iter = pSamples->begin();
2119 SampleList::iterator end = pSamples->end();
2120 for (int i = 0 ; iter != end ; ++iter, i++) {
2121 uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE(pRIFF->GetFileOffsetSize());
2122 (*iter)->ullWavePoolOffset = _64BitOffset;
2123 pWavePoolTable[i] = (uint32_t) _64BitOffset;
2124 }
2125 } else { // a) native 64 bit offsets without extension files or b) 32 bit offsets with extension files ...
2126 if (ExtensionFiles.empty()) { // native 64 bit offsets (and no extension files) [not compatible with GigaStudio] ...
2127 SampleList::iterator iter = pSamples->begin();
2128 SampleList::iterator end = pSamples->end();
2129 for (int i = 0 ; iter != end ; ++iter, i++) {
2130 uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE(pRIFF->GetFileOffsetSize());
2131 (*iter)->ullWavePoolOffset = _64BitOffset;
2132 pWavePoolTableHi[i] = (uint32_t) (_64BitOffset >> 32);
2133 pWavePoolTable[i] = (uint32_t) _64BitOffset;
2134 }
2135 } else { // 32 bit offsets with extension files (GigaStudio legacy support) ...
2136 // the main gig and the extension files may contain wave data
2137 std::vector<RIFF::File*> poolFiles;
2138 poolFiles.push_back(pRIFF);
2139 poolFiles.insert(poolFiles.end(), ExtensionFiles.begin(), ExtensionFiles.end());
2140
2141 RIFF::File* pCurPoolFile = NULL;
2142 int fileNo = 0;
2143 int waveOffset = 0;
2144 SampleList::iterator iter = pSamples->begin();
2145 SampleList::iterator end = pSamples->end();
2146 for (int i = 0 ; iter != end ; ++iter, i++) {
2147 RIFF::File* pPoolFile = (*iter)->pWaveList->GetFile();
2148 // if this sample is located in the same pool file as the
2149 // last we reuse the previously computed fileNo and waveOffset
2150 if (pPoolFile != pCurPoolFile) { // it is a different pool file than the last sample ...
2151 pCurPoolFile = pPoolFile;
2152
2153 std::vector<RIFF::File*>::iterator sIter;
2154 sIter = std::find(poolFiles.begin(), poolFiles.end(), pPoolFile);
2155 if (sIter != poolFiles.end())
2156 fileNo = std::distance(poolFiles.begin(), sIter);
2157 else
2158 throw DLS::Exception("Fatal error, unknown pool file");
2159
2160 RIFF::List* extWvpl = pCurPoolFile->GetSubList(LIST_TYPE_WVPL);
2161 if (!extWvpl)
2162 throw DLS::Exception("Fatal error, pool file has no 'wvpl' list chunk");
2163 waveOffset = extWvpl->GetFilePos() + LIST_HEADER_SIZE(pCurPoolFile->GetFileOffsetSize());
2164 }
2165 uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - waveOffset;
2166 // pWavePoolTableHi stores file number when extension files are in use
2167 pWavePoolTableHi[i] = (uint32_t) fileNo;
2168 pWavePoolTable[i] = (uint32_t) _64BitOffset;
2169 (*iter)->ullWavePoolOffset = _64BitOffset;
2170 }
2171 }
2172 }
2173 }
2174
2175
2176 // *************** Exception ***************
2177 // *
2178
2179 Exception::Exception() : RIFF::Exception() {
2180 }
2181
2182 Exception::Exception(String format, ...) : RIFF::Exception() {
2183 va_list arg;
2184 va_start(arg, format);
2185 Message = assemble(format, arg);
2186 va_end(arg);
2187 }
2188
2189 Exception::Exception(String format, va_list arg) : RIFF::Exception() {
2190 Message = assemble(format, arg);
2191 }
2192
2193 void Exception::PrintMessage() {
2194 std::cout << "DLS::Exception: " << Message << std::endl;
2195 }
2196
2197
2198 // *************** functions ***************
2199 // *
2200
2201 /**
2202 * Returns the name of this C++ library. This is usually "libgig" of
2203 * course. This call is equivalent to RIFF::libraryName() and
2204 * gig::libraryName().
2205 */
2206 String libraryName() {
2207 return PACKAGE;
2208 }
2209
2210 /**
2211 * Returns version of this C++ library. This call is equivalent to
2212 * RIFF::libraryVersion() and gig::libraryVersion().
2213 */
2214 String libraryVersion() {
2215 return VERSION;
2216 }
2217
2218 } // namespace DLS

  ViewVC Help
Powered by ViewVC