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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1063 - (show annotations) (download)
Sat Mar 3 21:45:25 2007 UTC (17 years, 1 month ago) by schoenebeck
File size: 16898 byte(s)
* fixed libgig's Dev-C++ project file to produce an actually working
  Windows DLL file (mandatory symbols were not exported so far)
* fixed native Windows implementation of RIFF::File::__GetFileSize() to
  work with younger versions than XP as well
* added Dev-C++ project files for the demo / example applications as well
* added instructions in README for how to compile libgig and its tools for
  Windows

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

  ViewVC Help
Powered by ViewVC