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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2698 - (hide 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 schoenebeck 2 /***************************************************************************
2     * *
3 schoenebeck 933 * libgig - C++ cross-platform Gigasampler format file access library *
4 schoenebeck 2 * *
5 schoenebeck 2682 * Copyright (C) 2003-2014 by Christian Schoenebeck *
6 schoenebeck 384 * <cuse@users.sourceforge.net> *
7 schoenebeck 2 * *
8     * This library is free software; you can redistribute it and/or modify *
9     * it under the terms of the GNU General Public License as published by *
10     * the Free Software Foundation; either version 2 of the License, or *
11     * (at your option) any later version. *
12     * *
13     * This library is distributed in the hope that it will be useful, *
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16     * GNU General Public License for more details. *
17     * *
18     * You should have received a copy of the GNU General Public License *
19     * along with this library; if not, write to the Free Software *
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21     * MA 02111-1307 USA *
22     ***************************************************************************/
23    
24     #include "DLS.h"
25    
26 persson 1713 #include <algorithm>
27 schoenebeck 800 #include <time.h>
28    
29 persson 1209 #ifdef __APPLE__
30     #include <CoreFoundation/CFUUID.h>
31     #elif defined(HAVE_UUID_UUID_H)
32     #include <uuid/uuid.h>
33     #endif
34    
35 schoenebeck 800 #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 persson 918 #define DRUM_TYPE_MASK 0x80000000
56 schoenebeck 800
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 schoenebeck 2 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 schoenebeck 800 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 schoenebeck 2
105    
106 schoenebeck 800
107 schoenebeck 2 // *************** Articulation ***************
108     // *
109    
110 schoenebeck 800 /** @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 schoenebeck 2 }
124 schoenebeck 800 HeaderSize = artl->ReadUint32();
125     Connections = artl->ReadUint32();
126     artl->SetPos(HeaderSize);
127 schoenebeck 2
128     pConnections = new Connection[Connections];
129     Connection::conn_block_t connblock;
130 schoenebeck 800 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 schoenebeck 2 pConnections[i].Init(&connblock);
137     }
138     }
139    
140     Articulation::~Articulation() {
141     if (pConnections) delete[] pConnections;
142     }
143    
144 schoenebeck 800 /**
145     * Apply articulation connections to the respective RIFF chunks. You
146     * have to call File::Save() to make changes persistent.
147 schoenebeck 2682 *
148     * @param pProgress - callback function for progress notification
149 schoenebeck 800 */
150 schoenebeck 2682 void Articulation::UpdateChunks(progress_t* pProgress) {
151 schoenebeck 800 const int iEntrySize = 12; // 12 bytes per connection block
152     pArticulationCk->Resize(HeaderSize + Connections * iEntrySize);
153     uint8_t* pData = (uint8_t*) pArticulationCk->LoadChunkData();
154 persson 1179 store16(&pData[0], HeaderSize);
155     store16(&pData[2], Connections);
156 schoenebeck 800 for (uint32_t i = 0; i < Connections; i++) {
157     Connection::conn_block_t c = pConnections[i].ToConnBlock();
158 persson 1179 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 schoenebeck 800 }
164     }
165 schoenebeck 2
166    
167 schoenebeck 800
168 schoenebeck 2 // *************** 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 schoenebeck 800 uint32_t artCkType = (lart->GetListType() == LIST_TYPE_LAR2) ? CHUNK_ID_ART2
195     : CHUNK_ID_ARTL;
196     RIFF::Chunk* art = lart->GetFirstSubChunk();
197 schoenebeck 2 while (art) {
198 schoenebeck 800 if (art->GetChunkID() == artCkType) {
199 schoenebeck 2 if (!pArticulations) pArticulations = new ArticulationList;
200     pArticulations->push_back(new Articulation(art));
201     }
202 schoenebeck 800 art = lart->GetNextSubChunk();
203 schoenebeck 2 }
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 schoenebeck 800 /**
220     * Apply all articulations to the respective RIFF chunks. You have to
221     * call File::Save() to make changes persistent.
222 schoenebeck 2682 *
223     * @param pProgress - callback function for progress notification
224 schoenebeck 800 */
225 schoenebeck 2682 void Articulator::UpdateChunks(progress_t* pProgress) {
226 schoenebeck 804 if (pArticulations) {
227     ArticulationList::iterator iter = pArticulations->begin();
228     ArticulationList::iterator end = pArticulations->end();
229     for (; iter != end; ++iter) {
230 schoenebeck 2682 (*iter)->UpdateChunks(pProgress);
231 schoenebeck 804 }
232 schoenebeck 800 }
233     }
234 schoenebeck 2394
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 schoenebeck 2
244    
245 schoenebeck 800
246 schoenebeck 2 // *************** Info ***************
247     // *
248    
249 schoenebeck 800 /** @brief Constructor.
250     *
251 schoenebeck 929 * Initializes the info strings with values provided by an INFO list chunk.
252 schoenebeck 800 *
253 schoenebeck 929 * @param list - pointer to a list chunk which contains an INFO list chunk
254 schoenebeck 800 */
255 schoenebeck 2 Info::Info(RIFF::List* list) {
256 schoenebeck 1416 pFixedStringLengths = NULL;
257 schoenebeck 800 pResourceListChunk = list;
258 schoenebeck 2 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 persson 928 LoadString(CHUNK_ID_ISBJ, lstINFO, Subject);
278 schoenebeck 2 }
279     }
280     }
281    
282 schoenebeck 823 Info::~Info() {
283     }
284    
285 schoenebeck 1416 /**
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 schoenebeck 800 /** @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 schoenebeck 929 ::LoadString(ck, s); // function from helper.h
308 schoenebeck 800 }
309 schoenebeck 2
310 schoenebeck 800 /** @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 schoenebeck 804 * 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 schoenebeck 800 *
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 persson 1180 void Info::SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault) {
326     int size = 0;
327 schoenebeck 1416 if (pFixedStringLengths) {
328     for (int i = 0 ; pFixedStringLengths[i].length ; i++) {
329     if (pFixedStringLengths[i].chunkId == ChunkID) {
330     size = pFixedStringLengths[i].length;
331 persson 1209 break;
332 persson 1180 }
333     }
334     }
335 schoenebeck 800 RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
336 persson 1180 ::SaveString(ChunkID, ck, lstINFO, s, sDefault, size != 0, size); // function from helper.h
337 schoenebeck 800 }
338 schoenebeck 2
339 schoenebeck 800 /** @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 schoenebeck 2682 *
344     * @param pProgress - callback function for progress notification
345 schoenebeck 800 */
346 schoenebeck 2682 void Info::UpdateChunks(progress_t* pProgress) {
347 schoenebeck 800 if (!pResourceListChunk) return;
348    
349     // make sure INFO list chunk exists
350     RIFF::List* lstINFO = pResourceListChunk->GetSubList(LIST_TYPE_INFO);
351    
352 persson 918 String defaultName = "";
353     String defaultCreationDate = "";
354     String defaultSoftware = "";
355     String defaultComments = "";
356 schoenebeck 800
357 persson 918 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 schoenebeck 800 // save values
382 persson 918
383 persson 1180 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 schoenebeck 800 }
401 schoenebeck 2394
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 schoenebeck 800
430    
431    
432 schoenebeck 2 // *************** Resource ***************
433     // *
434    
435 schoenebeck 800 /** @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 schoenebeck 2 Resource::Resource(Resource* Parent, RIFF::List* lstResource) {
445     pParent = Parent;
446 schoenebeck 800 pResourceList = lstResource;
447 schoenebeck 2
448     pInfo = new Info(lstResource);
449    
450     RIFF::Chunk* ckDLSID = lstResource->GetSubChunk(CHUNK_ID_DLID);
451     if (ckDLSID) {
452     pDLSID = new dlsid_t;
453 schoenebeck 11 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 schoenebeck 2 }
458     else pDLSID = NULL;
459     }
460    
461     Resource::~Resource() {
462     if (pDLSID) delete pDLSID;
463     if (pInfo) delete pInfo;
464     }
465    
466 schoenebeck 800 /** @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 schoenebeck 2682 *
474     * @param pProgress - callback function for progress notification
475 schoenebeck 800 */
476 schoenebeck 2682 void Resource::UpdateChunks(progress_t* pProgress) {
477     pInfo->UpdateChunks(pProgress);
478 persson 1209
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 schoenebeck 800 }
491 schoenebeck 2
492 persson 1209 /**
493     * Generates a new DLSID for the resource.
494     */
495     void Resource::GenerateDLSID() {
496     #if defined(WIN32) || defined(__APPLE__) || defined(HAVE_UUID_GENERATE)
497 schoenebeck 2
498 persson 1209 if (!pDLSID) pDLSID = new dlsid_t;
499 schoenebeck 800
500 persson 1209 #ifdef WIN32
501    
502     UUID uuid;
503     UuidCreate(&uuid);
504     pDLSID->ulData1 = uuid.Data1;
505 persson 1301 pDLSID->usData2 = uuid.Data2;
506     pDLSID->usData3 = uuid.Data3;
507 persson 1209 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 schoenebeck 2394
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 persson 1209
546    
547 schoenebeck 2 // *************** Sampler ***************
548     // *
549    
550     Sampler::Sampler(RIFF::List* ParentList) {
551 schoenebeck 800 pParentList = ParentList;
552 schoenebeck 2 RIFF::Chunk* wsmp = ParentList->GetSubChunk(CHUNK_ID_WSMP);
553 schoenebeck 800 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 persson 1388 uiHeaderSize = 20;
562 persson 1218 UnityNote = 60;
563 schoenebeck 800 FineTune = 0; // +- 0 cents
564     Gain = 0; // 0 dB
565     SamplerOptions = F_WSMP_NO_COMPRESSION;
566     SampleLoops = 0;
567     }
568 schoenebeck 2 NoSampleDepthTruncation = SamplerOptions & F_WSMP_NO_TRUNCATION;
569     NoSampleCompression = SamplerOptions & F_WSMP_NO_COMPRESSION;
570     pSampleLoops = (SampleLoops) ? new sample_loop_t[SampleLoops] : NULL;
571 schoenebeck 800 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 schoenebeck 2 }
579     }
580     }
581    
582     Sampler::~Sampler() {
583     if (pSampleLoops) delete[] pSampleLoops;
584     }
585    
586 schoenebeck 1358 void Sampler::SetGain(int32_t gain) {
587     Gain = gain;
588     }
589    
590 schoenebeck 800 /**
591     * Apply all sample player options to the respective RIFF chunk. You
592     * have to call File::Save() to make changes persistent.
593 schoenebeck 2682 *
594     * @param pProgress - callback function for progress notification
595 schoenebeck 800 */
596 schoenebeck 2682 void Sampler::UpdateChunks(progress_t* pProgress) {
597 schoenebeck 800 // make sure 'wsmp' chunk exists
598     RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP);
599 persson 1388 int wsmpSize = uiHeaderSize + SampleLoops * 16;
600 schoenebeck 800 if (!wsmp) {
601 persson 1388 wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, wsmpSize);
602     } else if (wsmp->GetSize() != wsmpSize) {
603     wsmp->Resize(wsmpSize);
604 schoenebeck 800 }
605     uint8_t* pData = (uint8_t*) wsmp->LoadChunkData();
606     // update headers size
607 persson 1179 store32(&pData[0], uiHeaderSize);
608 schoenebeck 800 // 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 persson 1179 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 schoenebeck 800 // update loop definitions
619     for (uint32_t i = 0; i < SampleLoops; i++) {
620     //FIXME: this does not handle extended loop structs correctly
621 persson 1179 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 schoenebeck 800 }
626     }
627 schoenebeck 2
628 schoenebeck 1154 /**
629     * Adds a new sample loop with the provided loop definition.
630     *
631 schoenebeck 1194 * @param pLoopDef - points to a loop definition that is to be copied
632 schoenebeck 1154 */
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 schoenebeck 1155 // auto correct size field
642     pNewLoops[SampleLoops].Size = sizeof(DLS::sample_loop_t);
643 schoenebeck 1154 // free the old array and update the member variables
644     if (SampleLoops) delete[] pSampleLoops;
645     pSampleLoops = pNewLoops;
646     SampleLoops++;
647     }
648 schoenebeck 2
649 schoenebeck 1154 /**
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 persson 2310 if (o == SampleLoops - 1) {
661     delete[] pNewLoops;
662 schoenebeck 1154 throw Exception("Could not delete Sample Loop, because it does not exist");
663 persson 2310 }
664 schoenebeck 1154 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 schoenebeck 2394
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 schoenebeck 800
695 schoenebeck 1154
696 schoenebeck 2 // *************** Sample ***************
697     // *
698    
699 schoenebeck 800 /** @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 schoenebeck 2 Sample::Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset) : Resource(pFile, waveList) {
715 schoenebeck 800 pWaveList = waveList;
716 schoenebeck 2 ulWavePoolOffset = WavePoolOffset - LIST_HEADER_SIZE;
717     pCkFormat = waveList->GetSubChunk(CHUNK_ID_FMT);
718     pCkData = waveList->GetSubChunk(CHUNK_ID_DATA);
719 schoenebeck 800 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 schoenebeck 1050 if (FormatTag == DLS_WAVE_FORMAT_PCM) {
728 schoenebeck 800 BitDepth = pCkFormat->ReadUint16();
729 persson 928 FrameSize = (BitDepth / 8) * Channels;
730 schoenebeck 800 } else { // unsupported sample data format
731     BitDepth = 0;
732     FrameSize = 0;
733     }
734     } else { // 'fmt' chunk missing
735 schoenebeck 1050 FormatTag = DLS_WAVE_FORMAT_PCM;
736 schoenebeck 800 BitDepth = 16;
737     Channels = 1;
738     SamplesPerSecond = 44100;
739     AverageBytesPerSecond = (BitDepth / 8) * SamplesPerSecond * Channels;
740     FrameSize = (BitDepth / 8) * Channels;
741     BlockAlign = FrameSize;
742 schoenebeck 2 }
743 schoenebeck 1050 SamplesTotal = (pCkData) ? (FormatTag == DLS_WAVE_FORMAT_PCM) ? pCkData->GetSize() / FrameSize
744     : 0
745 schoenebeck 800 : 0;
746 schoenebeck 2 }
747    
748 schoenebeck 800 /** @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 schoenebeck 2482
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 schoenebeck 800
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 schoenebeck 2 void* Sample::LoadSampleData() {
836 schoenebeck 800 return (pCkData) ? pCkData->LoadChunkData() : NULL;
837 schoenebeck 2 }
838    
839 schoenebeck 800 /** @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 schoenebeck 2 void Sample::ReleaseSampleData() {
845 schoenebeck 800 if (pCkData) pCkData->ReleaseChunkData();
846 schoenebeck 2 }
847    
848 schoenebeck 800 /** @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 schoenebeck 1050 * @returns number of sample points or 0 if FormatTag != DLS_WAVE_FORMAT_PCM
856 schoenebeck 800 * @see FrameSize, FormatTag
857     */
858 schoenebeck 2482 unsigned long Sample::GetSize() const {
859 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0;
860 schoenebeck 800 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 schoenebeck 1050 * 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 schoenebeck 800 * other formats will fail!
884     *
885     * @param iNewSize - new sample wave data size in sample points (must be
886     * greater than zero)
887 schoenebeck 1050 * @throws Excecption if FormatTag != DLS_WAVE_FORMAT_PCM
888 schoenebeck 800 * @throws Exception if \a iNewSize is less than 1
889     * @see File::Save(), FrameSize, FormatTag
890     */
891     void Sample::Resize(int iNewSize) {
892 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) throw Exception("Sample's format is not DLS_WAVE_FORMAT_PCM");
893 schoenebeck 800 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 schoenebeck 2 /**
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 schoenebeck 1050 * 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 schoenebeck 800 * with other formats will fail!
908     *
909 schoenebeck 2 * @param SampleCount number of sample points
910     * @param Whence to which relation \a SampleCount refers to
911 schoenebeck 800 * @returns new position within the sample, 0 if
912 schoenebeck 1050 * FormatTag != DLS_WAVE_FORMAT_PCM
913 schoenebeck 800 * @throws Exception if no data RIFF chunk was created for the sample yet
914     * @see FrameSize, FormatTag
915 schoenebeck 2 */
916     unsigned long Sample::SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence) {
917 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
918 schoenebeck 800 if (!pCkData) throw Exception("No data chunk created for sample yet, call Sample::Resize() to create one");
919 schoenebeck 2 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 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
936 schoenebeck 11 return pCkData->Read(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?
937 schoenebeck 2 }
938    
939 schoenebeck 800 /** @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 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
956 schoenebeck 800 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 schoenebeck 2
960 schoenebeck 800 /**
961     * Apply sample and its settings to the respective RIFF chunks. You have
962     * to call File::Save() to make changes persistent.
963     *
964 schoenebeck 2682 * @param pProgress - callback function for progress notification
965 schoenebeck 1050 * @throws Exception if FormatTag != DLS_WAVE_FORMAT_PCM or no sample data
966 schoenebeck 800 * was provided yet
967     */
968 schoenebeck 2682 void Sample::UpdateChunks(progress_t* pProgress) {
969 schoenebeck 1050 if (FormatTag != DLS_WAVE_FORMAT_PCM)
970 schoenebeck 800 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 schoenebeck 2682 Resource::UpdateChunks(pProgress);
976 schoenebeck 800 // 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 persson 1179 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 schoenebeck 800 }
988 schoenebeck 2
989 schoenebeck 800
990    
991 schoenebeck 2 // *************** Region ***************
992     // *
993    
994     Region::Region(Instrument* pInstrument, RIFF::List* rgnList) : Resource(pInstrument, rgnList), Articulator(rgnList), Sampler(rgnList) {
995     pCkRegion = rgnList;
996    
997 schoenebeck 800 // articulation informations
998 schoenebeck 2 RIFF::Chunk* rgnh = rgnList->GetSubChunk(CHUNK_ID_RGNH);
999 schoenebeck 800 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 schoenebeck 2 }
1017 schoenebeck 800 SelfNonExclusive = FormatOptionFlags & F_RGN_OPTION_SELFNONEXCLUSIVE;
1018 schoenebeck 2
1019 schoenebeck 800 // sample informations
1020 schoenebeck 2 RIFF::Chunk* wlnk = rgnList->GetSubChunk(CHUNK_ID_WLNK);
1021 schoenebeck 800 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 schoenebeck 2
1035     pSample = NULL;
1036     }
1037    
1038 schoenebeck 800 /** @brief Destructor.
1039     *
1040     * Removes RIFF chunks associated with this Region.
1041     */
1042 schoenebeck 2 Region::~Region() {
1043 schoenebeck 800 RIFF::List* pParent = pCkRegion->GetParent();
1044     pParent->DeleteSubChunk(pCkRegion);
1045 schoenebeck 2 }
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 schoenebeck 800 /**
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 schoenebeck 2
1069 schoenebeck 800 /**
1070 schoenebeck 1335 * 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 schoenebeck 800 * Apply Region settings to the respective RIFF chunks. You have to
1106     * call File::Save() to make changes persistent.
1107     *
1108 schoenebeck 2682 * @param pProgress - callback function for progress notification
1109 schoenebeck 800 * @throws Exception - if the Region's sample could not be found
1110     */
1111 schoenebeck 2682 void Region::UpdateChunks(progress_t* pProgress) {
1112 schoenebeck 800 // make sure 'rgnh' chunk exists
1113     RIFF::Chunk* rgnh = pCkRegion->GetSubChunk(CHUNK_ID_RGNH);
1114 persson 918 if (!rgnh) rgnh = pCkRegion->AddSubChunk(CHUNK_ID_RGNH, Layer ? 14 : 12);
1115 schoenebeck 800 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 persson 1179 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 schoenebeck 2
1128 persson 918 // update chunks of base classes as well (but skip Resource,
1129     // as a rgn doesn't seem to have dlid and INFO chunks)
1130 schoenebeck 2682 Articulator::UpdateChunks(pProgress);
1131     Sampler::UpdateChunks(pProgress);
1132 schoenebeck 800
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 schoenebeck 804 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 schoenebeck 800 }
1155     }
1156     WavePoolTableIndex = index;
1157     // update 'wlnk' chunk
1158 persson 1179 store16(&pData[0], WaveLinkOptionFlags);
1159     store16(&pData[2], PhaseGroup);
1160     store32(&pData[4], Channel);
1161     store32(&pData[8], WavePoolTableIndex);
1162 schoenebeck 800 }
1163 schoenebeck 2394
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 schoenebeck 2482 // 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 schoenebeck 2394 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 schoenebeck 800
1203    
1204 schoenebeck 2 // *************** Instrument ***************
1205     // *
1206    
1207 schoenebeck 800 /** @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 schoenebeck 2 Instrument::Instrument(File* pFile, RIFF::List* insList) : Resource(pFile, insList), Articulator(insList) {
1221     pCkInstrument = insList;
1222    
1223 schoenebeck 800 midi_locale_t locale;
1224 schoenebeck 2 RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
1225 schoenebeck 800 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 schoenebeck 2 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 schoenebeck 800 pRegions = NULL;
1241 schoenebeck 2 }
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 schoenebeck 823 if (!pRegions) pRegions = new RegionList;
1258 schoenebeck 2 RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1259 schoenebeck 823 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 schoenebeck 2 }
1268     }
1269     }
1270    
1271 schoenebeck 800 Region* Instrument::AddRegion() {
1272 schoenebeck 823 if (!pRegions) LoadRegions();
1273 schoenebeck 800 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 schoenebeck 823 Regions = pRegions->size();
1279 schoenebeck 800 return pNewRegion;
1280     }
1281    
1282 persson 1102 void Instrument::MoveRegion(Region* pSrc, Region* pDst) {
1283     RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1284 schoenebeck 2698 lrgn->MoveSubChunk(pSrc->pCkRegion, (RIFF::Chunk*) (pDst ? pDst->pCkRegion : 0));
1285 persson 1102
1286     pRegions->remove(pSrc);
1287     RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pDst);
1288     pRegions->insert(iter, pSrc);
1289     }
1290    
1291 schoenebeck 800 void Instrument::DeleteRegion(Region* pRegion) {
1292 schoenebeck 802 if (!pRegions) return;
1293 schoenebeck 800 RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pRegion);
1294     if (iter == pRegions->end()) return;
1295     pRegions->erase(iter);
1296 schoenebeck 823 Regions = pRegions->size();
1297 schoenebeck 800 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 schoenebeck 2682 * @param pProgress - callback function for progress notification
1305 schoenebeck 800 * @throws Exception - on errors
1306     */
1307 schoenebeck 2682 void Instrument::UpdateChunks(progress_t* pProgress) {
1308 schoenebeck 800 // first update base classes' chunks
1309 schoenebeck 2682 Resource::UpdateChunks(pProgress);
1310     Articulator::UpdateChunks(pProgress);
1311 schoenebeck 800 // 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 schoenebeck 804 Regions = (pRegions) ? pRegions->size() : 0;
1317 schoenebeck 800 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 persson 1179 store32(&pData[0], Regions);
1323     store32(&pData[4], locale.bank);
1324     store32(&pData[8], locale.instrument);
1325 schoenebeck 800 // update Region's chunks
1326 schoenebeck 804 if (!pRegions) return;
1327 schoenebeck 800 RegionList::iterator iter = pRegions->begin();
1328     RegionList::iterator end = pRegions->end();
1329 schoenebeck 2682 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 schoenebeck 800 }
1336 schoenebeck 2682 __notify_progress(pProgress, 1.0); // notify done
1337 schoenebeck 800 }
1338    
1339     /** @brief Destructor.
1340     *
1341     * Removes RIFF chunks associated with this Instrument and frees all
1342     * memory occupied by this instrument.
1343     */
1344 schoenebeck 2 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 schoenebeck 800 // remove instrument's chunks
1355     RIFF::List* pParent = pCkInstrument->GetParent();
1356     pParent->DeleteSubChunk(pCkInstrument);
1357 schoenebeck 2 }
1358 schoenebeck 2394
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 schoenebeck 2
1396    
1397     // *************** File ***************
1398     // *
1399    
1400 schoenebeck 800 /** @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 persson 1184 File::File() : Resource(NULL, pRIFF = new RIFF::File(RIFF_TYPE_DLS)) {
1407     pRIFF->SetByteOrder(RIFF::endian_little);
1408 schoenebeck 800 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 schoenebeck 2 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 schoenebeck 11 ckVersion->Read(pVersion, 4, 2);
1443 schoenebeck 2 }
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 persson 902 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 schoenebeck 2
1464 persson 902 // 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 schoenebeck 317 }
1477 persson 666 }
1478 schoenebeck 317
1479 schoenebeck 2 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 persson 666 if (pWavePoolTableHi) delete[] pWavePoolTableHi;
1506 schoenebeck 2 if (pVersion) delete pVersion;
1507 persson 834 for (std::list<RIFF::File*>::iterator i = ExtensionFiles.begin() ; i != ExtensionFiles.end() ; i++)
1508     delete *i;
1509 schoenebeck 2 }
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 schoenebeck 823 if (!pSamples) pSamples = new SampleList;
1526 schoenebeck 2 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 schoenebeck 800 /** @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 schoenebeck 809 if (!pSamples) LoadSamples();
1563 schoenebeck 800 __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 schoenebeck 802 if (!pSamples) return;
1581 schoenebeck 800 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 schoenebeck 2 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 schoenebeck 823 if (!pInstruments) pInstruments = new InstrumentList;
1602 schoenebeck 2 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 schoenebeck 800 /** @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 schoenebeck 809 if (!pInstruments) LoadInstruments();
1623 schoenebeck 800 __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 schoenebeck 2
1631 schoenebeck 809 /** @brief Delete an instrument.
1632 schoenebeck 800 *
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 schoenebeck 802 if (!pInstruments) return;
1640 schoenebeck 800 InstrumentList::iterator iter = find(pInstruments->begin(), pInstruments->end(), pInstrument);
1641     if (iter == pInstruments->end()) return;
1642     pInstruments->erase(iter);
1643     delete pInstrument;
1644     }
1645 schoenebeck 2
1646 schoenebeck 2329 /**
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 schoenebeck 2274 /** @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 schoenebeck 2329 *
1673     * @see GetExtensionFile()
1674 schoenebeck 2274 */
1675     String File::GetFileName() {
1676     return pRIFF->GetFileName();
1677     }
1678 schoenebeck 2482
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 schoenebeck 2274
1687 schoenebeck 800 /**
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 schoenebeck 2682 * @param pProgress - callback function for progress notification
1693 schoenebeck 800 * @throws Exception - on errors
1694     */
1695 schoenebeck 2682 void File::UpdateChunks(progress_t* pProgress) {
1696 schoenebeck 800 // first update base class's chunks
1697 schoenebeck 2682 Resource::UpdateChunks(pProgress);
1698 schoenebeck 800
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 persson 1179 store16(&pData[0], pVersion->minor);
1705     store16(&pData[2], pVersion->major);
1706     store16(&pData[4], pVersion->build);
1707     store16(&pData[6], pVersion->release);
1708 schoenebeck 800 }
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 persson 1179 store32(pData, Instruments);
1716 schoenebeck 800
1717     // update instrument's chunks
1718     if (pInstruments) {
1719 schoenebeck 2682 // 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 schoenebeck 800 InstrumentList::iterator iter = pInstruments->begin();
1725     InstrumentList::iterator end = pInstruments->end();
1726 schoenebeck 2682 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 schoenebeck 800 }
1733 schoenebeck 2682
1734     __notify_progress(&subprogress, 1.0); // notify subprogress done
1735 schoenebeck 800 }
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 persson 1179 store32(&pData[4], WavePoolCount);
1747 schoenebeck 800 // 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 schoenebeck 2682 // 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 schoenebeck 800 SampleList::iterator iter = pSamples->begin();
1758     SampleList::iterator end = pSamples->end();
1759 schoenebeck 2682 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 schoenebeck 800 }
1766 schoenebeck 2682
1767     __notify_progress(&subprogress, 1.0); // notify subprogress done
1768 schoenebeck 800 }
1769 schoenebeck 2682
1770     __notify_progress(pProgress, 1.0); // notify done
1771 schoenebeck 800 }
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 schoenebeck 2682 * @param pProgress - optional: callback function for progress notification
1786 schoenebeck 800 */
1787 schoenebeck 2682 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 schoenebeck 2609 UpdateFileOffsets();
1804 schoenebeck 2682 __notify_progress(pProgress, 1.0); // notify done
1805 schoenebeck 800 }
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 schoenebeck 2682 * @param pProgress - optional: callback function for progress notification
1814 schoenebeck 800 * @throws RIFF::Exception if any kind of IO error occured
1815     * @throws DLS::Exception if any kind of DLS specific error occured
1816     */
1817 schoenebeck 2682 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 schoenebeck 2609 UpdateFileOffsets();
1833 schoenebeck 2682 __notify_progress(pProgress, 1.0); // notify done
1834 schoenebeck 2609 }
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 schoenebeck 800 __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 schoenebeck 804 * '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 schoenebeck 800 *
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 schoenebeck 802 WavePoolCount = (pSamples) ? pSamples->size() : 0;
1885 schoenebeck 800 const unsigned long ulRequiredSize = WavePoolHeaderSize + iOffsetSize * WavePoolCount;
1886     if (ptbl->GetSize() < ulRequiredSize) throw Exception("Fatal error, 'ptbl' chunk too small");
1887 schoenebeck 804 // save the 'ptbl' chunk's current read/write position
1888     unsigned long ulOriginalPos = ptbl->GetPos();
1889 schoenebeck 800 // update headers
1890 schoenebeck 804 ptbl->SetPos(0);
1891 persson 1179 uint32_t tmp = WavePoolHeaderSize;
1892     ptbl->WriteUint32(&tmp);
1893     tmp = WavePoolCount;
1894     ptbl->WriteUint32(&tmp);
1895 schoenebeck 800 // update offsets
1896 schoenebeck 804 ptbl->SetPos(WavePoolHeaderSize);
1897 schoenebeck 800 if (b64BitWavePoolOffsets) {
1898     for (int i = 0 ; i < WavePoolCount ; i++) {
1899 persson 1179 tmp = pWavePoolTableHi[i];
1900     ptbl->WriteUint32(&tmp);
1901     tmp = pWavePoolTable[i];
1902     ptbl->WriteUint32(&tmp);
1903 schoenebeck 800 }
1904     } else { // conventional 32 bit offsets
1905 persson 1179 for (int i = 0 ; i < WavePoolCount ; i++) {
1906     tmp = pWavePoolTable[i];
1907     ptbl->WriteUint32(&tmp);
1908     }
1909 schoenebeck 800 }
1910 schoenebeck 804 // restore 'ptbl' chunk's original read/write position
1911     ptbl->SetPos(ulOriginalPos);
1912 schoenebeck 800 }
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 schoenebeck 802 WavePoolCount = (pSamples) ? pSamples->size() : 0;
1921 schoenebeck 800 // 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 schoenebeck 802 if (!pSamples) return;
1927 schoenebeck 800 // 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 schoenebeck 804 uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE;
1935 schoenebeck 800 (*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 schoenebeck 804 uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE;
1944 schoenebeck 800 (*iter)->ulWavePoolOffset = _64BitOffset;
1945     pWavePoolTable[i] = (uint32_t) _64BitOffset;
1946     }
1947     }
1948     }
1949    
1950    
1951    
1952 schoenebeck 2 // *************** 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 schoenebeck 518
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 schoenebeck 2 } // namespace DLS

  ViewVC Help
Powered by ViewVC