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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 902 - (hide 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 schoenebeck 2 /***************************************************************************
2     * *
3     * libgig - C++ cross-platform Gigasampler format file loader library *
4     * *
5 schoenebeck 518 * Copyright (C) 2003-2005 by Christian Schoenebeck *
6     * <cuse@users.sourceforge.net> *
7 schoenebeck 2 * *
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 schoenebeck 220 #include <string.h>
43 schoenebeck 518 #include <string>
44 schoenebeck 2 #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 schoenebeck 220
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 schoenebeck 608 // 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 schoenebeck 220 // 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 schoenebeck 2 #include "gig.h"
70    
71     using namespace std;
72    
73     typedef map<unsigned int, bool> OrderMap;
74     OrderMap* pOrderedSamples = NULL;
75    
76 schoenebeck 518 string Revision();
77     void PrintVersion();
78 schoenebeck 2 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 schoenebeck 220 string ToString(int i);
82    
83 schoenebeck 608 #if !HAVE_SNDFILE // use libaudiofile
84 schoenebeck 2 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 schoenebeck 220 #endif // !HAVE_SNDFILE
98 schoenebeck 2
99     int main(int argc, char *argv[]) {
100 schoenebeck 518 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 schoenebeck 2 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 schoenebeck 608 #if !HAVE_SNDFILE // use libaudiofile
171 schoenebeck 2 hAFlib = NULL;
172     openAFlib();
173 schoenebeck 220 #endif // !HAVE_SNDFILE
174 schoenebeck 2 uint8_t* pWave = NULL;
175 persson 902 int* pIntWave = NULL;
176 schoenebeck 2 long BufferSize = 0;
177     int samples = 0;
178 persson 902 gig::buffer_t decompressionBuffer;
179     decompressionBuffer.Size = 0;
180     unsigned long decompressionBufferSize = 0;
181 schoenebeck 2 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 persson 902 long neededsize = pSample->BitDepth == 24 ?
214     pSample->SamplesTotal * pSample->Channels * sizeof(int) :
215     pSample->SamplesTotal * pSample->FrameSize;
216 schoenebeck 2 if (BufferSize < neededsize) {
217     if (pWave) delete[] pWave;
218     pWave = new uint8_t[neededsize];
219     BufferSize = neededsize;
220     }
221 persson 902 pIntWave = (int*)pWave;
222 schoenebeck 2 # if HASHED_READS_TEST
223 persson 902 unsigned long readinthisrun = 0,
224 schoenebeck 2 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 persson 902 pSamplePiece += readinthisrun * pSample->FrameSize;
229 schoenebeck 2 } while (readinthisrun == samplepiecesize);
230    
231     # else // read in one piece
232     if (pSample->Compressed) {
233 persson 902 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 schoenebeck 2 pSample->Read(pWave, pSample->SamplesTotal);
241     }
242     # endif // HASHED_READS_TEST
243     #else // no disk streaming
244     if (pSample->Compressed) {
245 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;
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 schoenebeck 2 }
258     #endif // USE_DISK_STREAMING
259     if (pWave) {
260 persson 902
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 schoenebeck 2 int res = writeWav(filename.c_str(),
276 persson 902 pSample->BitDepth == 24 ? static_cast<void*>(pIntWave) : pWave,
277 schoenebeck 2 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 persson 902 gig::Sample::DestroyDecompressionBuffer(decompressionBuffer);
290     #if USE_DISK_STREAMING
291     if (pWave) delete[] pWave;
292     #else
293     if (pIntWave) delete[] pIntWave;
294     #endif
295 schoenebeck 608 #if !HAVE_SNDFILE // use libaudiofile
296 schoenebeck 2 closeAFlib();
297 schoenebeck 220 #endif // !HAVE_SNDFILE
298 schoenebeck 2 }
299    
300     int writeWav(const char* filename, void* samples, long samplecount, int channels, int bitdepth, long rate) {
301 schoenebeck 220 #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 persson 902 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 schoenebeck 220 cerr << sf_strerror(hfile) << endl << flush;
336     sf_close(hfile);
337     return -1;
338     }
339     sf_close(hfile);
340 schoenebeck 608 #else // use libaudiofile
341 schoenebeck 2 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 schoenebeck 220 #endif // HAVE_SNDFILE
353 schoenebeck 55
354 schoenebeck 220 return 0; // success
355 schoenebeck 2 }
356    
357 schoenebeck 608 #if !HAVE_SNDFILE // use libaudiofile
358 schoenebeck 2 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 schoenebeck 220 #endif // !HAVE_SNDFILE
380 schoenebeck 2
381 schoenebeck 518 string Revision() {
382 persson 902 string s = "$Revision: 1.8 $";
383 schoenebeck 518 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 schoenebeck 549 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 schoenebeck 608 #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 schoenebeck 549 #endif // HAVE_SNDFILE
400     cout << endl;
401 schoenebeck 518 }
402    
403 schoenebeck 2 void PrintUsage() {
404     cout << "gigextract - extracts samples from a Gigasampler file." << endl;
405     cout << endl;
406 schoenebeck 518 cout << "Usage: gigextract [-v] GIGFILE DESTDIR [SAMPLENR] [ [SAMPLENR] ...]" << endl;
407 schoenebeck 2 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 schoenebeck 518 cout << " -v Print version and exit." << endl;
417     cout << endl;
418 schoenebeck 2 }
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