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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2985 - (show annotations) (download)
Tue Sep 20 22:13:37 2016 UTC (7 years, 6 months ago) by schoenebeck
File size: 18782 byte(s)
* gig.cpp/gig.h: Added new method Sample::VerifyWaveData() which
  allows to check whether a sample had been damaged for some
  reason.
* gigdump tool: added and implemented new parameter "--verify"
  which allows to check the raw wave form data integrity of all
  samples.
* gigdump tool: added and implemented new parameter
  "--rebuild-checksums" which allows to recalculate the CRC32
  checksum of all samples' raw wave data and rebuilding the gig
  file's global checksum table (i.e. in case the file's checksum
  table was damaged).
* Bumped version (4.0.0.svn8).

1 /***************************************************************************
2 * *
3 * libgig - C++ cross-platform Gigasampler format file access library *
4 * *
5 * Copyright (C) 2003-2016 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
30 // just for testing the disk streaming capability of libgig;
31 // you will also need to set this to 1 at the moment if you want to
32 // extract compressed samples, as I haven't implemented the
33 // decompression algorithm in gig::Sample::LoadSampleData() yet
34 #define USE_DISK_STREAMING 1
35
36 // only when USE_DISK_STREAMING is set to 1:
37 // just for testing the disk stream capability; with this option set
38 // gigextract will read the samples in smaller pieces, just to stress
39 // gig::Sample::Read() method a bit
40 #define HASHED_READS_TEST 1
41
42 #include <iostream>
43 #include <cstdlib>
44 #include <string.h>
45 #include <string>
46 #include <stdlib.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <errno.h>
50
51 #include "../gig.h"
52
53 #ifdef _MSC_VER
54 #define S_ISDIR(x) (S_IFDIR & (x))
55 #define S_IWUSR S_IWRITE
56 #define S_IXUSR S_IEXEC
57 #endif
58
59 #if POSIX
60 # include <dlfcn.h>
61 #endif
62
63 // only libsndfile is available for Windows, so we use that for writing the sound files
64 #ifdef WIN32
65 # define HAVE_SNDFILE 1
66 #endif // WIN32
67
68 // abort compilation here if neither libsndfile nor libaudiofile are available
69 #if !HAVE_SNDFILE && !HAVE_AUDIOFILE
70 # error "Neither libsndfile nor libaudiofile seem to be available!"
71 # error "(HAVE_SNDFILE and HAVE_AUDIOFILE are both false)"
72 #endif
73
74 // we prefer libsndfile before libaudiofile
75 #if HAVE_SNDFILE
76 # include <sndfile.h>
77 #else
78 # include <audiofile.h>
79 #endif // HAVE_SNDFILE
80
81 using namespace std;
82
83 typedef map<unsigned int, bool> OrderMap;
84 OrderMap* pOrderedSamples = NULL;
85
86 string Revision();
87 void PrintVersion();
88 void PrintUsage();
89 void ExtractSamples(gig::File* gig, char* destdir, OrderMap* ordered);
90 int writeWav(gig::Sample* sample, const char* filename, void* samples, long samplecount, int channels, int bitdepth, long rate);
91 string ToString(int i);
92
93 #if !HAVE_SNDFILE // use libaudiofile
94 void* hAFlib; // handle to libaudiofile
95 void openAFlib(void);
96 void closeAFlib(void);
97 // pointers to libaudiofile functions
98 AFfilesetup(*_afNewFileSetup)(void);
99 void(*_afFreeFileSetup)(AFfilesetup);
100 void(*_afInitChannels)(AFfilesetup,int,int);
101 void(*_afInitSampleFormat)(AFfilesetup,int,int,int);
102 void(*_afInitFileFormat)(AFfilesetup,int);
103 void(*_afInitRate)(AFfilesetup,int,double);
104 int(*_afWriteFrames)(AFfilehandle,int,const void*,int);
105 AFfilehandle(*_afOpenFile)(const char*,const char*,AFfilesetup);
106 int(*_afCloseFile)(AFfilehandle file);
107 #endif // !HAVE_SNDFILE
108
109 int main(int argc, char *argv[]) {
110 if (argc >= 2) {
111 if (argv[1][0] == '-') {
112 switch (argv[1][1]) {
113 case 'v':
114 PrintVersion();
115 return EXIT_SUCCESS;
116 }
117 }
118 }
119 if (argc < 3) {
120 PrintUsage();
121 return EXIT_FAILURE;
122 }
123 if (argc > 3) { // extracting specific samples
124 pOrderedSamples = new OrderMap;
125 for (int i = 3; i < argc; i++) {
126 unsigned int index = atoi(argv[i]);
127 (*pOrderedSamples)[index] = true;
128 }
129 }
130 FILE* hFile = fopen(argv[1], "r");
131 if (!hFile) {
132 cout << "Invalid input file argument!" << endl;
133 return EXIT_FAILURE;
134 }
135 fclose(hFile);
136 struct stat buf;
137 if (stat(argv[2], &buf) == -1) {
138 cout << "Unable to open DESTDIR: ";
139 switch (errno) {
140 case EACCES: cout << "Permission denied." << endl;
141 break;
142 case ENOENT: cout << "Directory does not exist, or name is an empty string." << endl;
143 break;
144 case ENOMEM: cout << "Insufficient memory to complete the operation." << endl;
145 break;
146 case ENOTDIR: cout << "Is not a directory." << endl;
147 break;
148 default: cout << "Unknown error" << endl;
149 }
150 return EXIT_FAILURE;
151 } else if (!S_ISDIR(buf.st_mode)) {
152 cout << "Unable to open DESTDIR: Is not a directory." << endl;
153 return EXIT_FAILURE;
154 } else if (!(S_IWUSR & buf.st_mode) || !(S_IXUSR & buf.st_mode)) {
155 cout << "Unable to open DESTDIR: Permission denied." << endl;
156 return EXIT_FAILURE;
157 }
158 try {
159 RIFF::File* riff = new RIFF::File(argv[1]);
160 gig::File* gig = new gig::File(riff);
161 cout << "Extracting samples from \"" << argv[1] << "\" to directory \"" << argv[2] << "\"." << endl << flush;
162 ExtractSamples(gig, argv[2], pOrderedSamples);
163 cout << "Extraction finished." << endl << flush;
164 delete gig;
165 delete riff;
166 if (pOrderedSamples) delete pOrderedSamples;
167 }
168 catch (RIFF::Exception e) {
169 e.PrintMessage();
170 return EXIT_FAILURE;
171 }
172 catch (...) {
173 cout << "Unknown exception while trying to parse file." << endl;
174 return EXIT_FAILURE;
175 }
176
177 return EXIT_SUCCESS;
178 }
179
180 static std::string getLoopTypeText(gig::loop_type_t type) {
181 switch (type) {
182 case gig::loop_type_normal:
183 return "normal";
184 case gig::loop_type_bidirectional:
185 return "pingpong";
186 case gig::loop_type_backward:
187 return "backward";
188 default:
189 return "INVALID";
190 }
191 }
192
193 static string replacePathSeparators(string s) {
194 for (int i = 0; i < s.size(); ++i)
195 if (s[i] == '/' || s[i] == '\\')
196 s[i] = '-';
197 return s;
198 }
199
200 void ExtractSamples(gig::File* gig, char* destdir, OrderMap* ordered) {
201 #if !HAVE_SNDFILE // use libaudiofile
202 hAFlib = NULL;
203 openAFlib();
204 #endif // !HAVE_SNDFILE
205 uint8_t* pWave = NULL;
206 int* pIntWave = NULL;
207 long BufferSize = 0;
208 int samples = 0;
209 gig::buffer_t decompressionBuffer;
210 decompressionBuffer.Size = 0;
211 cout << "Seeking for available samples..." << flush;
212 gig::Sample* pSample = gig->GetFirstSample();
213 cout << "OK" << endl << flush;
214 while (pSample) {
215 samples++;
216 if (ordered) {
217 if ((*ordered)[samples] == false) {
218 pSample = gig->GetNextSample();
219 continue;
220 }
221 }
222 string name = replacePathSeparators(pSample->pInfo->Name);
223 string filename = destdir;
224 if (filename[filename.size() - 1] != '/') filename += "/";
225 filename += ToString(samples);
226 filename += "_";
227 if (name == "") {
228 name = "(NO NAME)";
229 filename += "NONAME";
230 }
231 else {
232 filename += name;
233 name.insert(0, "\"");
234 name += "\"";
235 }
236 filename += ".wav";
237 if (pSample->Compressed) cout << "Decompressing ";
238 else cout << "Extracting ";
239 cout << "Sample " << samples << ") " << name << " (" << pSample->BitDepth <<"Bits, " << pSample->SamplesPerSecond << "Hz, " << pSample->Channels << " Channels, " << pSample->SamplesTotal << " Samples";
240 if (pSample->Loops > 0) {
241 cout << ", LoopType " << getLoopTypeText(pSample->LoopType)
242 << ", LoopStart " << pSample->LoopStart
243 << ", LoopEnd " << pSample->LoopEnd;
244 }
245 cout << ")..." << flush;
246
247
248 #if USE_DISK_STREAMING
249 long neededsize = pSample->BitDepth == 24 ?
250 pSample->SamplesTotal * pSample->Channels * sizeof(int) :
251 pSample->SamplesTotal * pSample->FrameSize;
252 if (BufferSize < neededsize) {
253 if (pWave) delete[] pWave;
254 pWave = new uint8_t[neededsize];
255 BufferSize = neededsize;
256 }
257 pIntWave = (int*)pWave;
258 # if HASHED_READS_TEST
259 unsigned long readinthisrun = 0,
260 samplepiecesize = 2000;
261 uint8_t* pSamplePiece = pWave;
262 do { // we read the sample in small pieces and increment the size with each run just to test streaming capability
263 readinthisrun = pSample->Read(pSamplePiece, ++samplepiecesize);
264 pSamplePiece += readinthisrun * pSample->FrameSize;
265 } while (readinthisrun == samplepiecesize);
266
267 # else // read in one piece
268 if (pSample->Compressed) {
269 if (decompressionBufferSize < pSample->SamplesTotal) {
270 gig::Sample::DestroyDecompressionBuffer(decompressionBuffer);
271 decompressionBuffer = gig::Sample::CreateDecompressionBuffer(pSample->SamplesTotal);
272 decompressionBufferSize = pSample->SamplesTotal;
273 }
274 pSample->Read(pWave, pSample->SamplesTotal, &decompressionBuffer);
275 } else {
276 pSample->Read(pWave, pSample->SamplesTotal);
277 }
278 # endif // HASHED_READS_TEST
279 #else // no disk streaming
280 if (pSample->Compressed) {
281 cout << "Sorry, sample is compressed and Sample::LoadSampleData() only decompresses the beginning of the sample - Solution: set USE_DISK_STREAMING in gigextract.cpp (line 32) to 1 and recompile!" << endl;
282 } else {
283 gig::buffer_t buffer = pSample->LoadSampleData(); // load wave into RAM
284 pWave = static_cast<uint8_t*>(buffer.pStart);
285 if (pSample->BitDepth == 24) {
286 long neededsize = pSample->SamplesTotal * pSample->Channels;
287 if (BufferSize < neededsize) {
288 if (pIntWave) delete[] pIntWave;
289 pIntWave = new int[neededsize];
290 BufferSize = neededsize;
291 }
292 }
293 }
294 #endif // USE_DISK_STREAMING
295 if (pWave) {
296
297 // Both libsndfile and libaudiofile uses int for 24 bit
298 // samples. libgig however returns 3 bytes per sample, so
299 // we have to convert the wave data before writing.
300 if (pSample->BitDepth == 24) {
301 int n = pSample->SamplesTotal * pSample->Channels;
302 for (int i = n - 1 ; i >= 0 ; i--) {
303 #if HAVE_SNDFILE
304 pIntWave[i] = pWave[i * 3] << 8 | pWave[i * 3 + 1] << 16 | pWave[i * 3 + 2] << 24;
305 #else
306 pIntWave[i] = pWave[i * 3] | pWave[i * 3 + 1] << 8 | pWave[i * 3 + 2] << 16;
307 #endif
308 }
309 }
310
311 int res = writeWav(pSample,
312 filename.c_str(),
313 pSample->BitDepth == 24 ? static_cast<void*>(pIntWave) : pWave,
314 pSample->SamplesTotal,
315 pSample->Channels,
316 pSample->BitDepth,
317 pSample->SamplesPerSecond);
318 if (res < 0) cout << "Couldn't write sample data." << endl;
319 else cout << "ok" << endl;
320 pSample->ReleaseSampleData(); // free wave from RAM
321 }
322 else cout << "Failed to load sample data." << endl;
323
324 pSample = gig->GetNextSample();
325 }
326 gig::Sample::DestroyDecompressionBuffer(decompressionBuffer);
327 #if USE_DISK_STREAMING
328 if (pWave) delete[] pWave;
329 #else
330 if (pIntWave) delete[] pIntWave;
331 #endif
332 #if !HAVE_SNDFILE // use libaudiofile
333 closeAFlib();
334 #endif // !HAVE_SNDFILE
335 }
336
337 int writeWav(gig::Sample* sample, const char* filename, void* samples, long samplecount, int channels, int bitdepth, long rate) {
338 #if HAVE_SNDFILE
339 SNDFILE* hfile;
340 SF_INFO sfinfo;
341 SF_INSTRUMENT instr;
342 int format = SF_FORMAT_WAV;
343 switch (bitdepth) {
344 case 8:
345 format |= SF_FORMAT_PCM_S8;
346 break;
347 case 16:
348 format |= SF_FORMAT_PCM_16;
349 break;
350 case 24:
351 format |= SF_FORMAT_PCM_24;
352 break;
353 case 32:
354 format |= SF_FORMAT_PCM_32;
355 break;
356 default:
357 cerr << "Error: Bithdepth " << ToString(bitdepth) << " not supported by libsndfile, ignoring sample!\n" << flush;
358 return -1;
359 }
360 memset(&sfinfo, 0, sizeof (sfinfo));
361 memset(&instr, 0, sizeof (instr));
362 sfinfo.samplerate = rate;
363 sfinfo.frames = samplecount;
364 sfinfo.channels = channels;
365 sfinfo.format = format;
366 if (!(hfile = sf_open(filename, SFM_WRITE, &sfinfo))) {
367 cerr << "Error: Unable to open output file \'" << filename << "\'.\n" << flush;
368 return -1;
369 }
370 instr.basenote = sample->MIDIUnityNote;
371 instr.detune = sample->FineTune;
372 if (sample->Loops > 0) {
373 instr.loop_count = 1;
374 switch (sample->LoopType) {
375 case gig::loop_type_normal:
376 instr.loops[0].mode = SF_LOOP_FORWARD;
377 break;
378 case gig::loop_type_bidirectional:
379 instr.loops[0].mode = SF_LOOP_ALTERNATING;
380 break;
381 case gig::loop_type_backward:
382 instr.loops[0].mode = SF_LOOP_BACKWARD;
383 break;
384 default:
385 instr.loops[0].mode = SF_LOOP_NONE;
386 break;
387 }
388 instr.loops[0].start = sample->LoopStart;
389 instr.loops[0].end = sample->LoopEnd;
390 instr.loops[0].count = sample->LoopPlayCount;
391 }
392 sf_command(hfile, SFC_SET_INSTRUMENT, &instr, sizeof(instr));
393 sf_count_t res = bitdepth == 24 ?
394 sf_write_int(hfile, static_cast<int*>(samples), channels * samplecount) :
395 sf_write_short(hfile, static_cast<short*>(samples), channels * samplecount);
396 if (res != channels * samplecount) {
397 cerr << sf_strerror(hfile) << endl << flush;
398 sf_close(hfile);
399 return -1;
400 }
401 sf_close(hfile);
402 #else // use libaudiofile
403 AFfilesetup setup = _afNewFileSetup();
404 _afInitFileFormat(setup, AF_FILE_WAVE);
405 _afInitChannels(setup, AF_DEFAULT_TRACK, channels);
406 _afInitSampleFormat(setup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, bitdepth);
407 _afInitRate(setup, AF_DEFAULT_TRACK, rate);
408 if (setup == AF_NULL_FILESETUP) return -1;
409 AFfilehandle hFile = _afOpenFile(filename, "w", setup);
410 if (hFile == AF_NULL_FILEHANDLE) return -1;
411 if (_afWriteFrames(hFile, AF_DEFAULT_TRACK, samples, samplecount) < 0) return -1;
412 _afCloseFile(hFile);
413 _afFreeFileSetup(setup);
414 #endif // HAVE_SNDFILE
415
416 return 0; // success
417 }
418
419 #if !HAVE_SNDFILE // use libaudiofile
420 void openAFlib() {
421 hAFlib = dlopen("libaudiofile.so", RTLD_NOW);
422 if (!hAFlib) {
423 cout << "Unable to load library libaudiofile.so: " << dlerror() << endl;
424 return;
425 }
426 _afNewFileSetup = (AFfilesetup(*)(void)) dlsym(hAFlib, "afNewFileSetup");
427 _afFreeFileSetup = (void(*)(AFfilesetup)) dlsym(hAFlib, "afFreeFileSetup");
428 _afInitChannels = (void(*)(AFfilesetup,int,int)) dlsym(hAFlib, "afInitChannels");
429 _afInitSampleFormat = (void(*)(AFfilesetup,int,int,int)) dlsym(hAFlib, "afInitSampleFormat");
430 _afInitFileFormat = (void(*)(AFfilesetup,int)) dlsym(hAFlib, "afInitFileFormat");
431 _afInitRate = (void(*)(AFfilesetup,int,double)) dlsym(hAFlib, "afInitRate");
432 _afWriteFrames = (int(*)(AFfilehandle,int,const void*,int)) dlsym(hAFlib, "afWriteFrames");
433 _afOpenFile = (AFfilehandle(*)(const char*,const char*,AFfilesetup)) dlsym(hAFlib, "afOpenFile");
434 _afCloseFile = (int(*)(AFfilehandle file)) dlsym(hAFlib, "afCloseFile");
435 if (dlerror()) cout << "Failed to load function from libaudiofile.so: " << dlerror() << endl;
436 }
437
438 void closeAFlib() {
439 if (hAFlib) dlclose(hAFlib);
440 }
441 #endif // !HAVE_SNDFILE
442
443 string Revision() {
444 string s = "$Revision$";
445 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
446 }
447
448 void PrintVersion() {
449 cout << "gigextract revision " << Revision() << endl;
450 cout << "using " << gig::libraryName() << " " << gig::libraryVersion();
451 #if HAVE_SNDFILE
452 char versionBuffer[128];
453 sf_command(NULL, SFC_GET_LIB_VERSION, versionBuffer, 128);
454 cout << ", " << versionBuffer;
455 #else // use libaudiofile
456 cout << "\nbuilt against libaudiofile "
457 << LIBAUDIOFILE_MAJOR_VERSION << "." << LIBAUDIOFILE_MINOR_VERSION;
458 # ifdef LIBAUDIOFILE_MICRO_VERSION
459 cout << "." << LIBAUDIOFILE_MICRO_VERSION;
460 # endif // LIBAUDIOFILE_MICRO_VERSION
461 #endif // HAVE_SNDFILE
462 cout << endl;
463 }
464
465 void PrintUsage() {
466 cout << "gigextract - extracts samples from a Gigasampler file." << endl;
467 cout << endl;
468 cout << "Usage: gigextract [-v] GIGFILE DESTDIR [SAMPLENR] [ [SAMPLENR] ...]" << endl;
469 cout << endl;
470 cout << " GIGFILE Input Gigasampler (.gig) file." << endl;
471 cout << endl;
472 cout << " DESTDIR Destination directory where all .wav files will be written to." << endl;
473 cout << endl;
474 cout << " SAMPLENR Index (/indices) of Sample(s) which should be extracted." << endl;
475 cout << " If no sample indices are given, all samples will be extracted" << endl;
476 cout << " (use gigdump to look for available samples)." << endl;
477 cout << endl;
478 cout << " -v Print version and exit." << endl;
479 cout << endl;
480 }
481
482 string ToString(int i) {
483 static char strbuf[1024];
484 sprintf(strbuf,"%d",i);
485 string s = strbuf;
486 return s;
487 }

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC