Main Page | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members

gigextract.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                                                         *
00003  *   libgig - C++ cross-platform Gigasampler format file loader library    *
00004  *                                                                         *
00005  *   Copyright (C) 2003-2005 by Christian Schoenebeck                      *
00006  *                              <cuse@users.sourceforge.net>               *
00007  *                                                                         *
00008  *   This program is free software; you can redistribute it and/or modify  *
00009  *   it under the terms of the GNU General Public License as published by  *
00010  *   the Free Software Foundation; either version 2 of the License, or     *
00011  *   (at your option) any later version.                                   *
00012  *                                                                         *
00013  *   This program is distributed in the hope that it will be useful,       *
00014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00016  *   GNU General Public License for more details.                          *
00017  *                                                                         *
00018  *   You should have received a copy of the GNU General Public License     *
00019  *   along with this program; if not, write to the Free Software           *
00020  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
00021  *   MA  02111-1307  USA                                                   *
00022  ***************************************************************************/
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027 
00028 // just for testing the disk streaming capability of libgig;
00029 // you will also need to set this to 1 at the moment if you want to
00030 // extract compressed samples, as I haven't implemented the
00031 // decompression algorithm in gig::Sample::LoadSampleData() yet
00032 #define USE_DISK_STREAMING      1
00033 
00034 // only when USE_DISK_STREAMING is set to 1:
00035 // just for testing the disk stream capability; with this option set
00036 // gigextract will read the samples in smaller pieces, just to stress
00037 // gig::Sample::Read() method a bit
00038 #define HASHED_READS_TEST       1
00039 
00040 #include <iostream>
00041 #include <cstdlib>
00042 #include <string.h>
00043 #include <string>
00044 #include <stdlib.h>
00045 #include <sys/types.h>
00046 #include <sys/stat.h>
00047 #include <dirent.h>
00048 #include <errno.h>
00049 #include <dlfcn.h>
00050 
00051 // only libsndfile is available for Windows, so we use that for writing the sound files
00052 #ifdef WIN32
00053 # define HAVE_SNDFILE 1
00054 #endif // WIN32
00055 
00056 // abort compilation here if neither libsndfile nor libaudiofile are available
00057 #if !HAVE_SNDFILE && !HAVE_AUDIOFILE
00058 # error "Neither libsndfile nor libaudiofile seem to be available!"
00059 # error "(HAVE_SNDFILE and HAVE_AUDIOFILE are both false)"
00060 #endif
00061 
00062 // we prefer libsndfile before libaudiofile
00063 #if HAVE_SNDFILE
00064 # include <sndfile.h>
00065 #else
00066 # include <audiofile.h>
00067 #endif // HAVE_SNDFILE
00068 
00069 #include "gig.h"
00070 
00071 using namespace std;
00072 
00073 typedef map<unsigned int, bool> OrderMap;
00074 OrderMap* pOrderedSamples = NULL;
00075 
00076 string Revision();
00077 void PrintVersion();
00078 void PrintUsage();
00079 void ExtractSamples(gig::File* gig, char* destdir, OrderMap* ordered);
00080 int writeWav(const char* filename, void* samples, long samplecount, int channels, int bitdepth, long rate);
00081 string ToString(int i);
00082 
00083 #if !HAVE_SNDFILE // use libaudiofile
00084 void* hAFlib; // handle to libaudiofile
00085 void openAFlib(void);
00086 void closeAFlib(void);
00087 // pointers to libaudiofile functions
00088 AFfilesetup(*_afNewFileSetup)(void);
00089 void(*_afFreeFileSetup)(AFfilesetup);
00090 void(*_afInitChannels)(AFfilesetup,int,int);
00091 void(*_afInitSampleFormat)(AFfilesetup,int,int,int);
00092 void(*_afInitFileFormat)(AFfilesetup,int);
00093 void(*_afInitRate)(AFfilesetup,int,double);
00094 int(*_afWriteFrames)(AFfilehandle,int,const void*,int);
00095 AFfilehandle(*_afOpenFile)(const char*,const char*,AFfilesetup);
00096 int(*_afCloseFile)(AFfilehandle file);
00097 #endif // !HAVE_SNDFILE
00098 
00099 int main(int argc, char *argv[]) {
00100      if (argc >= 2) {
00101         if (argv[1][0] == '-') {
00102             switch (argv[1][1]) {
00103                 case 'v':
00104                     PrintVersion();
00105                     return EXIT_SUCCESS;
00106             }
00107         }
00108     }
00109     if (argc < 3) {
00110         PrintUsage();
00111         return EXIT_FAILURE;
00112     }
00113     if (argc > 3) { // extracting specific samples
00114         pOrderedSamples = new OrderMap;
00115         for (int i = 3; i < argc; i++) {
00116             unsigned int index = atoi(argv[i]);
00117             (*pOrderedSamples)[index] = true;
00118         }
00119     }
00120     FILE* hFile = fopen(argv[1], "r");
00121     if (!hFile) {
00122         cout << "Invalid input file argument!" << endl;
00123         return EXIT_FAILURE;
00124     }
00125     fclose(hFile);
00126     DIR* dir = opendir(argv[2]);
00127     if (!dir) {
00128         cout << "Unable to open DESTDIR: ";
00129         switch (errno) {
00130             case EACCES:  cout << "Permission denied." << endl;
00131                           break;
00132             case EMFILE:  cout << "Too many file descriptors in use by process." << endl;
00133                           break;
00134             case ENFILE:  cout << "Too many files are currently open in the system." << endl;
00135                           break;
00136             case ENOENT:  cout << "Directory does not exist, or name is an empty string." << endl;
00137                           break;
00138             case ENOMEM:  cout << "Insufficient memory to complete the operation." << endl;
00139                           break;
00140             case ENOTDIR: cout << "Is not a directory." << endl;
00141                           break;
00142             default:      cout << "Unknown error" << endl;
00143         }
00144         return EXIT_FAILURE;
00145     }
00146     if (dir) closedir(dir);
00147     try {
00148         RIFF::File* riff = new RIFF::File(argv[1]);
00149         gig::File*  gig  = new gig::File(riff);
00150         cout << "Extracting samples from \"" << argv[1] << "\" to directory \"" << argv[2] << "\"." << endl << flush;
00151         ExtractSamples(gig, argv[2], pOrderedSamples);
00152         cout << "Extraction finished." << endl << flush;
00153         delete gig;
00154         delete riff;
00155         if (pOrderedSamples) delete pOrderedSamples;
00156     }
00157     catch (RIFF::Exception e) {
00158         e.PrintMessage();
00159         return EXIT_FAILURE;
00160     }
00161     catch (...) {
00162         cout << "Unknown exception while trying to parse file." << endl;
00163         return EXIT_FAILURE;
00164     }
00165 
00166     return EXIT_SUCCESS;
00167 }
00168 
00169 void ExtractSamples(gig::File* gig, char* destdir, OrderMap* ordered) {
00170 #if !HAVE_SNDFILE // use libaudiofile
00171     hAFlib = NULL;
00172     openAFlib();
00173 #endif // !HAVE_SNDFILE
00174     uint8_t* pWave  = NULL;
00175     long BufferSize = 0;
00176     int samples     = 0;
00177     cout << "Seeking for available samples..." << flush;
00178     gig::Sample* pSample = gig->GetFirstSample();
00179     cout << "OK" << endl << flush;
00180     while (pSample) {
00181         samples++;
00182         if (ordered) {
00183             if ((*ordered)[samples] == false) {
00184                 pSample = gig->GetNextSample();
00185                 continue;
00186             }
00187         }
00188         string name = pSample->pInfo->Name;
00189         string filename = destdir;
00190         if (filename[filename.size() - 1] != '/') filename += "/";
00191         filename += ToString(samples);
00192         filename += "_";
00193         if (name == "") {
00194             name = "(NO NAME)";
00195             filename += "NONAME";
00196         }
00197         else {
00198             filename += name;
00199             name.insert(0, "\"");
00200             name += "\"";
00201         }
00202         filename += ".wav";
00203         if (pSample->Compressed) cout << "Decompressing ";
00204         else                     cout << "Extracting ";
00205         cout << "Sample " << samples << ") " << name << " (" << pSample->BitDepth <<"Bits, " << pSample->SamplesPerSecond << "Hz, " << pSample->Channels << " Channels, " << pSample->SamplesTotal << " Samples)..." << flush;
00206 
00207 
00208 #if USE_DISK_STREAMING
00209         long neededsize = (pSample->Compressed) ? 10485760 /* 10 MB buffer */
00210                                                 : pSample->SamplesTotal * pSample->FrameSize;
00211         if (BufferSize < neededsize) {
00212             if (pWave) delete[] pWave;
00213             pWave = new uint8_t[neededsize];
00214             BufferSize = neededsize;
00215         }
00216 #  if HASHED_READS_TEST
00217         unsigned long readsamples     = 0,
00218                       readinthisrun   = 0,
00219                       samplepiecesize = 2000;
00220         uint8_t* pSamplePiece = pWave;
00221         do { // we read the sample in small pieces and increment the size with each run just to test streaming capability
00222             readinthisrun = pSample->Read(pSamplePiece, ++samplepiecesize);
00223             // 24 bit is truncated to 16 by Sample::Read at the moment
00224             pSamplePiece += readinthisrun * (2 * pSample->Channels); // readinthisrun * pSample->FrameSize;
00225             readsamples  += readinthisrun;
00226         } while (readinthisrun == samplepiecesize);
00227 
00228         if (pSample->Compressed) { // hack
00229             pSample->SamplesTotal = readsamples;
00230             pSample->BitDepth = 16;
00231         }
00232 #  else // read in one piece
00233         if (pSample->Compressed) {
00234             pSample->SamplesTotal = pSample->Read(pWave, 10485760 >> 2); // assumes 16 bit stereo
00235         }
00236         else {
00237             pSample->Read(pWave, pSample->SamplesTotal);
00238         }
00239 #  endif // HASHED_READS_TEST
00240 #else // no disk streaming
00241         if (pSample->Compressed) {
00242             cout << "Sorry, sample is compressed and Sample::LoadSampleData() has no decompression routine yet! - Solution: set USE_DISK_STREAMING in gigextract.cpp (line 29) to 1 and recompile!" << endl;
00243         }
00244         else pWave = (uint8_t*) pSample->LoadSampleData(); // load wave into RAM
00245 #endif // USE_DISK_STREAMING
00246         if (pWave) {
00247             int res = writeWav(filename.c_str(),
00248                                pWave,
00249                                pSample->SamplesTotal,
00250                                pSample->Channels,
00251                                pSample->BitDepth,
00252                                pSample->SamplesPerSecond);
00253             if (res < 0) cout << "Couldn't write sample data." << endl;
00254             else cout << "ok" << endl;
00255             pSample->ReleaseSampleData(); // free wave from RAM
00256         }
00257         else cout << "Failed to load sample data." << endl;
00258 
00259         pSample = gig->GetNextSample();
00260     }
00261     if (pWave) delete[] (uint8_t*) pWave;
00262 #if !HAVE_SNDFILE // use libaudiofile
00263     closeAFlib();
00264 #endif // !HAVE_SNDFILE
00265 }
00266 
00267 int writeWav(const char* filename, void* samples, long samplecount, int channels, int bitdepth, long rate) {
00268 #if HAVE_SNDFILE
00269     SNDFILE* hfile;
00270     SF_INFO  sfinfo;
00271     int format = SF_FORMAT_WAV;
00272     switch (bitdepth) {
00273         case 8:
00274             format |= SF_FORMAT_PCM_S8;
00275             cout << "8 bit" << endl << flush;
00276             break;
00277         case 16:
00278             format |= SF_FORMAT_PCM_16;
00279             cout << "16 bit" << endl << flush;
00280             break;
00281         case 24:
00282             format |= SF_FORMAT_PCM_24;
00283             cout << "24 bit" << endl << flush;
00284             break;
00285         case 32:
00286             format |= SF_FORMAT_PCM_32;
00287             cout << "32 bit" << endl << flush;
00288             break;
00289         default:
00290             cerr << "Error: Bithdepth " << ToString(bitdepth) << " not supported by libsndfile, ignoring sample!\n" << flush;
00291             return -1;
00292     }
00293     memset(&sfinfo, 0, sizeof (sfinfo));
00294     sfinfo.samplerate = rate;
00295     sfinfo.frames     = samplecount;
00296     sfinfo.channels   = channels;
00297     sfinfo.format     = format;
00298     if (!(hfile = sf_open(filename, SFM_WRITE, &sfinfo))) {
00299         cerr << "Error: Unable to open output file \'" << filename << "\'.\n" << flush;
00300         return -1;
00301     }
00302     if (sf_write_short(hfile, (short*)samples, channels * samplecount) != channels * samplecount) {
00303         cerr << sf_strerror(hfile) << endl << flush;
00304         sf_close(hfile);
00305         return -1;
00306     }
00307     sf_close(hfile);
00308 #else // use libaudiofile
00309     AFfilesetup setup = _afNewFileSetup();
00310     _afInitFileFormat(setup, AF_FILE_WAVE);
00311     _afInitChannels(setup, AF_DEFAULT_TRACK, channels);
00312     _afInitSampleFormat(setup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, bitdepth);
00313     _afInitRate(setup, AF_DEFAULT_TRACK, rate);
00314     if (setup == AF_NULL_FILESETUP) return -1;
00315     AFfilehandle hFile = _afOpenFile(filename, "w", setup);
00316     if (hFile == AF_NULL_FILEHANDLE) return -1;
00317     if (_afWriteFrames(hFile, AF_DEFAULT_TRACK, samples, samplecount) < 0) return -1;
00318     _afCloseFile(hFile);
00319     _afFreeFileSetup(setup);
00320 #endif // HAVE_SNDFILE
00321 
00322     return 0; // success
00323 }
00324 
00325 #if !HAVE_SNDFILE // use libaudiofile
00326 void openAFlib() {
00327     hAFlib = dlopen("libaudiofile.so", RTLD_NOW);
00328     if (!hAFlib) {
00329         cout << "Unable to load library libaudiofile.so: " << dlerror() << endl;
00330         return;
00331     }
00332     _afNewFileSetup     = (AFfilesetup(*)(void)) dlsym(hAFlib, "afNewFileSetup");
00333     _afFreeFileSetup    = (void(*)(AFfilesetup)) dlsym(hAFlib, "afFreeFileSetup");
00334     _afInitChannels     = (void(*)(AFfilesetup,int,int)) dlsym(hAFlib, "afInitChannels");
00335     _afInitSampleFormat = (void(*)(AFfilesetup,int,int,int)) dlsym(hAFlib, "afInitSampleFormat");
00336     _afInitFileFormat   = (void(*)(AFfilesetup,int)) dlsym(hAFlib, "afInitFileFormat");
00337     _afInitRate         = (void(*)(AFfilesetup,int,double)) dlsym(hAFlib, "afInitRate");
00338     _afWriteFrames      = (int(*)(AFfilehandle,int,const void*,int)) dlsym(hAFlib, "afWriteFrames");
00339     _afOpenFile         = (AFfilehandle(*)(const char*,const char*,AFfilesetup)) dlsym(hAFlib, "afOpenFile");
00340     _afCloseFile        = (int(*)(AFfilehandle file)) dlsym(hAFlib, "afCloseFile");
00341     if (dlerror()) cout << "Failed to load function from libaudiofile.so: " << dlerror() << endl;
00342 }
00343 
00344 void closeAFlib() {
00345     if (hAFlib) dlclose(hAFlib);
00346 }
00347 #endif // !HAVE_SNDFILE
00348 
00349 string Revision() {
00350     string s = "$Revision: 1.7 $";
00351     return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
00352 }
00353 
00354 void PrintVersion() {
00355     cout << "gigextract revision " << Revision() << endl;
00356     cout << "using " << gig::libraryName() << " " << gig::libraryVersion();
00357     #if HAVE_SNDFILE
00358     char versionBuffer[128];
00359     sf_command(NULL, SFC_GET_LIB_VERSION, versionBuffer, 128);
00360     cout << ", " << versionBuffer;
00361     #else // use libaudiofile
00362     cout << "\nbuilt against libaudiofile "
00363          << LIBAUDIOFILE_MAJOR_VERSION << "." << LIBAUDIOFILE_MINOR_VERSION;
00364     # ifdef LIBAUDIOFILE_MICRO_VERSION
00365     cout << "." << LIBAUDIOFILE_MICRO_VERSION;
00366     # endif // LIBAUDIOFILE_MICRO_VERSION
00367     #endif // HAVE_SNDFILE
00368     cout << endl;
00369 }
00370 
00371 void PrintUsage() {
00372     cout << "gigextract - extracts samples from a Gigasampler file." << endl;
00373     cout << endl;
00374     cout << "Usage: gigextract [-v] GIGFILE DESTDIR [SAMPLENR] [ [SAMPLENR] ...]" << endl;
00375     cout << endl;
00376     cout << "   GIGFILE  Input Gigasampler (.gig) file." << endl;
00377     cout << endl;
00378     cout << "   DESTDIR  Destination directory where all .wav files will be written to." << endl;
00379     cout << endl;
00380     cout << "   SAMPLENR Index (/indices) of Sample(s) which should be extracted." << endl;
00381     cout << "            If no sample indices are given, all samples will be extracted" << endl;
00382     cout << "            (use gigdump to look for available samples)." << endl;
00383     cout << endl;
00384     cout << "   -v       Print version and exit." << endl;
00385     cout << endl;
00386 }
00387 
00388 string ToString(int i) {
00389     static char strbuf[1024];
00390     sprintf(strbuf,"%d",i);
00391     string s = strbuf;
00392     return s;
00393 }

Generated on Fri Jul 29 22:09:29 2005 for libgig by doxygen 1.3.4