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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2543 - (show annotations) (download)
Sat May 10 02:06:58 2014 UTC (9 years, 11 months ago) by schoenebeck
File size: 30175 byte(s)
* Initial support for sample based instruments in KORG's file format (.KMP
  and .KSF files) -> Korg.h, Korg.cpp.
* Added new command line tool "korgdump" (and a man page for it).
* Added new command line tool "korg2gig" (and a man page for it), for
  converting KORG sounds to Giga format.
* riftree tool: Added more command line options for being able to also dump
  other kind of file formats similar but not equal to the RIFF format.
* gig.h/.cpp: Added new method File::GetGroup(String name) for retrieving
  group by name.
* RIFF.h/.cpp: Added support for loading RIFF-like files with a bit
  different layout than "real" RIFF files (used for KORG format support).
* RIFF.h/.cpp: Added new method Chunk::GetFile().
* RIFF.h/.cpp: Added new method Chunk::GetLayout().
* Bumped version (3.3.0.svn9).

1 /***************************************************************************
2 * *
3 * Copyright (C) 2014 Christian Schoenebeck *
4 * <cuse@users.sourceforge.net> *
5 * *
6 * This program is part of libgig. *
7 * *
8 * This program 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 program 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 program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <iostream>
29 #include <cstdlib>
30 #include <string>
31 #include <set>
32
33 #if !defined(WIN32)
34 # include <unistd.h>
35 #endif
36
37 #include "Korg.h"
38 #include "gig.h"
39
40 using namespace std;
41
42 static string Revision() {
43 string s = "$Revision$";
44 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
45 }
46
47 static void printVersion() {
48 cout << "korg2gig revision " << Revision() << endl;
49 cout << "using " << gig::libraryName() << " " << gig::libraryVersion() << endl;
50 }
51
52 static void printUsage() {
53 cout << "korg2gig - convert sound files from KORG to GigaStudio format." << endl;
54 cout << endl;
55 cout << "Usage: korg2gig [-v] [-f] [--interpret-names] FILE1 [ FILE2 ... ] NEWFILE" << endl;
56 cout << endl;
57 cout << " -v Print version and exit." << endl;
58 cout << endl;
59 cout << " -f Overwrite output file if it already exists." << endl;
60 cout << endl;
61 cout << " --interpret-names" << endl;
62 cout << " Try to guess left/right sample pairs and velocity splits." << endl;
63 cout << endl;
64 }
65
66 static bool endsWith(const string& haystack, const string& needle) {
67 return haystack.substr(haystack.size() - needle.size(), needle.size()) == needle;
68 }
69
70 static bool fileExists(const string& filename) {
71 FILE* hFile = fopen(filename.c_str(), "r");
72 if (!hFile) return false;
73 fclose(hFile);
74 return true;
75 }
76
77 // this could also be replaced by fopen(name, "w") to simply truncate the file to zero
78 static void deleteFile(const string& filename) {
79 #if defined(WIN32)
80 DeleteFile(filename.c_str());
81 #else
82 unlink(filename.c_str());
83 #endif
84 }
85
86 /**
87 * Intermediate container class which helps to group .KMP ("multi sample")
88 * instruments together, so that only one .gig instrument will be created per
89 * respective identified .KMP instrument group.
90 */
91 class InstrGroup : public vector<Korg::KMPInstrument*> {
92 public:
93 bool isStereo; ///< True if a stereo pair of sampls was found, will result in a "stereo dimension" being created in the .gig instrument.
94 bool hasVelocitySplits; ///< True if velocity split informations were identified in the .KMP ("multi sample") instrument name, a velocity dimension will be created accordingly.
95 string baseName; ///< This is the .KMP ("multi sample") instrument name with the numeric velecity range hints and stereo pair hints being stripped of, resulting in a shorter left hand string that all KMPinstruments have in common of this group.
96
97 typedef vector<Korg::KMPInstrument*> base_t;
98
99 InstrGroup () :
100 vector<Korg::KMPInstrument*>(),
101 isStereo(false), hasVelocitySplits(false)
102 {
103 }
104
105 InstrGroup (const InstrGroup& x) :
106 vector<Korg::KMPInstrument*>(x)
107 {
108 isStereo = x.isStereo;
109 hasVelocitySplits = x.hasVelocitySplits;
110 baseName = x.baseName;
111 }
112
113 InstrGroup& operator= (const InstrGroup& x) {
114 base_t::operator=(x);
115 isStereo = x.isStereo;
116 hasVelocitySplits = x.hasVelocitySplits;
117 baseName = x.baseName;
118 return *this;
119 }
120 };
121
122 /// Removes spaces from the beginning and end of @a s.
123 inline void stripWhiteSpace(string& s) {
124 // strip white space at the beginning
125 for (int i = 0; i < s.size(); ++i) {
126 if (s[i] != ' ') {
127 s = s.substr(i);
128 break;
129 }
130 }
131 // strip white space at the end
132 for (int i = s.size() - 1; i >= 0; --i) {
133 if (s[i] != ' ') {
134 s = s.substr(0, i+1);
135 break;
136 }
137 }
138 }
139
140 inline bool isDigit(const char& c) {
141 return c >= '0' && c <= '9';
142 }
143
144 inline int digitToInt(const char& c) {
145 return c - '0';
146 }
147
148 /**
149 * I.e. for @a s = "FOO 003-127" it sets @a from = 3, @a to = 127 and
150 * returns "FOO ".
151 *
152 * @param s - input string
153 * @param from - (output) numeric left part of range
154 * @param to - (output) numeric right part of range
155 * @returns input string without the numeric range string component
156 */
157 inline string parseNumberRangeAtEnd(const string& s, int& from, int& to) {
158 enum _State {
159 EXPECT_NUMBER1,
160 EXPECT_NUMBER1_OR_MINUS,
161 EXPECT_NUMBER2,
162 EXPECT_NUMBER2_OR_END
163 };
164 int state = EXPECT_NUMBER1;
165 from = to = -1;
166 int dec = 10;
167 for (int i = s.size() - 1; i >= 0; --i) {
168 switch (state) {
169 case EXPECT_NUMBER1:
170 if (isDigit(s[i])) {
171 to = digitToInt(s[i]);
172 state++;
173 } else return s; // unexpected, error
174 break;
175 case EXPECT_NUMBER1_OR_MINUS:
176 if (s[i] == '-') {
177 state++;
178 } else if (isDigit(s[i])) {
179 to += digitToInt(s[i]) * dec;
180 dec *= 10;
181 } else return s; // unexpected, error
182 break;
183 case EXPECT_NUMBER2:
184 if (isDigit(s[i])) {
185 from = digitToInt(s[i]);
186 dec = 10;
187 state++;
188 } else return s; // unexpected, error
189 break;
190 case EXPECT_NUMBER2_OR_END:
191 if (isDigit(s[i])) {
192 from += digitToInt(s[i]) * dec;
193 dec *= 10;
194 } else return s.substr(0, i+1);
195 break;
196 }
197 }
198 return (state == EXPECT_NUMBER2_OR_END) ? "" : s;
199 }
200
201 inline void stripLeftOrRightMarkerAtEnd(string& s);
202
203 /**
204 * I.e. for @a s = "FOO 003-127 -R" it returns "FOO" and sets @a from = 3 and
205 * @a to = 127. In case no number range was found (that is on parse errors),
206 * the returned string will equal the input string @a s instead.
207 */
208 inline string parseNumberRange(const string& s, int& from, int& to) {
209 string w = s;
210 stripWhiteSpace(w);
211 stripLeftOrRightMarkerAtEnd(w);
212 string result = parseNumberRangeAtEnd(w, from, to);
213 if (result == w) return s; // parse error occured, return original input s
214 stripWhiteSpace(result);
215 return result;
216 }
217
218 /// I.e. for @a s = "FOO 003-127" it returns "FOO".
219 inline string stripNumberRangeAtEnd(string s) {
220 stripWhiteSpace(s);
221 int from, to;
222 s = parseNumberRangeAtEnd(s, from, to);
223 stripWhiteSpace(s);
224 return s;
225 }
226
227 inline bool hasLeftOrRightMarker(string s) {
228 stripWhiteSpace(s);
229 return endsWith(s, "-L") || endsWith(s, "-R");
230 }
231
232 inline bool isLeftMarker(string s) {
233 stripWhiteSpace(s);
234 return endsWith(s, "-L");
235 }
236
237 inline void stripLeftOrRightMarkerAtEnd(string& s) {
238 if (hasLeftOrRightMarker(s)) {
239 s = s.substr(0, s.size() - 2);
240 stripWhiteSpace(s);
241 }
242 }
243
244 /// I.e. for @a name = "FOO 003-127 -R" it returns "FOO".
245 static string interpretBaseName(string name) {
246 // if there is "-L" or "-R" at the end, then remove it
247 stripLeftOrRightMarkerAtEnd(name);
248 // if there is a velocity split range indicator like "008-110", then remove it
249 string s = stripNumberRangeAtEnd(name);
250 return (s.empty()) ? name : s;
251 }
252
253 static bool hasNumberRangeIndicator(string name) {
254 int from, to;
255 string baseName = parseNumberRange(name, from, to);
256 return baseName != name;
257 }
258
259 inline InstrGroup* findGroupWithBaseName(vector<InstrGroup>& groups, const string& baseName) {
260 if (baseName.empty()) return NULL;
261 for (int i = 0; i < groups.size(); ++i)
262 if (groups[i].baseName == baseName)
263 return &groups[i];
264 return NULL;
265 }
266
267 /**
268 * If requested, try to group individual input instruments by looking at their
269 * instrument names (guessing left/right samples and velocity splits by name).
270 */
271 static vector<InstrGroup> groupInstruments(
272 const vector<Korg::KMPInstrument*>& instruments, bool bInterpretNames)
273 {
274 vector<InstrGroup> result;
275 if (!bInterpretNames) {
276 // no grouping requested, so make each input instrument its own group
277 for (vector<Korg::KMPInstrument*>::const_iterator it = instruments.begin();
278 it != instruments.end(); ++it)
279 {
280 InstrGroup g;
281 g.baseName = (*it)->Name();
282 g.push_back(*it);
283 result.push_back(g);
284 }
285 } else {
286 // try grouping the input instruments by instrument name interpretation
287 for (vector<Korg::KMPInstrument*>::const_iterator it = instruments.begin();
288 it != instruments.end(); ++it)
289 {
290 const string baseName = interpretBaseName((*it)->Name());
291 InstrGroup* g = findGroupWithBaseName(result, baseName);
292 if (!g) {
293 result.push_back(InstrGroup());
294 g = &result.back();
295 g->baseName = baseName;
296 }
297 g->push_back(*it);
298 g->isStereo |= hasLeftOrRightMarker( (*it)->Name() );
299 g->hasVelocitySplits |= hasNumberRangeIndicator( (*it)->Name() );
300 }
301 }
302 return result;
303 }
304
305 static set<DLS::range_t> collectVelocitySplits(const InstrGroup& group, DLS::range_t keyRange) {
306 set<DLS::range_t> velocityRanges;
307 for (int i = 0; i < group.size(); ++i) {
308 Korg::KMPInstrument* instr = group[i];
309 int iLowerKey = 0;
310 for (int k = 0; k < instr->GetRegionCount(); ++k) {
311 Korg::KMPRegion* rgn = instr->GetRegion(k);
312 DLS::range_t keyRange2 = { iLowerKey, rgn->TopKey };
313 iLowerKey = rgn->TopKey + 1; // for next loop cycle
314 if (keyRange == keyRange2) {
315 int from, to;
316 string baseName = parseNumberRange(instr->Name(), from, to);
317 if (baseName != instr->Name()) { // number range like "003-120" found in instrument name ...
318 DLS::range_t velRange = { from, to };
319 velocityRanges.insert(velRange);
320 }
321 }
322 }
323 }
324 return velocityRanges;
325 }
326
327 static vector<Korg::KSFSample*> ksfSamples; // input .KSF files
328 static vector<Korg::KMPInstrument*> kmpInstruments; // input .KMP files
329 static gig::File* g_gig = NULL; // output .gig file
330
331 static Korg::KSFSample* findKSFSampleWithFileName(const string& name) {
332 for (int i = 0; i < ksfSamples.size(); ++i)
333 if (ksfSamples[i]->FileName() == name)
334 return ksfSamples[i];
335 return NULL;
336 }
337
338 static map<Korg::KSFSample*,gig::Sample*> sampleRelations;
339
340 /**
341 * First pass: if the respective gig sample does not exist yet, it will be
342 * created, meta informations and size will be copied from the original Korg
343 * sample. However the actual sample data will not yet be copied here yet.
344 * Because the .gig file needs to be saved and resized in file size accordingly
345 * to the total size of all samples. For efficiency reasons the resize opertion
346 * is first just here schedued for all samples, then in a second pass the actual
347 * data written in writeAllSampleData();
348 */
349 static gig::Sample* findOrcreateGigSampleForKSFSample(Korg::KSFSample* ksfSample, gig::Group* gigSampleGroup, const Korg::KMPRegion* kmpRegion = NULL) {
350 if (ksfSample->SamplePoints <= 0) {
351 cout << "Skipping KSF sample '" << ksfSample->FileName() << "' (because of zero length)." << endl;
352 return NULL; // writing a gig sample with zero size to a gig file would throw an exception
353 }
354
355 gig::Sample* gigSample = sampleRelations[ksfSample];
356 if (gigSample) return gigSample;
357
358 // no such gig file yet, create it ...
359
360 // add new gig sample to gig output file
361 gigSample = g_gig->AddSample();
362 gigSampleGroup->AddSample(gigSample);
363 sampleRelations[ksfSample] = gigSample;
364 // convert Korg Sample -> gig Sample
365 gigSample->pInfo->Name = ksfSample->Name;
366 gigSample->Channels = ksfSample->Channels;
367 gigSample->SamplesPerSecond = ksfSample->SampleRate;
368 gigSample->BitDepth = ksfSample->BitDepth;
369 gigSample->FrameSize = ksfSample->Channels * ksfSample->BitDepth / 8;
370 //gigSample->SamplesTotal = ksfSample->SamplePoints;
371 if (kmpRegion) {
372 gigSample->MIDIUnityNote = kmpRegion->OriginalKey;
373 }
374 if (ksfSample->LoopEnd) {
375 gigSample->Loops = 1; // the gig format currently supports only one loop per sample
376 gigSample->LoopType = gig::loop_type_normal;
377 gigSample->LoopStart = ksfSample->LoopStart;
378 gigSample->LoopEnd = ksfSample->LoopEnd;
379 gigSample->LoopSize = gigSample->LoopEnd - gigSample->LoopStart;
380 gigSample->LoopPlayCount = 0; // infinite
381 }
382 // schedule for resize (will be performed when gig->Save() is called)
383 gigSample->Resize(ksfSample->SamplePoints);
384
385 return gigSample;
386 }
387
388 /**
389 * Second pass: see comment of findOrcreateGigSampleForKSFSample().
390 */
391 static void writeAllSampleData() {
392 for (map<Korg::KSFSample*,gig::Sample*>::iterator it = sampleRelations.begin();
393 it != sampleRelations.end(); ++it)
394 {
395 Korg::KSFSample* ksfSample = it->first;
396 gig::Sample* gigSample = it->second;
397 if (!gigSample) continue; // can be NULL if the source KORG file had zero length (attempting to write a .gig file with zero size sample would throw an exception)
398 Korg::buffer_t src = ksfSample->LoadSampleData();
399 gigSample->SetPos(0);
400 gigSample->Write(src.pStart, ksfSample->SamplePoints);
401 ksfSample->ReleaseSampleData();
402 }
403 }
404
405 static gig::Sample* findOrCreateGigSampleForKSFRegion(const Korg::KMPRegion* kmpRegion) {
406 int from, to;
407 const string baseName = parseNumberRange(kmpRegion->GetInstrument()->Name(), from, to);
408
409 gig::Group* gigSampleGroup = g_gig->GetGroup(baseName);
410 if (!gigSampleGroup) {
411 gigSampleGroup = g_gig->AddGroup();
412 gigSampleGroup->Name = baseName;
413 }
414
415 Korg::KSFSample* ksfSample = findKSFSampleWithFileName(kmpRegion->FullSampleFileName());
416 if (!ksfSample)
417 throw Korg::Exception("Internal error: Could not resolve KSFSample object");
418 gig::Sample* gigSample = sampleRelations[ksfSample];
419 if (gigSample) return gigSample;
420 return findOrcreateGigSampleForKSFSample(
421 ksfSample, gigSampleGroup, kmpRegion
422 );
423 }
424
425 static void loadKorgFile(const string& filename, bool bReferenced = false) {
426 try {
427 if (endsWith(filename, ".KMP")) {
428 cout << "Loading KORG Multi Sample file '" << filename << "' ... " << flush;
429 Korg::KMPInstrument* instr = new Korg::KMPInstrument(filename);
430 cout << "OK\n";
431 kmpInstruments.push_back(instr);
432
433 for (int i = 0; i < instr->GetRegionCount(); ++i) {
434 Korg::KMPRegion* rgn = instr->GetRegion(i);
435 // check if the sample referenced by this region was already
436 // loaded, if not then load it ...
437 if (!findKSFSampleWithFileName(rgn->FullSampleFileName()))
438 loadKorgFile(rgn->FullSampleFileName(), true);
439 }
440 } else if (endsWith(filename, ".KSF")) {
441 cout << "Loading " << (bReferenced ? "referenced " : "") << "KORG Sample file '" << filename << "' ... " << flush;
442 Korg::KSFSample* smpl = new Korg::KSFSample(filename);
443 cout << "OK\n";
444 ksfSamples.push_back(smpl);
445 } else if (endsWith(filename, ".PCG")) {
446 cerr << "Error with input file '" << filename << "':" << endl;
447 cerr << "There is no support for .PCG files in this version of korg2gig yet." << endl;
448 exit(EXIT_FAILURE);
449 } else {
450 cerr << "Unknown file type (file name postfix) for input file '" << filename << "'" << endl;
451 exit(EXIT_FAILURE);
452 }
453 } catch (RIFF::Exception e) {
454 cerr << "Failed opening input file '" << filename << "':" << endl;
455 e.PrintMessage();
456 exit(EXIT_FAILURE);
457 } catch (...) {
458 cerr << "Failed opening input file '" << filename << "':" << endl;
459 cerr << "Unknown exception occured while trying to access input file." << endl;
460 exit(EXIT_FAILURE);
461 }
462 }
463
464 static void cleanup() {
465 if (g_gig) {
466 delete g_gig;
467 g_gig = NULL;
468 }
469 for (int i = 0; i < ksfSamples.size(); ++i) delete ksfSamples[i];
470 ksfSamples.clear();
471 for (int i = 0; i < kmpInstruments.size(); ++i) delete kmpInstruments[i];
472 kmpInstruments.clear();
473 }
474
475 inline int getDimensionIndex(gig::Region* region, gig::dimension_t type) {
476 for (int d = 0; d < region->Dimensions; ++d)
477 if (region->pDimensionDefinitions[d].dimension == type)
478 return d;
479 return -1;
480 }
481
482 int main(int argc, char *argv[]) {
483 bool bInterpretNames = false;
484 bool bForce = false;
485
486 // validate & parse arguments provided to this program
487 if (argc < 3) {
488 printUsage();
489 return EXIT_FAILURE;
490 }
491 int iArg;
492 for (iArg = 1; iArg < argc; ++iArg) {
493 const string opt = argv[iArg];
494 if (opt == "--") { // common for all command line tools: separator between initial option arguments and i.e. subsequent file arguments
495 iArg++;
496 break;
497 }
498 if (opt.substr(0, 1) != "-") break;
499
500 if (opt == "-v") {
501 printVersion();
502 return EXIT_SUCCESS;
503 } else if (opt == "-f") {
504 bForce = true;
505 } else if (opt == "--interpret-names") {
506 bInterpretNames = true;
507 } else {
508 cerr << "Unknown option '" << opt << "'" << endl;
509 cerr << endl;
510 printUsage();
511 return EXIT_FAILURE;
512 }
513 }
514
515 set<string> inFileNames;
516 string outFileName;
517
518 // all options have been processed, all subsequent args should be files
519 for (; iArg < argc; ++iArg) {
520 if (iArg == argc - 1) {
521 outFileName = argv[iArg];
522 } else {
523 inFileNames.insert(argv[iArg]);
524 }
525 }
526 if (inFileNames.empty() || outFileName.empty()) {
527 cerr << "You must provide at least one input file (Korg format) and one output file (.gig format)!" << endl;
528 return EXIT_FAILURE;
529 }
530
531 // check if output file already exists
532 if (fileExists(outFileName)) {
533 if (bForce) deleteFile(outFileName);
534 else {
535 cerr << "Output file '" << outFileName << "' already exists. Use -f to overwrite it." << endl;
536 return EXIT_FAILURE;
537 }
538 }
539
540 // open all input (KORG) files
541 for (set<string>::const_iterator it = inFileNames.begin();
542 it != inFileNames.end(); ++it)
543 {
544 loadKorgFile(*it);
545 }
546
547 // create and assemble a new .gig file as output
548 try {
549 // start with an empty .gig file
550 g_gig = new gig::File();
551
552 // if requested, try to group individual input instruments by looking
553 // at their names, and create only one output instrument per group
554 // (guessing left/right samples and velocity splits by name)
555 vector<InstrGroup> instrGroups = groupInstruments(kmpInstruments, bInterpretNames);
556
557 // create the individual instruments in the gig file
558 for (int i = 0; i < instrGroups.size(); ++i) {
559 InstrGroup& group = instrGroups[i];
560 cout << "Adding gig instrument '" << group.baseName << "'" << endl;
561
562 gig::Instrument* instr = g_gig->AddInstrument();
563 instr->pInfo->Name = group.baseName;
564
565 map<DLS::range_t, gig::Region*> regions; // the gig instrument regions to be created, sorted by key ranges
566
567 // apply korg regions to gig regions
568 for (InstrGroup::iterator itInstr = group.begin();
569 itInstr != group.end(); ++itInstr)
570 {
571 Korg::KMPInstrument* kmpInstr = *itInstr;
572 cout << " |---> KMP multi sample '" << kmpInstr->Name() << "'" << endl;
573
574 int iLowKey = 0;
575 for (int k = 0; k < kmpInstr->GetRegionCount(); ++k) {
576 Korg::KMPRegion* kmpRegion = kmpInstr->GetRegion(k);
577 DLS::range_t keyRange = { iLowKey, kmpRegion->TopKey };
578 iLowKey = kmpRegion->TopKey + 1; // for next loop cycle
579 gig::Region* gigRegion = regions[keyRange];
580
581 // if there is no gig region for that key range yet, create it
582 if (!gigRegion) {
583 gigRegion = instr->AddRegion();
584 gigRegion->SetKeyRange(keyRange.low, keyRange.high);
585 regions[keyRange] = gigRegion;
586 }
587
588 uint8_t iDimBits[8] = {}; // used to select the target gig::DimensionRegion for current source KMPRegion to be applied
589
590 bool isStereoSplit = bInterpretNames && hasLeftOrRightMarker(kmpInstr->Name());
591 if (isStereoSplit) { // stereo dimension split required for this region ...
592 int iStereoDimensionIndex = getDimensionIndex(gigRegion, gig::dimension_samplechannel);
593
594 // create stereo dimension if it doesn't exist already ...
595 if (iStereoDimensionIndex < 0) {
596 gig::dimension_def_t dim;
597 dim.dimension = gig::dimension_samplechannel;
598 dim.bits = 1; // 2^(1) = 2
599 dim.zones = 2; // stereo = 2 audio channels = 2 split zones
600 gigRegion->AddDimension(&dim);
601
602 iStereoDimensionIndex = getDimensionIndex(gigRegion, gig::dimension_samplechannel);
603 }
604
605 if (iStereoDimensionIndex < 0)
606 throw gig::Exception("Internal error: Could not resolve target stereo dimension bit");
607
608 // select dimension bit for this stereo dimension split
609 iDimBits[iStereoDimensionIndex] = isLeftMarker(kmpInstr->Name()) ? 0 : 1;
610 }
611
612 int iVelocityDimensionIndex = -1;
613 DLS::range_t velRange = { 0 , 127 };
614 bool isVelocitySplit = bInterpretNames && hasNumberRangeIndicator(kmpInstr->Name());
615 if (isVelocitySplit) { // a velocity split is required for this region ...
616 // get the amount of velocity split zones for this
617 // particular output instrument and key range
618 const set<DLS::range_t> velocitySplits = collectVelocitySplits(group, keyRange);
619
620 // get the velocity range for current KORG region
621 {
622 int from, to;
623 if (parseNumberRange(kmpInstr->Name(), from, to) == kmpInstr->Name())
624 throw Korg::Exception("Internal error: parsing velocity range failed");
625 velRange.low = from;
626 velRange.high = to;
627 }
628
629 // create velocity split dimension if it doesn't exist already ...
630 iVelocityDimensionIndex = getDimensionIndex(gigRegion, gig::dimension_velocity);
631 if (iVelocityDimensionIndex < 0) {
632 gig::dimension_def_t dim;
633 dim.dimension = gig::dimension_velocity;
634 dim.zones = velocitySplits.size();
635
636 // Find the number of bits required to hold the
637 // specified amount of zones.
638 int zoneBits = dim.zones - 1;
639 for (dim.bits = 0; zoneBits > 1; dim.bits += 2, zoneBits >>= 2);
640 dim.bits += zoneBits;
641
642 gigRegion->AddDimension(&dim);
643
644 iVelocityDimensionIndex = getDimensionIndex(gigRegion, gig::dimension_velocity);
645 }
646
647 if (iVelocityDimensionIndex < 0)
648 throw gig::Exception("Internal error: Could not resolve target velocity dimension bit");
649
650 // find the velocity zone for this one
651 int iVelocitySplitZone = -1;
652 {
653 int i = 0;
654 for (set<DLS::range_t>::const_iterator itVelSplit = velocitySplits.begin();
655 itVelSplit != velocitySplits.end(); ++itVelSplit, ++i)
656 {
657 if (*itVelSplit == velRange) {
658 iVelocitySplitZone = i;
659 break;
660 }
661 }
662 if (iVelocitySplitZone == -1)
663 throw gig::Exception("Internal error: Could not resolve target velocity dimension zone");
664 }
665
666 // select dimension bit for this stereo dimension split
667 iDimBits[iVelocityDimensionIndex] = iVelocitySplitZone;
668 }
669
670 // resolve target gig::DimensionRegion for the left/right and velocity split zone detected above
671 gig::DimensionRegion* dimRgn = gigRegion->GetDimensionRegionByBit(iDimBits);
672 if (!dimRgn)
673 throw gig::Exception("Internal error: Could not resolve Dimension Region");
674
675 // if this is a velocity split, apply the precise velocity split range values
676 if (isVelocitySplit) {
677 dimRgn->VelocityUpperLimit = velRange.high; // gig v2
678 dimRgn->DimensionUpperLimits[iVelocityDimensionIndex] = velRange.high; // gig v3 and above
679 }
680
681 // assign the respective gig sample to this dimension region
682 gig::Sample* gigSample = findOrCreateGigSampleForKSFRegion(kmpRegion);
683 dimRgn->pSample = gigSample; // might be NULL (if Korg sample had zero size)
684 if (gigSample) {
685 dimRgn->UnityNote = gigSample->MIDIUnityNote;
686 if (gigSample->Loops) {
687 DLS::sample_loop_t loop;
688 loop.Size = sizeof(loop);
689 loop.LoopType = gig::loop_type_normal;
690 loop.LoopStart = gigSample->LoopStart;
691 loop.LoopLength = gigSample->LoopEnd - gigSample->LoopStart;
692 dimRgn->AddSampleLoop(&loop);
693 }
694 }
695 }
696 }
697 }
698
699 // add the .KSF samples that are not referenced by any instrument
700 for (int i = 0; i < ksfSamples.size(); ++i) {
701 Korg::KSFSample* ksfSample = ksfSamples[i];
702 gig::Sample* gigSample = sampleRelations[ksfSample];
703 if (!gigSample) {
704 // put those "orphaned" samples into a sample group called
705 // "Not referenced", showing the user in gigedit that this
706 // sample is not used (referenced) by any gig instrument
707 const string sNotReferenced = "Not referenced";
708 gig::Group* gigSampleGroup = g_gig->GetGroup(sNotReferenced);
709 if (!gigSampleGroup) {
710 gigSampleGroup = g_gig->AddGroup();
711 gigSampleGroup->Name = sNotReferenced;
712 }
713 // create the gig sample
714 gigSample = findOrcreateGigSampleForKSFSample(ksfSample, gigSampleGroup);
715 }
716 }
717
718 // save result to disk (as .gig file)
719 cout << "Saving converted (.gig) file to '" << outFileName << "' ... " << flush;
720 g_gig->Save(outFileName);
721 cout << "OK\n";
722 cout << "Writing all samples to converted (.gig) file ... " << flush;
723 writeAllSampleData();
724 cout << "OK\n";
725 } catch (RIFF::Exception e) {
726 cerr << "Failed generating output file:" << endl;
727 e.PrintMessage();
728 cleanup();
729 return EXIT_FAILURE;
730 } catch (...) {
731 cerr << "Unknown exception while trying to assemble output file." << endl;
732 cleanup();
733 return EXIT_FAILURE;
734 }
735
736 cleanup();
737 return EXIT_SUCCESS;
738 }

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC