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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2985 - (hide annotations) (download)
Tue Sep 20 22:13:37 2016 UTC (7 years, 7 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 schoenebeck 2 /***************************************************************************
2     * *
3 schoenebeck 933 * libgig - C++ cross-platform Gigasampler format file access library *
4 schoenebeck 2 * *
5 schoenebeck 2985 * Copyright (C) 2003-2016 by Christian Schoenebeck *
6 schoenebeck 518 * <cuse@users.sourceforge.net> *
7 schoenebeck 2 * *
8 schoenebeck 933 * This program is part of libgig. *
9     * *
10 schoenebeck 2 * 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 schoenebeck 220 #include <string.h>
45 schoenebeck 518 #include <string>
46 schoenebeck 2 #include <stdlib.h>
47     #include <sys/types.h>
48     #include <sys/stat.h>
49     #include <errno.h>
50 schoenebeck 220
51 persson 2577 #include "../gig.h"
52 schoenebeck 1063
53 persson 1869 #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 schoenebeck 1063 #if POSIX
60     # include <dlfcn.h>
61     #endif
62    
63 schoenebeck 220 // 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 schoenebeck 608 // 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 schoenebeck 220 // 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 schoenebeck 2 using namespace std;
82    
83     typedef map<unsigned int, bool> OrderMap;
84     OrderMap* pOrderedSamples = NULL;
85    
86 schoenebeck 518 string Revision();
87     void PrintVersion();
88 schoenebeck 2 void PrintUsage();
89     void ExtractSamples(gig::File* gig, char* destdir, OrderMap* ordered);
90 schoenebeck 2465 int writeWav(gig::Sample* sample, const char* filename, void* samples, long samplecount, int channels, int bitdepth, long rate);
91 schoenebeck 220 string ToString(int i);
92    
93 schoenebeck 608 #if !HAVE_SNDFILE // use libaudiofile
94 schoenebeck 2 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 schoenebeck 220 #endif // !HAVE_SNDFILE
108 schoenebeck 2
109     int main(int argc, char *argv[]) {
110 schoenebeck 518 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 schoenebeck 2 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 persson 1869 struct stat buf;
137     if (stat(argv[2], &buf) == -1) {
138 schoenebeck 2 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 persson 1869 } 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 schoenebeck 2 }
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 schoenebeck 2465 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 schoenebeck 2984 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 schoenebeck 2 void ExtractSamples(gig::File* gig, char* destdir, OrderMap* ordered) {
201 schoenebeck 608 #if !HAVE_SNDFILE // use libaudiofile
202 schoenebeck 2 hAFlib = NULL;
203     openAFlib();
204 schoenebeck 220 #endif // !HAVE_SNDFILE
205 schoenebeck 2 uint8_t* pWave = NULL;
206 persson 902 int* pIntWave = NULL;
207 schoenebeck 2 long BufferSize = 0;
208     int samples = 0;
209 persson 902 gig::buffer_t decompressionBuffer;
210     decompressionBuffer.Size = 0;
211 schoenebeck 2 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 schoenebeck 2984 string name = replacePathSeparators(pSample->pInfo->Name);
223 schoenebeck 2 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 schoenebeck 2465 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 schoenebeck 2
247    
248     #if USE_DISK_STREAMING
249 persson 902 long neededsize = pSample->BitDepth == 24 ?
250     pSample->SamplesTotal * pSample->Channels * sizeof(int) :
251     pSample->SamplesTotal * pSample->FrameSize;
252 schoenebeck 2 if (BufferSize < neededsize) {
253     if (pWave) delete[] pWave;
254     pWave = new uint8_t[neededsize];
255     BufferSize = neededsize;
256     }
257 persson 902 pIntWave = (int*)pWave;
258 schoenebeck 2 # if HASHED_READS_TEST
259 persson 902 unsigned long readinthisrun = 0,
260 schoenebeck 2 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 persson 902 pSamplePiece += readinthisrun * pSample->FrameSize;
265 schoenebeck 2 } while (readinthisrun == samplepiecesize);
266    
267     # else // read in one piece
268     if (pSample->Compressed) {
269 persson 902 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 schoenebeck 2 pSample->Read(pWave, pSample->SamplesTotal);
277     }
278     # endif // HASHED_READS_TEST
279     #else // no disk streaming
280     if (pSample->Compressed) {
281 persson 902 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 schoenebeck 2 }
294     #endif // USE_DISK_STREAMING
295     if (pWave) {
296 persson 902
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 schoenebeck 2465 int res = writeWav(pSample,
312     filename.c_str(),
313 persson 902 pSample->BitDepth == 24 ? static_cast<void*>(pIntWave) : pWave,
314 schoenebeck 2 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 persson 902 gig::Sample::DestroyDecompressionBuffer(decompressionBuffer);
327     #if USE_DISK_STREAMING
328     if (pWave) delete[] pWave;
329     #else
330     if (pIntWave) delete[] pIntWave;
331     #endif
332 schoenebeck 608 #if !HAVE_SNDFILE // use libaudiofile
333 schoenebeck 2 closeAFlib();
334 schoenebeck 220 #endif // !HAVE_SNDFILE
335 schoenebeck 2 }
336    
337 schoenebeck 2465 int writeWav(gig::Sample* sample, const char* filename, void* samples, long samplecount, int channels, int bitdepth, long rate) {
338 schoenebeck 220 #if HAVE_SNDFILE
339     SNDFILE* hfile;
340     SF_INFO sfinfo;
341 schoenebeck 2465 SF_INSTRUMENT instr;
342 schoenebeck 220 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 schoenebeck 2465 memset(&instr, 0, sizeof (instr));
362 schoenebeck 220 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 schoenebeck 2465 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 persson 902 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 schoenebeck 220 cerr << sf_strerror(hfile) << endl << flush;
398     sf_close(hfile);
399     return -1;
400     }
401     sf_close(hfile);
402 schoenebeck 608 #else // use libaudiofile
403 schoenebeck 2 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 schoenebeck 220 #endif // HAVE_SNDFILE
415 schoenebeck 55
416 schoenebeck 220 return 0; // success
417 schoenebeck 2 }
418    
419 schoenebeck 608 #if !HAVE_SNDFILE // use libaudiofile
420 schoenebeck 2 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 schoenebeck 220 #endif // !HAVE_SNDFILE
442 schoenebeck 2
443 schoenebeck 518 string Revision() {
444 schoenebeck 2493 string s = "$Revision$";
445 schoenebeck 518 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 schoenebeck 549 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 schoenebeck 608 #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 schoenebeck 549 #endif // HAVE_SNDFILE
462     cout << endl;
463 schoenebeck 518 }
464    
465 schoenebeck 2 void PrintUsage() {
466     cout << "gigextract - extracts samples from a Gigasampler file." << endl;
467     cout << endl;
468 schoenebeck 518 cout << "Usage: gigextract [-v] GIGFILE DESTDIR [SAMPLENR] [ [SAMPLENR] ...]" << endl;
469 schoenebeck 2 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 schoenebeck 518 cout << " -v Print version and exit." << endl;
479     cout << endl;
480 schoenebeck 2 }
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