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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 902 - (show annotations) (download)
Sat Jul 22 14:22:01 2006 UTC (17 years, 9 months ago) by persson
File size: 16724 byte(s)
* real support for 24 bit samples
* support for reading of .art files

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

  ViewVC Help
Powered by ViewVC