gigextract.cpp

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

Generated on Wed Dec 6 19:25:57 2006 for libgig by  doxygen 1.5.1