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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

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

  ViewVC Help
Powered by ViewVC