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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2698 - (show annotations) (download)
Sun Jan 11 17:47:57 2015 UTC (9 years, 3 months ago) by schoenebeck
File size: 80559 byte(s)
* Bugfix: Adding a new region in between two existing regions caused the
  new one being dropped after save operation and the gig file (or DLS file)
  being tainted (chunks were at wrong location in the RIFF tree).
* Bumped version (3.3.0.svn25).

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

  ViewVC Help
Powered by ViewVC