/[svn]/libgig/trunk/src/tools/gig2stereo.cpp
ViewVC logotype

Contents of /libgig/trunk/src/tools/gig2stereo.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3115 - (show annotations) (download)
Sat Apr 15 20:17:05 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 32840 byte(s)
* src/gig.cpp: Fixed CRC checksums being wrong sometimes.
* src/tools/gig2stereo.cpp: Also merge mono sample pairs
  with non matching loop information if argument
  "--incompatible" was given.
* Bumped version (4.0.0.svn13).

1 /***************************************************************************
2 * *
3 * libgig - C++ cross-platform Gigasampler format file access library *
4 * *
5 * Copyright (C) 2003-2015 by Christian Schoenebeck *
6 * <cuse@users.sourceforge.net> *
7 * *
8 * This program is part of libgig. *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the Free Software *
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
23 * MA 02111-1307 USA *
24 ***************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29 #include <errno.h>
30 #include <sys/stat.h>
31 #include <dirent.h>
32 #include <string.h>
33 #include <iostream>
34 #include <cstdlib>
35 #include <string>
36 #include <set>
37 #include <vector>
38 #include <map>
39 #include <utility>
40
41 #ifdef WIN32
42 # define DIR_SEPARATOR '\\'
43 #else
44 # define DIR_SEPARATOR '/'
45 #endif
46
47 #include "../gig.h"
48
49 using namespace std;
50
51 static set<string> g_files;
52
53 static void printUsage() {
54 cout << "gig2stereo - converts Gigasampler files from mono sample pairs to" << endl;
55 cout << " true stereo interleaved samples." << endl;
56 cout << endl;
57 cout << "Usage: gig2stereo [-r] [--keep] [--verbose [LEVEL]] [--force-replace] FILE_OR_DIR1 [ FILE_OR_DIR2 ... ]" << endl;
58 cout << " gig2stereo -v" << endl;
59 cout << endl;
60 cout << " --force-replace Replace all old mono references by the new stereo ones." << endl;
61 cout << endl;
62 cout << " --incompatible Also match sample pairs that seem to be incompatible." << endl;
63 cout << endl;
64 cout << " --keep Keep orphaned mono samples after conversion." << endl;
65 cout << endl;
66 cout << " -r Recurse through subdirectories." << endl;
67 cout << endl;
68 cout << " -v Print version and exit." << endl;
69 cout << endl;
70 cout << " --verbose Print additional information while converting." << endl;
71 cout << endl;
72 cout << "Read `man gig2stereo' for details." << endl;
73 cout << endl;
74 }
75
76 static string programRevision() {
77 string s = "$Revision$";
78 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
79 }
80
81 static void printVersion() {
82 cout << "gig2stereo revision " << programRevision() << endl;
83 cout << "using " << gig::libraryName() << " " << gig::libraryVersion() << endl;
84 }
85
86 static bool isGigFileName(const string path) {
87 const string t = ".gig";
88 return path.substr(path.length() - t.length()) == t;
89 }
90
91 static bool endsWith(const string& haystack, const string& needle, bool caseSensitive) {
92 const string sub = haystack.substr(haystack.size() - needle.size(), needle.size());
93 return (caseSensitive) ? (sub == needle) : (!strcasecmp(sub.c_str(), needle.c_str()));
94 }
95
96 /// Removes spaces from the beginning and end of @a s.
97 static string stripWhiteSpace(string s) {
98 // strip white space at the beginning
99 for (int i = 0; i < s.size(); ++i) {
100 if (s[i] != ' ') {
101 s = s.substr(i);
102 break;
103 }
104 }
105 // strip white space at the end
106 for (int i = s.size() - 1; i >= 0; --i) {
107 if (s[i] != ' ') {
108 s = s.substr(0, i+1);
109 break;
110 }
111 }
112 return s;
113 }
114
115 static void collectFilesOfDir(string path, bool bRecurse, bool* pbError = NULL) {
116 DIR* d = opendir(path.c_str());
117 if (!d) {
118 if (pbError) *pbError = true;
119 cerr << strerror(errno) << " : '" << path << "'" << endl;
120 return;
121 }
122
123 for (struct dirent* e = readdir(d); e; e = readdir(d)) {
124 if (string(e->d_name) == "." || string(e->d_name) == "..")
125 continue;
126
127 const string fullName = path + DIR_SEPARATOR + e->d_name;
128
129 struct stat s;
130 if (stat(fullName.c_str(), &s)) {
131 if (pbError) *pbError = true;
132 cerr << strerror(errno) << " : '" << fullName << "'" << endl;
133 continue;
134 }
135
136 if (S_ISREG(s.st_mode) && isGigFileName(fullName)) {
137 g_files.insert(fullName);
138 } else if (S_ISDIR(s.st_mode) && bRecurse) {
139 collectFilesOfDir(fullName, bRecurse, pbError);
140 }
141 }
142
143 closedir(d);
144 }
145
146 static void collectFiles(string path, bool bRecurse, bool* pbError = NULL) {
147 struct stat s;
148 if (stat(path.c_str(), &s)) {
149 if (pbError) *pbError = true;
150 cerr << strerror(errno) << " : '" << path << "'" << endl;
151 return;
152 }
153 if (S_ISREG(s.st_mode) && isGigFileName(path)) {
154 g_files.insert(path);
155 } else if (S_ISDIR(s.st_mode)) {
156 collectFilesOfDir(path, bRecurse, pbError);
157 } else {
158 if (pbError) *pbError = true;
159 cerr << "Neither a regular (.gig) file nor directory : '" << path << "'" << endl;
160 }
161 }
162
163 static string stripAudioChannelFromName(const string& s) {
164 if (endsWith(s, "-L", false) || endsWith(s, "-R", false) ||
165 endsWith(s, "_L", false) || endsWith(s, "_R", false) ||
166 endsWith(s, " L", false) || endsWith(s, " R", false) )
167 {
168 return s.substr(0, s.size() - 2);
169 }
170 if (endsWith(s, "-LEFT", false) || endsWith(s, "_LEFT", false) || endsWith(s, " LEFT", false) ) {
171 return s.substr(0, s.size() - 5);
172 }
173 if (endsWith(s, "-RIGHT", false) || endsWith(s, "_RIGHT", false) || endsWith(s, " RIGHT", false) ) {
174 return s.substr(0, s.size() - 6);
175 }
176 return s;
177 }
178
179 #define OPTIONAL_SKIP_CHECK() \
180 if (skipIncompatible) { \
181 cerr << " Skipping!\n"; \
182 continue; /* skip to convert this sample pair */ \
183 } else { \
184 cerr << " Merging anyway (upon request)!\n"; \
185 }
186
187 /**
188 * Converts .gig file given by @a path towards using true stereo interleaved
189 * samples.
190 *
191 * @param path - path and file name of .gig file to be converted
192 * @param keep - if true: do not delete the old mono samples, even if they are
193 * not referenced at all anymore after conversion
194 * @param forceReplace - By default certain references of the old mono samples
195 * are not replaced by the new true stereo samples,
196 * usually because the respective mono reference is been
197 * used in an instrument context that seems to be entirely
198 * mono, not stereo. By enabling this argument all those
199 * old references will be replaced by the new stereo
200 * sample reference instead as well.
201 * @param matchIncompatible - if set to true, only mono sample pairs will be
202 * qualified to be merged to true stereo samples if
203 * their main sample characteristics match, if set
204 * to true this sanity check will be skipped
205 * @param verbosity - verbosity level, defines whether and how much additional
206 * information to be printed to the console while doing the
207 * conversion (0 .. 2)
208 */
209 static bool convertFileToStereo(const string path, bool keep, bool forceReplace, bool skipIncompatible, int verbose) {
210 try {
211 // open .gig file
212 RIFF::File riff(path);
213 gig::File gig(&riff);
214
215 typedef pair<gig::DimensionRegion*, gig::DimensionRegion*> DimRgnPair;
216 typedef pair<gig::Sample*, gig::Sample*> SamplePair;
217
218 // find and collect all dimension regions with a 2 mono samples pair
219 if (verbose) cout << "Searching mono sample pairs ... " << flush;
220 if (verbose >= 2) cout << endl;
221 map<SamplePair, vector<DimRgnPair> > samplePairs;
222 map<gig::Sample*, SamplePair> samplePairsByL;
223 map<gig::Sample*, SamplePair> samplePairsByR;
224 for (gig::Instrument* instr = gig.GetFirstInstrument(); instr; instr = gig.GetNextInstrument()) {
225 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
226 int bits = 0;
227 for (int d = 0; d < 8; ++d) {
228 if (rgn->pDimensionDefinitions[d].dimension == gig::dimension_samplechannel) {
229 for (int dr = 0; dr < rgn->DimensionRegions && rgn->pDimensionRegions[dr]; ++dr) {
230 if (!rgn->pDimensionRegions[dr]->pSample) continue; // no sample assigned, ignore
231 if (rgn->pDimensionRegions[dr]->pSample->Channels != 1) continue; // seems already to be a true stereo sample, ignore
232
233 const int channel = (dr & (1 << bits)) ? 1 : 0;
234 if (channel == 1) continue; // ignore right audio channel dimension region here
235
236 // so current dimension region is for left channel, so now get its matching right channel dimension region ...
237 const int drR = dr | (1 << bits);
238 if (drR >= rgn->DimensionRegions || !rgn->pDimensionRegions[drR]) {
239 cerr << "WARNING: Could not find matching right channel for left channel dimension region!" << endl;
240 continue;
241 }
242 if (!rgn->pDimensionRegions[drR]->pSample) continue; // only a sample for left channel, but none for right channel, ignore
243 if (rgn->pDimensionRegions[drR]->pSample->Channels != 1) {
244 cerr << "WARNING: Right channel seems already to be a true stereo sample!" << endl;
245 continue;
246 }
247 if (rgn->pDimensionRegions[dr]->pSample == rgn->pDimensionRegions[drR]->pSample) continue; // both channels use same sample, thus actually a mono thing here, ignore
248
249 // found a valid mono pair, remember it
250 SamplePair samplePair(
251 rgn->pDimensionRegions[dr]->pSample,
252 rgn->pDimensionRegions[drR]->pSample
253 );
254 SamplePair samplePairInversed(
255 rgn->pDimensionRegions[drR]->pSample,
256 rgn->pDimensionRegions[dr]->pSample
257 );
258 DimRgnPair dimRgnpair(
259 rgn->pDimensionRegions[dr],
260 rgn->pDimensionRegions[drR]
261 );
262 if (samplePairs.count(samplePairInversed)) {
263 cerr << "WARNING: Sample pair ['" << rgn->pDimensionRegions[dr]->pSample->pInfo->Name << "', '" << rgn->pDimensionRegions[drR]->pSample->pInfo->Name << "'] already referenced in inverse channel order!\n";
264 } else {
265 if (samplePairsByL.count(rgn->pDimensionRegions[drR]->pSample)) {
266 cerr << "WARNING: Right mono sample '" << rgn->pDimensionRegions[drR]->pSample->pInfo->Name << "' already referenced as left sample!\n";
267 }
268 if (samplePairsByR.count(rgn->pDimensionRegions[dr]->pSample)) {
269 cerr << "WARNING: Left mono sample '" << rgn->pDimensionRegions[dr]->pSample->pInfo->Name << "' already referenced as right sample!\n";
270 }
271 }
272 samplePairs[samplePair].push_back(dimRgnpair);
273 if (verbose >= 2) cout << "Found mono sample pair ['" << rgn->pDimensionRegions[dr]->pSample->pInfo->Name << "', '" << rgn->pDimensionRegions[drR]->pSample->pInfo->Name << "'].\n" << flush;
274 }
275 goto nextRegion;
276 }
277 bits += rgn->pDimensionDefinitions[d].bits;
278 }
279 nextRegion:;
280 }
281 }
282 if (verbose) cout << samplePairs.size() << " mono pairs found.\n" << flush;
283
284 // create a true stereo (interleaved) sample for each one of the found
285 // mono sample pairs
286 if (verbose) cout << "Creating true stereo samples ... " << flush;
287 if (verbose >= 2) cout << endl;
288 map<SamplePair, vector<DimRgnPair> > samplePairsFiltered;
289 map<SamplePair, gig::Sample*> targetStereoSamples;
290 for (map<SamplePair, vector<DimRgnPair> >::iterator it = samplePairs.begin();
291 it != samplePairs.end(); ++it)
292 {
293 gig::Sample* pSampleL = it->first.first;
294 gig::Sample* pSampleR = it->first.second;
295
296 // bunch of sanity checks which ensure that both mono samples are
297 // using the same base parameter characteristics
298 if (pSampleL->SamplesPerSecond != pSampleR->SamplesPerSecond) {
299 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different sample rate! Skipping!\n";
300 continue;
301 }
302 if (pSampleL->FrameSize != pSampleR->FrameSize) {
303 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different frame size! Skipping!\n";
304 continue;
305 }
306 if (pSampleL->MIDIUnityNote != pSampleR->MIDIUnityNote) {
307 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different unity notes!";
308 OPTIONAL_SKIP_CHECK();
309 }
310 if (pSampleL->FineTune != pSampleR->FineTune) {
311 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different fine tune!";
312 OPTIONAL_SKIP_CHECK();
313 }
314 if (pSampleL->Loops != pSampleR->Loops) {
315 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop amount! Skipping!\n";
316 continue; // skip to convert this sample pair
317 }
318 if (pSampleL->Loops == 1 && pSampleR->Loops == 1) {
319 if (pSampleL->LoopType != pSampleR->LoopType) {
320 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop type!";
321 OPTIONAL_SKIP_CHECK();
322 }
323 if (pSampleL->LoopStart != pSampleR->LoopStart) {
324 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop start!";
325 OPTIONAL_SKIP_CHECK();
326 }
327 if (pSampleL->LoopEnd != pSampleR->LoopEnd) {
328 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop end!";
329 OPTIONAL_SKIP_CHECK();
330 }
331 if (pSampleL->LoopSize != pSampleR->LoopSize) {
332 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop size!";
333 OPTIONAL_SKIP_CHECK();
334 }
335 if (pSampleL->LoopPlayCount != pSampleR->LoopPlayCount) {
336 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop play count!";
337 OPTIONAL_SKIP_CHECK();
338 }
339 }
340
341 // create the stereo sample
342 gig::Sample* pStereoSample = gig.AddSample();
343
344 // add the new stereo sample to an appropriate sample group
345 const string groupName = stripWhiteSpace(stripAudioChannelFromName(pSampleL->GetGroup()->Name)) + " STEREO";
346 gig::Group* pGroup = NULL;
347 for (gig::Group* gr = gig.GetFirstGroup(); gr; gr = gig.GetNextGroup()) {
348 if (gr->Name == groupName) {
349 pGroup = gr;
350 break;
351 }
352 }
353 if (!pGroup) {
354 pGroup = gig.AddGroup();
355 pGroup->Name = groupName;
356 }
357 pGroup->AddSample(pStereoSample);
358
359 pStereoSample->pInfo->Name = stripWhiteSpace(stripAudioChannelFromName(pSampleL->pInfo->Name));
360 pStereoSample->Channels = 2;
361 pStereoSample->SamplesPerSecond = pSampleL->SamplesPerSecond;
362 pStereoSample->BitDepth = pSampleL->BitDepth;
363 pStereoSample->FrameSize = pSampleL->FrameSize * 2;
364 pStereoSample->MIDIUnityNote = pSampleL->MIDIUnityNote;
365 pStereoSample->Loops = pSampleL->Loops;
366 pStereoSample->LoopType = pSampleL->LoopType;
367 pStereoSample->LoopStart = pSampleL->LoopStart;
368 pStereoSample->LoopEnd = pSampleL->LoopEnd;
369 pStereoSample->LoopSize = pSampleL->LoopSize;
370 pStereoSample->LoopPlayCount = pSampleL->LoopPlayCount;
371
372 // libgig does not support saving of compressed samples
373 pStereoSample->Compressed = false;
374
375 // schedule new sample for resize (will be performed when gig.Save() is called)
376 const long newStereoSamplesTotal = max(pSampleL->SamplesTotal, pSampleR->SamplesTotal);
377 if (verbose >= 2) cout << "Resize new stereo sample '" << pStereoSample->pInfo->Name << "' to " << newStereoSamplesTotal << " sample points.\n" << flush;
378 pStereoSample->Resize(newStereoSamplesTotal);
379
380 samplePairsFiltered[it->first] = it->second;
381 targetStereoSamples[it->first] = pStereoSample;
382 }
383 if (verbose) cout << "Done.\n" << flush;
384
385 // increase the .gig file size appropriately
386 if (verbose) cout << "Increase file size ... " << flush;
387 gig.Save();
388 if (verbose) cout << "Done.\n" << flush;
389
390 vector<uint8_t> bufferMono[2];
391 vector<uint8_t> stereoBuffer;
392 gig::buffer_t decompressionBuffer;
393 unsigned long decompressionBufferSize = 0; // in sample points, not bytes
394
395 // do the actual sample conversion of each mono -> stereo sample by
396 // reading/writing directly from/to disk
397 if (verbose) cout << "Convert mono pair data to true stereo data ... " << flush;
398 if (verbose >= 2) cout << endl;
399 set<gig::Sample*> allAffectedMonoSamples;
400 for (map<SamplePair, vector<DimRgnPair> >::iterator it = samplePairsFiltered.begin();
401 it != samplePairsFiltered.end(); ++it)
402 {
403 gig::Sample* pSampleL = it->first.first;
404 gig::Sample* pSampleR = it->first.second;
405 gig::Sample* pSampleMono[2] = { pSampleL, pSampleR };
406 gig::Sample* pStereoSample = targetStereoSamples[it->first];
407
408 if ((pSampleMono[0]->SamplesTotal != pStereoSample->GetSize() &&
409 pSampleMono[1]->SamplesTotal != pStereoSample->GetSize()) || pStereoSample->GetSize() <= 0)
410 {
411 cerr << "Stereo length " << pStereoSample->GetSize() << ", mono L size " << pSampleL->SamplesTotal << ", mono R size " << pSampleR->SamplesTotal << "\n" << flush;
412 cerr << "CRITICAL ERROR: Ill sample sizes detected! Aborting.\n";
413 exit(-1);
414 }
415
416 allAffectedMonoSamples.insert(pSampleL);
417 allAffectedMonoSamples.insert(pSampleR);
418
419 const long neededStereoSize = pStereoSample->GetSize() * pStereoSample->FrameSize;
420 if (stereoBuffer.size() < neededStereoSize)
421 stereoBuffer.resize(neededStereoSize);
422
423 if (bufferMono[0].size() < neededStereoSize / 2) {
424 bufferMono[0].resize(neededStereoSize / 2);
425 bufferMono[1].resize(neededStereoSize / 2);
426 }
427 memset(&bufferMono[0][0], 0, bufferMono[0].size());
428 memset(&bufferMono[1][0], 0, bufferMono[1].size());
429
430 // read mono samples from disk
431 for (int iChan = 0; iChan < 2; ++iChan) {
432 pSampleMono[iChan]->SetPos(0);
433 unsigned long pointsRead = 0;
434 if (pSampleMono[iChan]->Compressed) {
435 if (decompressionBufferSize < pSampleMono[iChan]->SamplesTotal) {
436 decompressionBufferSize = pSampleMono[iChan]->SamplesTotal;
437 gig::Sample::DestroyDecompressionBuffer(decompressionBuffer);
438 decompressionBuffer = gig::Sample::CreateDecompressionBuffer(pSampleMono[iChan]->SamplesTotal);
439 }
440 pointsRead = pSampleMono[iChan]->Read(&bufferMono[iChan][0], pSampleMono[iChan]->SamplesTotal, &decompressionBuffer);
441 } else {
442 pointsRead = pSampleMono[iChan]->Read(&bufferMono[iChan][0], pSampleMono[iChan]->SamplesTotal);
443 }
444 if (pointsRead != pSampleMono[iChan]->SamplesTotal) {
445 cerr << "Read " << pointsRead << " sample points from mono sample " << ((iChan == 0) ? "L" : "R") << ".\n";
446 cerr << "CRITICAL ERROR: Reading entire mono sample data from disk failed! Aborting.\n";
447 exit(-1);
448 }
449 }
450
451 if (pSampleL->FrameSize != pSampleR->FrameSize || pStereoSample->FrameSize / 2 != pSampleL->FrameSize || pStereoSample->FrameSize < 1) {
452 cerr << "Stereo frame size " << pStereoSample->FrameSize << ", mono L frame size " << pSampleL->FrameSize << ", mono R frame size " << pSampleL->FrameSize << "\n" << flush;
453 cerr << "CRITICAL ERROR: Ill frame sizes detected! Aborting.\n";
454 exit(-1);
455 }
456
457 // convert from the 2 mono buffers to the stereo buffer
458 for (int s = 0; s < pStereoSample->GetSize(); ++s) {
459 memcpy(&stereoBuffer[s * pStereoSample->FrameSize], &bufferMono[0][s * pSampleL->FrameSize], pSampleL->FrameSize);
460 memcpy(&stereoBuffer[s * pStereoSample->FrameSize + pSampleL->FrameSize], &bufferMono[1][s * pSampleR->FrameSize], pSampleR->FrameSize);
461 }
462
463 // write converted stereo sample data directly to disk
464 if (verbose >= 2) cout << "Writing stereo sample '" << pStereoSample->pInfo->Name << "' (" << pStereoSample->GetSize() << " sample points).\n" << flush;
465 pStereoSample->SetPos(0);
466 unsigned long pointsWritten = pStereoSample->Write(&stereoBuffer[0], pStereoSample->GetSize());
467 if (verbose >= 2) cout << "Written " << pointsWritten << " sample points.\n" << flush;
468 }
469 if (verbose) cout << "Done.\n" << flush;
470
471 // decompression buffer not needed anymore
472 gig::Sample::DestroyDecompressionBuffer(decompressionBuffer);
473
474 // replace the old mono sample references by the new stereo sample
475 // references for all instruments of the .gig file
476 if (verbose) cout << "Replace old mono sample references by new stereo sample references ... " << flush;
477 for (map<SamplePair, vector<DimRgnPair> >::iterator it = samplePairsFiltered.begin();
478 it != samplePairsFiltered.end(); ++it)
479 {
480 if (forceReplace) {
481 // replace EVERY occurence of the old mono sample references by
482 // the new true stereo ones
483 gig::Sample* pMonoSamples[2] = { it->first.first, it->first.second };
484 gig::Sample* pStereoSample = targetStereoSamples[it->first];
485 for (int iChan = 0; iChan < 2; ++iChan) {
486 gig::Sample* pMonoSample = pMonoSamples[iChan];
487 // iterate through all instruments and replace all references of that sample
488 for (gig::Instrument* instr = gig.GetFirstInstrument(); instr; instr = gig.GetNextInstrument()) {
489 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
490 for (int dr = 0; dr < rgn->DimensionRegions; ++dr) {
491 if (!rgn->pDimensionRegions[dr])
492 continue;
493 if (rgn->pDimensionRegions[dr]->pSample == pMonoSample) {
494 rgn->pDimensionRegions[dr]->pSample = pStereoSample;
495 }
496 }
497 }
498 }
499 }
500 } else {
501 // only replace old mono sample references of instruments if
502 // they really seem to be used in a stereo context
503 const vector<DimRgnPair>& dimRgnPairs = it->second;
504 gig::Sample* pStereoSample = targetStereoSamples[it->first];
505 for (int drp = 0; drp < dimRgnPairs.size(); ++drp) {
506 dimRgnPairs[drp].first->pSample = pStereoSample;
507 dimRgnPairs[drp].second->pSample = pStereoSample;
508 }
509 }
510 }
511 if (verbose) cout << "Done.\n" << flush;
512
513 if (!keep) {
514 if (verbose) cout << "Removing orphaned mono samples ... " << flush;
515 int iDeletedSamples = 0;
516 // remove all old 2 mono sample pairs that are not referenced anymore
517 for (map<SamplePair, vector<DimRgnPair> >::iterator it = samplePairsFiltered.begin();
518 it != samplePairsFiltered.end(); ++it)
519 {
520 gig::Sample* pMonoSamples[2] = { it->first.first, it->first.second };
521 for (int iChan = 0; iChan < 2; ++iChan) {
522 gig::Sample* pMonoSample = pMonoSamples[iChan];
523
524 // iterate through all instruments and check if that
525 // old mono sample is still referenced somewhere
526 for (gig::Instrument* instr = gig.GetFirstInstrument(); instr; instr = gig.GetNextInstrument()) {
527 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
528 for (int dr = 0; dr < rgn->DimensionRegions; ++dr) {
529 if (!rgn->pDimensionRegions[dr])
530 continue;
531 if (rgn->pDimensionRegions[dr]->pSample == pMonoSample) {
532 std::cout << "NOTE: Mono sample '" + pMonoSample->pInfo->Name + "' is still referenced by instrument '" << instr->pInfo->Name << "'. Thus not deleting sample.\n";
533 goto nextSample; // still referenced, don't delete sample
534 }
535 }
536 }
537 }
538
539 // if this point is reached, then pMonoSample is not referenced
540 // by any instrument anymore, so delete this old mono sample now
541 {
542 gig::Group* pGroup = pMonoSample->GetGroup();
543 gig.DeleteSample(pMonoSample);
544 iDeletedSamples++;
545
546 // if deleted sample's group is now empty, delete the group
547 if (!pGroup->GetFirstSample()) gig.DeleteGroup(pGroup);
548 }
549
550 nextSample:;
551 }
552 }
553 if (verbose) {
554 cout << "Done (deleted " << iDeletedSamples << " of " << allAffectedMonoSamples.size() << " mono samples).\n" << flush;
555 if (iDeletedSamples < allAffectedMonoSamples.size())
556 cout << "(Use --force-replace to replace all old mono references.)\n" << flush;
557 }
558 }
559
560 // save sample reference changes (and drop deleted mono samples)
561 if (verbose) cout << "Saving final sample references to disk and shrink file ... " << flush;
562 gig.Save();
563 if (verbose) cout << "Done.\n" << flush;
564
565 } catch (RIFF::Exception e) {
566 cerr << "Failed converting file:" << endl;
567 e.PrintMessage();
568 return false; // error
569 } catch (...) {
570 cerr << "Unknown exception occurred while trying to convert file." << endl;
571 return false; // error
572 }
573 return true; // success
574 }
575
576 int main(int argc, char *argv[]) {
577 int nOptions = 0;
578 bool bOptionRecurse = false;
579 bool bOptionKeep = false;
580 bool bOptionForceReplace = false;
581 bool bOptionMatchIncompatible = false;
582 int iVerbose = 0;
583
584 // validate & parse arguments provided to this program
585 if (argc <= 1) {
586 printUsage();
587 return EXIT_FAILURE;
588 }
589 for (int i = 1; i < argc; ++i) {
590 const string opt = argv[i];
591 if (opt == "--") { // common for all command line tools: separator between initial option arguments and i.e. subsequent file arguments
592 nOptions++;
593 break;
594 }
595 if (opt.substr(0, 1) != "-") break;
596
597 if (opt == "-v") {
598 printVersion();
599 return EXIT_SUCCESS;
600 } else if (opt == "-r") {
601 nOptions++;
602 bOptionRecurse = true;
603 } else if (opt == "--keep") {
604 nOptions++;
605 bOptionKeep = true;
606 } else if (opt == "--force-replace") {
607 nOptions++;
608 bOptionForceReplace = true;
609 } else if (opt == "--incompatible") {
610 nOptions++;
611 bOptionMatchIncompatible = true;
612 } else if (opt == "--verbose") {
613 nOptions++;
614 iVerbose = 1;
615 if (i+1 < argc) {
616 char* s = argv[i+1];
617 char* endptr = NULL;
618 errno = 0;
619 long val = strtol(s, &endptr, 10);
620 if (errno != EINVAL && errno != ERANGE && endptr != s) {
621 if (val < 0) {
622 cerr << "Numeric argument for --verbose must not be negative!\n";
623 return -1;
624 }
625 iVerbose = val;
626 nOptions++;
627 i++;
628 }
629 }
630
631 }
632 }
633 const int nFileArguments = argc - nOptions - 1;
634 if (nFileArguments < 1) {
635 printUsage();
636 return EXIT_FAILURE;
637 }
638
639 bool bError = false;
640
641 // assemble list of .gig file names to be converted
642 for (int i = nOptions + 1; i < argc; ++i) {
643 collectFiles(argv[i], bOptionRecurse, &bError);
644 }
645 if (g_files.size() < 1) {
646 cerr << "No file given to be converted!" << endl;
647 return EXIT_FAILURE;
648 }
649 if (bError) {
650 cerr << "Aborted due to error. No file has been changed." << endl;
651 return EXIT_FAILURE;
652 }
653
654 // convert each file in the assembled list of file names
655 {
656 int i = 0;
657 for (set<string>::const_iterator it = g_files.begin(); it != g_files.end(); ++it, ++i) {
658 cout << "Converting file " << (i+1) << "/" << int(g_files.size()) << ": " << *it << " ... " << flush;
659 if (iVerbose) cout << endl;
660 bool bSuccess = convertFileToStereo(
661 *it, bOptionKeep, bOptionForceReplace, !bOptionMatchIncompatible,
662 iVerbose
663 );
664 if (!bSuccess) return EXIT_FAILURE;
665 cout << "OK" << endl;
666 }
667 }
668
669 return EXIT_SUCCESS;
670 }

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC