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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2482 - (show annotations) (download)
Mon Nov 25 02:22:38 2013 UTC (10 years, 4 months ago) by schoenebeck
File size: 76014 byte(s)
* Added new command line tool "gigmerge" which allows to merge
  a list of gig files to one single gig file.
* Added new "man" page for new tool "gigmerge".
* src/gig.h: Added new method File::AddContentOf().
* src/DLS.h: Added new method File::SetFileName().
* src/RIFF.h: Added new method File::SetFileName().
* src/RIFF.h: Added new method File::IsNew().
* Added "const" keyword to several methods.
* Bumped version to 3.3.0.svn6.

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

  ViewVC Help
Powered by ViewVC