Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members

DarkIce.cpp

Go to the documentation of this file.
00001 /*------------------------------------------------------------------------------
00002 
00003    Copyright (c) 2000 Tyrell Corporation. All rights reserved.
00004 
00005    Tyrell DarkIce
00006 
00007    File     : DarkIce.cpp
00008    Version  : $Revision: 1.49 $
00009    Author   : $Author: darkeye $
00010    Location : $Source: /cvsroot/darkice/darkice/src/DarkIce.cpp,v $
00011    
00012 
00013    Copyright notice:
00014 
00015     This program is free software; you can redistribute it and/or
00016     modify it under the terms of the GNU General Public License  
00017     as published by the Free Software Foundation; either version 2
00018     of the License, or (at your option) any later version.
00019    
00020     This program is distributed in the hope that it will be useful,
00021     but WITHOUT ANY WARRANTY; without even the implied warranty of 
00022     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
00023     GNU General Public License for more details.
00024    
00025     You should have received a copy of the GNU General Public License
00026     along with this program; if not, write to the Free Software
00027     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00028 
00029 ------------------------------------------------------------------------------*/
00030 
00031 /* ============================================================ include files */
00032 
00033 #ifdef HAVE_CONFIG_H
00034 #include "config.h"
00035 #endif
00036 
00037 #ifdef HAVE_STDLIB_H
00038 #include <stdlib.h>
00039 #else
00040 #error need stdlib.h
00041 #endif
00042 
00043 #ifdef HAVE_UNISTD_H
00044 #include <unistd.h>
00045 #else
00046 #error need unistd.h
00047 #endif
00048 
00049 #ifdef HAVE_SYS_TYPES_H
00050 #include <sys/types.h>
00051 #else
00052 #error need sys/types.h
00053 #endif
00054 
00055 #ifdef HAVE_SYS_WAIT_H
00056 #include <sys/wait.h>
00057 #else
00058 #error need sys/wait.h
00059 #endif
00060 
00061 #ifdef HAVE_ERRNO_H
00062 #include <errno.h>
00063 #else
00064 #error need errno.h
00065 #endif
00066 
00067 #ifdef HAVE_SCHED_H
00068 #include <sched.h>
00069 #else
00070 #error need sched.h
00071 #endif
00072 
00073 
00074 
00075 #include "Util.h"
00076 #include "IceCast.h"
00077 #include "IceCast2.h"
00078 #include "ShoutCast.h"
00079 #include "FileCast.h"
00080 #include "MultiThreadedConnector.h"
00081 #include "DarkIce.h"
00082 
00083 #ifdef HAVE_LAME_LIB
00084 #include "LameLibEncoder.h"
00085 #endif
00086 
00087 #ifdef HAVE_TWOLAME_LIB
00088 #include "TwoLameLibEncoder.h"
00089 #endif
00090 
00091 #ifdef HAVE_VORBIS_LIB
00092 #include "VorbisLibEncoder.h"
00093 #endif
00094 
00095 #ifdef HAVE_FAAC_LIB
00096 #include "FaacEncoder.h"
00097 #endif
00098 
00099 
00100 /* ===================================================  local data structures */
00101 
00102 
00103 /* ================================================  local constants & macros */
00104 
00105 /*------------------------------------------------------------------------------
00106  *  File identity
00107  *----------------------------------------------------------------------------*/
00108 static const char fileid[] = "$Id: DarkIce.cpp,v 1.49 2006/03/23 09:34:57 darkeye Exp $";
00109 
00110 
00111 /*------------------------------------------------------------------------------
00112  *  Make sure wait-related stuff is what we expect
00113  *----------------------------------------------------------------------------*/
00114 #ifndef WEXITSTATUS
00115 # define WEXITSTATUS(stat_val)      ((unsigned)(stat_val) >> 8)
00116 #endif
00117 #ifndef WIFEXITED
00118 # define WIFEXITED(stat_val)        (((stat_val) & 255) == 0)
00119 #endif
00120 
00121 
00122 
00123 /* ===============================================  local function prototypes */
00124 
00125 
00126 /* =============================================================  module code */
00127 
00128 /*------------------------------------------------------------------------------
00129  *  Initialize the object
00130  *----------------------------------------------------------------------------*/
00131 void
00132 DarkIce :: init ( const Config      & config )              throw ( Exception )
00133 {
00134     unsigned int             bufferSecs;
00135     const ConfigSection    * cs;
00136     const char             * str;
00137     unsigned int             sampleRate;
00138     unsigned int             bitsPerSample;
00139     unsigned int             channel;
00140     bool                     reconnect;
00141     const char             * device;
00142 
00143     // the [general] section
00144     if ( !(cs = config.get( "general")) ) {
00145         throw Exception( __FILE__, __LINE__, "no section [general] in config");
00146     }
00147     str = cs->getForSure( "duration", " missing in section [general]");
00148     duration = Util::strToL( str);
00149     str = cs->getForSure( "bufferSecs", " missing in section [general]");
00150     bufferSecs = Util::strToL( str);
00151     if (bufferSecs == 0) {
00152         throw Exception(__FILE__, __LINE__,
00153                         "setting bufferSecs to 0 not supported");
00154     }
00155     str           = cs->get( "reconnect");
00156     reconnect     = str ? (Util::strEq( str, "yes") ? true : false) : true;
00157 
00158     // real-time scheduling is enabled by default
00159     str = cs->get( "realtime" );
00160     enableRealTime = str ? (Util::strEq( str, "yes") ? true : false) : true;
00161 
00162 
00163     // the [input] section
00164     if ( !(cs = config.get( "input")) ) {
00165         throw Exception( __FILE__, __LINE__, "no section [input] in config");
00166     }
00167     
00168     str        = cs->getForSure( "sampleRate", " missing in section [input]");
00169     sampleRate = Util::strToL( str);
00170     str       = cs->getForSure( "bitsPerSample", " missing in section [input]");
00171     bitsPerSample = Util::strToL( str);
00172     str           = cs->getForSure( "channel", " missing in section [input]");
00173     channel       = Util::strToL( str);
00174     device        = cs->getForSure( "device", " missing in section [input]");
00175 
00176     dsp             = AudioSource::createDspSource( device,
00177                                                     sampleRate,
00178                                                     bitsPerSample,
00179                                                     channel );
00180     encConnector    = new MultiThreadedConnector( dsp.get(), reconnect );
00181 
00182     noAudioOuts = 0;
00183     configIceCast( config, bufferSecs);
00184     configIceCast2( config, bufferSecs);
00185     configShoutCast( config, bufferSecs);
00186     configFileCast( config);
00187 }
00188 
00189 
00190 /*------------------------------------------------------------------------------
00191  *  Look for the IceCast stream outputs in the config file
00192  *----------------------------------------------------------------------------*/
00193 void
00194 DarkIce :: configIceCast (  const Config      & config,
00195                             unsigned int        bufferSecs  )
00196                                                         throw ( Exception )
00197 {
00198     // look for IceCast encoder output streams,
00199     // sections [icecast-0], [icecast-1], ...
00200     char            stream[]        = "icecast- ";
00201     size_t          streamLen       = Util::strLen( stream);
00202     unsigned int    u;
00203 
00204     for ( u = noAudioOuts; u < maxOutput; ++u ) {
00205         const ConfigSection    * cs;
00206 
00207         // ugly hack to change the section name to "stream0", "stream1", etc.
00208         stream[streamLen-1] = '0' + (u - noAudioOuts);
00209 
00210         if ( !(cs = config.get( stream)) ) {
00211             break;
00212         }
00213 
00214 #if !defined HAVE_LAME_LIB && !defined HAVE_TWOLAME_LIB
00215         throw Exception( __FILE__, __LINE__,
00216                          "DarkIce not compiled with lame or twolame support, "
00217                          "thus can't connect to IceCast 1.x, stream: ",
00218                          stream);
00219 #else
00220 
00221         const char                * str;
00222 
00223         unsigned int                sampleRate      = 0;
00224         unsigned int                channel         = 0;
00225         AudioEncoder::BitrateMode   bitrateMode;
00226         unsigned int                bitrate         = 0;
00227         double                      quality         = 0.0;
00228         const char                * server          = 0;
00229         unsigned int                port            = 0;
00230         const char                * password        = 0;
00231         const char                * mountPoint      = 0;
00232         const char                * remoteDumpFile  = 0;
00233         const char                * name            = 0;
00234         const char                * description     = 0;
00235         const char                * url             = 0;
00236         const char                * genre           = 0;
00237         bool                        isPublic        = false;
00238         int                         lowpass         = 0;
00239         int                         highpass        = 0;
00240         const char                * localDumpName   = 0;
00241         FileSink                  * localDumpFile   = 0;
00242         bool                        fileAddDate     = false;
00243 
00244         str         = cs->get( "sampleRate");
00245         sampleRate  = str ? Util::strToL( str) : dsp->getSampleRate();
00246         str         = cs->get( "channel");
00247         channel     = str ? Util::strToL( str) : dsp->getChannel();
00248 
00249         str         = cs->get( "bitrate");
00250         bitrate     = str ? Util::strToL( str) : 0;
00251         str         = cs->get( "quality");
00252         quality     = str ? Util::strToD( str) : 0.0;
00253         
00254         str         = cs->getForSure( "bitrateMode",
00255                                       " not specified in section ",
00256                                       stream);
00257         if ( Util::strEq( str, "cbr") ) {
00258             bitrateMode = AudioEncoder::cbr;
00259             
00260             if ( bitrate == 0 ) {
00261                 throw Exception( __FILE__, __LINE__,
00262                                  "bitrate not specified for CBR encoding");
00263             }
00264             if ( cs->get( "quality" ) == 0 ) {
00265                 throw Exception( __FILE__, __LINE__,
00266                                  "quality not specified for CBR encoding");
00267             }
00268         } else if ( Util::strEq( str, "abr") ) {
00269             bitrateMode = AudioEncoder::abr;
00270 
00271             if ( bitrate == 0 ) {
00272                 throw Exception( __FILE__, __LINE__,
00273                                  "bitrate not specified for ABR encoding");
00274             }
00275         } else if ( Util::strEq( str, "vbr") ) {
00276             bitrateMode = AudioEncoder::vbr;
00277 
00278             if ( cs->get( "quality" ) == 0 ) {
00279                 throw Exception( __FILE__, __LINE__,
00280                                  "quality not specified for VBR encoding");
00281             }
00282         } else {
00283             throw Exception( __FILE__, __LINE__,
00284                              "invalid bitrate mode: ", str);
00285         }
00286         
00287         
00288 
00289         server      = cs->getForSure( "server", " missing in section ", stream);
00290         str         = cs->getForSure( "port", " missing in section ", stream);
00291         port        = Util::strToL( str);
00292         password    = cs->getForSure("password"," missing in section ",stream);
00293         mountPoint  = cs->getForSure( "mountPoint",
00294                                       " missing in section ",
00295                                       stream);
00296         remoteDumpFile = cs->get( "remoteDumpFile");
00297         name        = cs->get( "name");
00298         description = cs->get("description");
00299         url         = cs->get( "url");
00300         genre       = cs->get( "genre");
00301         str         = cs->get( "public");
00302         isPublic    = str ? (Util::strEq( str, "yes") ? true : false) : false;
00303         str         = cs->get( "lowpass");
00304         lowpass     = str ? Util::strToL( str) : 0;
00305         str         = cs->get( "highpass");
00306         highpass    = str ? Util::strToL( str) : 0;
00307         str         = cs->get("fileAddDate");
00308         fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false;
00309 
00310         localDumpName = cs->get( "localDumpFile");
00311 
00312         // go on and create the things
00313 
00314         // check for and create the local dump file if needed
00315         if ( localDumpName != 0 ) {
00316             if ( fileAddDate ) {
00317                 localDumpName = Util::fileAddDate(localDumpName);
00318             }
00319 
00320             localDumpFile = new FileSink( localDumpName);
00321             if ( !localDumpFile->exists() ) {
00322                 if ( !localDumpFile->create() ) {
00323                     reportEvent( 1, "can't create local dump file",
00324                                     localDumpName);
00325                     localDumpFile = 0;
00326                 }
00327             }
00328             if ( fileAddDate ) {
00329                 delete[] localDumpFile;
00330             }
00331         }
00332         // streaming related stuff
00333         audioOuts[u].socket = new TcpSocket( server, port);
00334         audioOuts[u].server = new IceCast( audioOuts[u].socket.get(),
00335                                            password,
00336                                            mountPoint,
00337                                            bitrate,
00338                                            name,
00339                                            description,
00340                                            url,
00341                                            genre,
00342                                            isPublic,
00343                                            remoteDumpFile,
00344                                            localDumpFile,
00345                                            bufferSecs );
00346 
00347         str = cs->getForSure( "format", " missing in section ", stream);
00348 
00349         if (!Util::strEq(str, "mp3") && !Util::strEq(str, "mp2")) {
00350             throw Exception( __FILE__, __LINE__,
00351                              "unsupported stream format: ", str);
00352 
00353         }
00354 
00355 #ifdef HAVE_LAME_LIB
00356         if ( Util::strEq( str, "mp3") ) {
00357             audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(),
00358                                                        dsp.get(),
00359                                                        bitrateMode,
00360                                                        bitrate,
00361                                                        quality,
00362                                                        sampleRate,
00363                                                        channel,
00364                                                        lowpass,
00365                                                        highpass );
00366         }
00367 #endif
00368 #ifdef HAVE_TWOLAME_LIB
00369         if ( Util::strEq( str, "mp2") ) {
00370             audioOuts[u].encoder = new TwoLameLibEncoder(
00371                                                       audioOuts[u].server.get(),
00372                                                       dsp.get(),
00373                                                       bitrateMode,
00374                                                       bitrate,
00375                                                       sampleRate,
00376                                                       channel );
00377         }
00378 #endif
00379 
00380         encConnector->attach( audioOuts[u].encoder.get());
00381 #endif // HAVE_LAME_LIB || HAVE_TWOLAME_LIB
00382     }
00383 
00384     noAudioOuts += u;
00385 }
00386 
00387 
00388 /*------------------------------------------------------------------------------
00389  *  Look for the IceCast2 stream outputs in the config file
00390  *----------------------------------------------------------------------------*/
00391 void
00392 DarkIce :: configIceCast2 (  const Config      & config,
00393                              unsigned int        bufferSecs  )
00394                                                         throw ( Exception )
00395 {
00396     // look for IceCast2 encoder output streams,
00397     // sections [icecast2-0], [icecast2-1], ...
00398     char            stream[]        = "icecast2- ";
00399     size_t          streamLen       = Util::strLen( stream);
00400     unsigned int    u;
00401 
00402     for ( u = noAudioOuts; u < maxOutput; ++u ) {
00403         const ConfigSection    * cs;
00404 
00405         // ugly hack to change the section name to "stream0", "stream1", etc.
00406         stream[streamLen-1] = '0' + (u - noAudioOuts);
00407 
00408         if ( !(cs = config.get( stream)) ) {
00409             break;
00410         }
00411 
00412         const char                * str;
00413 
00414         IceCast2::StreamFormat      format;
00415         unsigned int                sampleRate      = 0;
00416         unsigned int                channel         = 0;
00417         AudioEncoder::BitrateMode   bitrateMode;
00418         unsigned int                bitrate         = 0;
00419         unsigned int                maxBitrate      = 0;
00420         double                      quality         = 0.0;
00421         const char                * server          = 0;
00422         unsigned int                port            = 0;
00423         const char                * password        = 0;
00424         const char                * mountPoint      = 0;
00425         const char                * name            = 0;
00426         const char                * description     = 0;
00427         const char                * url             = 0;
00428         const char                * genre           = 0;
00429         bool                        isPublic        = false;
00430         int                         lowpass         = 0;
00431         int                         highpass        = 0;
00432         const char                * localDumpName   = 0;
00433         FileSink                  * localDumpFile   = 0;
00434         bool                        fileAddDate     = false;
00435 
00436         str         = cs->getForSure( "format", " missing in section ", stream);
00437         if ( Util::strEq( str, "vorbis") ) {
00438             format = IceCast2::oggVorbis;
00439         } else if ( Util::strEq( str, "mp3") ) {
00440             format = IceCast2::mp3;
00441         } else if ( Util::strEq( str, "mp2") ) {
00442             format = IceCast2::mp2;
00443         } else if ( Util::strEq( str, "aac") ) {
00444             format = IceCast2::aac;
00445         } else {
00446             throw Exception( __FILE__, __LINE__,
00447                              "unsupported stream format: ", str);
00448         }
00449                 
00450         str         = cs->get( "sampleRate");
00451         sampleRate  = str ? Util::strToL( str) : dsp->getSampleRate();
00452         str         = cs->get( "channel");
00453         channel     = str ? Util::strToL( str) : dsp->getChannel();
00454 
00455         // determine fixed bitrate or variable bitrate quality
00456         str         = cs->get( "bitrate");
00457         bitrate     = str ? Util::strToL( str) : 0;
00458         str         = cs->get( "maxBitrate");
00459         maxBitrate  = str ? Util::strToL( str) : 0;
00460         str         = cs->get( "quality");
00461         quality     = str ? Util::strToD( str) : 0.0;
00462         
00463         str         = cs->getForSure( "bitrateMode",
00464                                       " not specified in section ",
00465                                       stream);
00466         if ( Util::strEq( str, "cbr") ) {
00467             bitrateMode = AudioEncoder::cbr;
00468             
00469             if ( bitrate == 0 ) {
00470                 throw Exception( __FILE__, __LINE__,
00471                                  "bitrate not specified for CBR encoding");
00472             }
00473         } else if ( Util::strEq( str, "abr") ) {
00474             bitrateMode = AudioEncoder::abr;
00475 
00476             if ( bitrate == 0 ) {
00477                 throw Exception( __FILE__, __LINE__,
00478                                  "bitrate not specified for ABR encoding");
00479             }
00480         } else if ( Util::strEq( str, "vbr") ) {
00481             bitrateMode = AudioEncoder::vbr;
00482 
00483             if ( cs->get( "quality" ) == 0 ) {
00484                 throw Exception( __FILE__, __LINE__,
00485                                  "quality not specified for VBR encoding");
00486             }
00487         } else {
00488             throw Exception( __FILE__, __LINE__,
00489                              "invalid bitrate mode: ", str);
00490         }
00491 
00492         server      = cs->getForSure( "server", " missing in section ", stream);
00493         str         = cs->getForSure( "port", " missing in section ", stream);
00494         port        = Util::strToL( str);
00495         password    = cs->getForSure("password"," missing in section ",stream);
00496         mountPoint  = cs->getForSure( "mountPoint",
00497                                       " missing in section ",
00498                                       stream);
00499         name        = cs->get( "name");
00500         description = cs->get( "description");
00501         url         = cs->get( "url");
00502         genre       = cs->get( "genre");
00503         str         = cs->get( "public");
00504         isPublic    = str ? (Util::strEq( str, "yes") ? true : false) : false;
00505         str         = cs->get( "lowpass");
00506         lowpass     = str ? Util::strToL( str) : 0;
00507         str         = cs->get( "highpass");
00508         highpass    = str ? Util::strToL( str) : 0;
00509         str         = cs->get( "fileAddDate");
00510         fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false;
00511         
00512         localDumpName = cs->get( "localDumpFile");
00513 
00514         // go on and create the things
00515 
00516         // check for and create the local dump file if needed
00517         if ( localDumpName != 0 ) {
00518             if ( fileAddDate ) {
00519                 localDumpName = Util::fileAddDate(localDumpName);
00520             }
00521 
00522             localDumpFile = new FileSink( localDumpName);
00523             if ( !localDumpFile->exists() ) {
00524                 if ( !localDumpFile->create() ) {
00525                     reportEvent( 1, "can't create local dump file",
00526                                     localDumpName);
00527                     localDumpFile = 0;
00528                 }
00529             }
00530             if ( fileAddDate ) {
00531                 delete[] localDumpName;
00532             }
00533         }
00534 
00535         // streaming related stuff
00536         audioOuts[u].socket = new TcpSocket( server, port);
00537         audioOuts[u].server = new IceCast2( audioOuts[u].socket.get(),
00538                                             password,
00539                                             mountPoint,
00540                                             format,
00541                                             bitrate,
00542                                             name,
00543                                             description,
00544                                             url,
00545                                             genre,
00546                                             isPublic,
00547                                             localDumpFile,
00548                                             bufferSecs );
00549 
00550         switch ( format ) {
00551             case IceCast2::mp3:
00552 #ifndef HAVE_LAME_LIB
00553                 throw Exception( __FILE__, __LINE__,
00554                                  "DarkIce not compiled with lame support, "
00555                                  "thus can't create mp3 stream: ",
00556                                  stream);
00557 #else
00558                 audioOuts[u].encoder = new LameLibEncoder(
00559                                                     audioOuts[u].server.get(),
00560                                                     dsp.get(),
00561                                                     bitrateMode,
00562                                                     bitrate,
00563                                                     quality,
00564                                                     sampleRate,
00565                                                     channel,
00566                                                     lowpass,
00567                                                     highpass );
00568 #endif // HAVE_LAME_LIB
00569                 break;
00570 
00571 
00572             case IceCast2::oggVorbis:
00573 #ifndef HAVE_VORBIS_LIB
00574                 throw Exception( __FILE__, __LINE__,
00575                                 "DarkIce not compiled with Ogg Vorbis support, "
00576                                 "thus can't Ogg Vorbis stream: ",
00577                                 stream);
00578 #else
00579                 audioOuts[u].encoder = new VorbisLibEncoder(
00580                                                 audioOuts[u].server.get(),
00581                                                 dsp.get(),
00582                                                 bitrateMode,
00583                                                 bitrate,
00584                                                 quality,
00585                                                 sampleRate,
00586                                                 dsp->getChannel(),
00587                                                 maxBitrate);
00588 #endif // HAVE_VORBIS_LIB
00589                 break;
00590 
00591             case IceCast2::mp2:
00592 #ifndef HAVE_TWOLAME_LIB
00593                 throw Exception( __FILE__, __LINE__,
00594                                  "DarkIce not compiled with TwoLame support, "
00595                                  "thus can't create mp2 stream: ",
00596                                  stream);
00597 #else
00598                 audioOuts[u].encoder = new TwoLameLibEncoder(
00599                                                     audioOuts[u].server.get(),
00600                                                     dsp.get(),
00601                                                     bitrateMode,
00602                                                     bitrate,
00603                                                     sampleRate,
00604                                                     channel );
00605 #endif // HAVE_TWOLAME_LIB
00606                 break;
00607 
00608 
00609             case IceCast2::aac:
00610 #ifndef HAVE_FAAC_LIB
00611                 throw Exception( __FILE__, __LINE__,
00612                                 "DarkIce not compiled with AAC support, "
00613                                 "thus can't aac stream: ",
00614                                 stream);
00615 #else
00616                 audioOuts[u].encoder = new FaacEncoder(
00617                                                 audioOuts[u].server.get(),
00618                                                 dsp.get(),
00619                                                 bitrateMode,
00620                                                 bitrate,
00621                                                 quality,
00622                                                 sampleRate,
00623                                                 dsp->getChannel());
00624 #endif // HAVE_FAAC_LIB
00625                 break;
00626 
00627             default:
00628                 throw Exception( __FILE__, __LINE__,
00629                                 "Illegal stream format: ", format);
00630         }
00631 
00632         encConnector->attach( audioOuts[u].encoder.get());
00633     }
00634 
00635     noAudioOuts += u;
00636 }
00637 
00638 
00639 /*------------------------------------------------------------------------------
00640  *  Look for the ShoutCast stream outputs in the config file
00641  *----------------------------------------------------------------------------*/
00642 void
00643 DarkIce :: configShoutCast (    const Config      & config,
00644                                 unsigned int        bufferSecs  )
00645                                                         throw ( Exception )
00646 {
00647     // look for Shoutcast encoder output streams,
00648     // sections [shoutcast-0], [shoutcast-1], ...
00649     char            stream[]        = "shoutcast- ";
00650     size_t          streamLen       = Util::strLen( stream);
00651     unsigned int    u;
00652 
00653     for ( u = noAudioOuts; u < maxOutput; ++u ) {
00654         const ConfigSection    * cs;
00655 
00656         // ugly hack to change the section name to "stream0", "stream1", etc.
00657         stream[streamLen-1] = '0' + (u - noAudioOuts);
00658 
00659         if ( !(cs = config.get( stream)) ) {
00660             break;
00661         }
00662 
00663 #ifndef HAVE_LAME_LIB
00664         throw Exception( __FILE__, __LINE__,
00665                          "DarkIce not compiled with lame support, "
00666                          "thus can't connect to ShoutCast, stream: ",
00667                          stream);
00668 #else
00669 
00670         const char                * str;
00671 
00672         unsigned int                sampleRate      = 0;
00673         unsigned int                channel         = 0;
00674         AudioEncoder::BitrateMode   bitrateMode;
00675         unsigned int                bitrate         = 0;
00676         double                      quality         = 0.0;
00677         const char                * server          = 0;
00678         unsigned int                port            = 0;
00679         const char                * password        = 0;
00680         const char                * name            = 0;
00681         const char                * url             = 0;
00682         const char                * genre           = 0;
00683         bool                        isPublic        = false;
00684         int                         lowpass         = 0;
00685         int                         highpass        = 0;
00686         const char                * irc             = 0;
00687         const char                * aim             = 0;
00688         const char                * icq             = 0;
00689         const char                * localDumpName   = 0;
00690         FileSink                  * localDumpFile   = 0;
00691         bool                        fileAddDate     = false;
00692 
00693         str         = cs->get( "sampleRate");
00694         sampleRate  = str ? Util::strToL( str) : dsp->getSampleRate();
00695         str         = cs->get( "channel");
00696         channel     = str ? Util::strToL( str) : dsp->getChannel();
00697 
00698         str         = cs->get( "bitrate");
00699         bitrate     = str ? Util::strToL( str) : 0;
00700         str         = cs->get( "quality");
00701         quality     = str ? Util::strToD( str) : 0.0;
00702         
00703         str         = cs->getForSure( "bitrateMode",
00704                                       " not specified in section ",
00705                                       stream);
00706         if ( Util::strEq( str, "cbr") ) {
00707             bitrateMode = AudioEncoder::cbr;
00708             
00709             if ( bitrate == 0 ) {
00710                 throw Exception( __FILE__, __LINE__,
00711                                  "bitrate not specified for CBR encoding");
00712             }
00713             if ( cs->get( "quality" ) == 0 ) {
00714                 throw Exception( __FILE__, __LINE__,
00715                                  "quality not specified for CBR encoding");
00716             }
00717         } else if ( Util::strEq( str, "abr") ) {
00718             bitrateMode = AudioEncoder::abr;
00719 
00720             if ( bitrate == 0 ) {
00721                 throw Exception( __FILE__, __LINE__,
00722                                  "bitrate not specified for ABR encoding");
00723             }
00724         } else if ( Util::strEq( str, "vbr") ) {
00725             bitrateMode = AudioEncoder::vbr;
00726 
00727             if ( cs->get( "quality" ) == 0 ) {
00728                 throw Exception( __FILE__, __LINE__,
00729                                  "quality not specified for VBR encoding");
00730             }
00731         } else {
00732             throw Exception( __FILE__, __LINE__,
00733                              "invalid bitrate mode: ", str);
00734         }
00735 
00736         server      = cs->getForSure( "server", " missing in section ", stream);
00737         str         = cs->getForSure( "port", " missing in section ", stream);
00738         port        = Util::strToL( str);
00739         password    = cs->getForSure("password"," missing in section ",stream);
00740         name        = cs->get( "name");
00741         url         = cs->get( "url");
00742         genre       = cs->get( "genre");
00743         str         = cs->get( "public");
00744         isPublic    = str ? (Util::strEq( str, "yes") ? true : false) : false;
00745         str         = cs->get( "lowpass");
00746         lowpass     = str ? Util::strToL( str) : 0;
00747         str         = cs->get( "highpass");
00748         highpass    = str ? Util::strToL( str) : 0;
00749         irc         = cs->get( "irc");
00750         aim         = cs->get( "aim");
00751         icq         = cs->get( "icq");
00752         str         = cs->get("fileAddDate");
00753         fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false;
00754 
00755         localDumpName = cs->get( "localDumpFile");
00756 
00757         // go on and create the things
00758 
00759         // check for and create the local dump file if needed
00760         if ( localDumpName != 0 ) {
00761             if ( fileAddDate ) {
00762                 localDumpName = Util::fileAddDate(localDumpName);
00763             }
00764 
00765             localDumpFile = new FileSink( localDumpName);
00766             if ( !localDumpFile->exists() ) {
00767                 if ( !localDumpFile->create() ) {
00768                     reportEvent( 1, "can't create local dump file",
00769                                     localDumpName);
00770                     localDumpFile = 0;
00771                 }
00772             }
00773             if ( fileAddDate ) {
00774                 delete[] localDumpFile;
00775             }
00776         }
00777 
00778         // streaming related stuff
00779         audioOuts[u].socket = new TcpSocket( server, port);
00780         audioOuts[u].server = new ShoutCast( audioOuts[u].socket.get(),
00781                                              password,
00782                                              bitrate,
00783                                              name,
00784                                              url,
00785                                              genre,
00786                                              isPublic,
00787                                              irc,
00788                                              aim,
00789                                              icq,
00790                                              localDumpFile,
00791                                              bufferSecs );
00792 
00793         audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(),
00794                                                    dsp.get(),
00795                                                    bitrateMode,
00796                                                    bitrate,
00797                                                    quality,
00798                                                    sampleRate,
00799                                                    channel,
00800                                                    lowpass,
00801                                                    highpass );
00802 
00803         encConnector->attach( audioOuts[u].encoder.get());
00804 #endif // HAVE_LAME_LIB
00805     }
00806 
00807     noAudioOuts += u;
00808 }
00809 
00810 
00811 /*------------------------------------------------------------------------------
00812  *  Look for the FileCast stream outputs in the config file
00813  *----------------------------------------------------------------------------*/
00814 void
00815 DarkIce :: configFileCast (  const Config      & config )
00816                                                         throw ( Exception )
00817 {
00818     // look for FileCast encoder output streams,
00819     // sections [file-0], [file-1], ...
00820     char            stream[]        = "file- ";
00821     size_t          streamLen       = Util::strLen( stream);
00822     unsigned int    u;
00823 
00824     for ( u = noAudioOuts; u < maxOutput; ++u ) {
00825         const ConfigSection    * cs;
00826 
00827         // ugly hack to change the section name to "stream0", "stream1", etc.
00828         stream[streamLen-1] = '0' + (u - noAudioOuts);
00829 
00830         if ( !(cs = config.get( stream)) ) {
00831             break;
00832         }
00833 
00834         const char                * str;
00835 
00836         const char                * format          = 0;
00837         AudioEncoder::BitrateMode   bitrateMode;
00838         unsigned int                bitrate         = 0;
00839         double                      quality         = 0.0;
00840         const char                * targetFileName  = 0;
00841         unsigned int                sampleRate      = 0;
00842         int                         lowpass         = 0;
00843         int                         highpass        = 0;
00844 
00845         format      = cs->getForSure( "format", " missing in section ", stream);
00846         if ( !Util::strEq( format, "vorbis")
00847           && !Util::strEq( format, "mp3")
00848           && !Util::strEq( format, "mp2")
00849           && !Util::strEq( format, "aac") ) {
00850             throw Exception( __FILE__, __LINE__,
00851                              "unsupported stream format: ", format);
00852         }
00853 
00854         str         = cs->getForSure("bitrate", " missing in section ", stream);
00855         bitrate     = Util::strToL( str);
00856         targetFileName    = cs->getForSure( "fileName",
00857                                             " missing in section ",
00858                                             stream);
00859         str         = cs->get( "sampleRate");
00860         sampleRate  = str ? Util::strToL( str) : dsp->getSampleRate();
00861 
00862         str         = cs->get( "bitrate");
00863         bitrate     = str ? Util::strToL( str) : 0;
00864         str         = cs->get( "quality");
00865         quality     = str ? Util::strToD( str) : 0.0;
00866         
00867         str         = cs->getForSure( "bitrateMode",
00868                                       " not specified in section ",
00869                                       stream);
00870         if ( Util::strEq( str, "cbr") ) {
00871             bitrateMode = AudioEncoder::cbr;
00872             
00873             if ( bitrate == 0 ) {
00874                 throw Exception( __FILE__, __LINE__,
00875                                  "bitrate not specified for CBR encoding");
00876             }
00877         } else if ( Util::strEq( str, "abr") ) {
00878             bitrateMode = AudioEncoder::abr;
00879 
00880             if ( bitrate == 0 ) {
00881                 throw Exception( __FILE__, __LINE__,
00882                                  "bitrate not specified for ABR encoding");
00883             }
00884         } else if ( Util::strEq( str, "vbr") ) {
00885             bitrateMode = AudioEncoder::vbr;
00886 
00887             if ( cs->get( "quality" ) == 0 ) {
00888                 throw Exception( __FILE__, __LINE__,
00889                                  "quality not specified for VBR encoding");
00890             }
00891         } else {
00892             throw Exception( __FILE__, __LINE__,
00893                              "invalid bitrate mode: ", str);
00894         }
00895 
00896         if (Util::strEq(format, "aac") && bitrateMode != AudioEncoder::abr) {
00897             throw Exception(__FILE__, __LINE__,
00898                             "currently the AAC format only supports "
00899                             "average bitrate mode");
00900         }
00901 
00902         str         = cs->get( "lowpass");
00903         lowpass     = str ? Util::strToL( str) : 0;
00904         str         = cs->get( "highpass");
00905         highpass    = str ? Util::strToL( str) : 0;
00906 
00907         // go on and create the things
00908 
00909         // the underlying file
00910         FileSink  * targetFile = new FileSink( targetFileName);
00911         if ( !targetFile->exists() ) {
00912             if ( !targetFile->create() ) {
00913                 throw Exception( __FILE__, __LINE__,
00914                                  "can't create output file", targetFileName);
00915             }
00916         }
00917 
00918         // streaming related stuff
00919         audioOuts[u].socket = 0;
00920         audioOuts[u].server = new FileCast( targetFile );
00921 
00922         if ( Util::strEq( format, "mp3") ) {
00923 #ifndef HAVE_LAME_LIB
00924                 throw Exception( __FILE__, __LINE__,
00925                                  "DarkIce not compiled with lame support, "
00926                                  "thus can't create mp3 stream: ",
00927                                  stream);
00928 #else
00929                 audioOuts[u].encoder = new LameLibEncoder(
00930                                                     audioOuts[u].server.get(),
00931                                                     dsp.get(),
00932                                                     bitrateMode,
00933                                                     bitrate,
00934                                                     quality,
00935                                                     sampleRate,
00936                                                     dsp->getChannel(),
00937                                                     lowpass,
00938                                                     highpass );
00939 #endif // HAVE_TWOLAME_LIB
00940         } else if ( Util::strEq( format, "mp2") ) {
00941 #ifndef HAVE_TWOLAME_LIB
00942                 throw Exception( __FILE__, __LINE__,
00943                                 "DarkIce not compiled with TwoLAME support, "
00944                                 "thus can't create MPEG Audio Layer 2 stream: ",
00945                                 stream);
00946 #else
00947                 audioOuts[u].encoder = new TwoLameLibEncoder(
00948                                                     audioOuts[u].server.get(),
00949                                                     dsp.get(),
00950                                                     bitrateMode,
00951                                                     bitrate,
00952                                                     sampleRate,
00953                                                     dsp->getChannel() );
00954 #endif // HAVE_TWOLAME_LIB
00955         } else if ( Util::strEq( format, "vorbis") ) {
00956 #ifndef HAVE_VORBIS_LIB
00957                 throw Exception( __FILE__, __LINE__,
00958                                 "DarkIce not compiled with Ogg Vorbis support, "
00959                                 "thus can't Ogg Vorbis stream: ",
00960                                 stream);
00961 #else
00962                 audioOuts[u].encoder = new VorbisLibEncoder(
00963                                                     audioOuts[u].server.get(),
00964                                                     dsp.get(),
00965                                                     bitrateMode,
00966                                                     bitrate,
00967                                                     dsp->getSampleRate(),
00968                                                     dsp->getChannel() );
00969 #endif // HAVE_VORBIS_LIB
00970         } else if ( Util::strEq( format, "aac") ) {
00971 #ifndef HAVE_FAAC_LIB
00972                 throw Exception( __FILE__, __LINE__,
00973                                 "DarkIce not compiled with AAC support, "
00974                                 "thus can't aac stream: ",
00975                                 stream);
00976 #else
00977                 audioOuts[u].encoder = new FaacEncoder(
00978                                                 audioOuts[u].server.get(),
00979                                                 dsp.get(),
00980                                                 bitrateMode,
00981                                                 bitrate,
00982                                                 quality,
00983                                                 sampleRate,
00984                                                 dsp->getChannel());
00985 #endif // HAVE_FAAC_LIB
00986         } else {
00987                 throw Exception( __FILE__, __LINE__,
00988                                 "Illegal stream format: ", format);
00989         }
00990 
00991         encConnector->attach( audioOuts[u].encoder.get());
00992     }
00993 
00994     noAudioOuts += u;
00995 }
00996 
00997 
00998 /*------------------------------------------------------------------------------
00999  *  Set POSIX real-time scheduling, if super-user
01000  *----------------------------------------------------------------------------*/
01001 void
01002 DarkIce :: setRealTimeScheduling ( void )               throw ( Exception )
01003 {
01004 // Only if the OS has the POSIX real-time scheduling functions implemented.
01005 #if defined( HAVE_SCHED_GETSCHEDULER ) && defined( HAVE_SCHED_GETPARAM )
01006     uid_t   euid;
01007 
01008     euid = geteuid();
01009 
01010     if ( euid == 0 ) {
01011         int                 high_priority;
01012         struct sched_param  param;
01013 
01014         /* store the old scheduling parameters */
01015         if ( (origSchedPolicy = sched_getscheduler(0)) == -1 ) {
01016             throw Exception( __FILE__, __LINE__, "sched_getscheduler", errno);
01017         }
01018 
01019         if ( sched_getparam( 0, &param) == -1 ) {
01020             throw Exception( __FILE__, __LINE__, "sched_getparam", errno);
01021         }
01022         origSchedPriority = param.sched_priority;
01023         
01024         /* set SCHED_FIFO with max - 1 priority */
01025         if ( (high_priority = sched_get_priority_max(SCHED_FIFO)) == -1 ) {
01026             throw Exception(__FILE__,__LINE__,"sched_get_priority_max",errno);
01027         }
01028         reportEvent( 8, "scheduler high priority", high_priority);
01029 
01030         param.sched_priority = high_priority - 1;
01031 
01032         if ( sched_setscheduler( 0, SCHED_FIFO, &param) == -1 ) {
01033             throw Exception( __FILE__, __LINE__, "sched_setscheduler", errno);
01034         }
01035 
01036         /* ask the new priortiy and report it */
01037         if ( sched_getparam( 0, &param) == -1 ) {
01038             throw Exception( __FILE__, __LINE__, "sched_getparam", errno);
01039         }
01040 
01041         reportEvent( 1,
01042                      "Using POSIX real-time scheduling, priority",
01043                      param.sched_priority );
01044     } else {
01045         reportEvent( 1,
01046         "Not running as super-user, unable to use POSIX real-time scheduling" );
01047         reportEvent( 1,
01048         "It is recommended that you run this program as super-user");
01049     }
01050 #else
01051     reportEvent( 1, "POSIX scheduling not supported on this system, "
01052                     "this may cause recording skips");
01053 #endif // HAVE_SCHED_GETSCHEDULER && HAVE_SCHED_GETPARAM
01054 }
01055 
01056 
01057 /*------------------------------------------------------------------------------
01058  *  Set the original scheduling of the process, the one prior to the
01059  *  setRealTimeScheduling call.
01060  *  WARNING: make sure you don't call this before setRealTimeScheduling!!
01061  *----------------------------------------------------------------------------*/
01062 void
01063 DarkIce :: setOriginalScheduling ( void )               throw ( Exception )
01064 {
01065 // Only if the OS has the POSIX real-time scheduling functions implemented.
01066 #if defined( HAVE_SCHED_GETSCHEDULER ) && defined( HAVE_SCHED_GETPARAM )
01067     uid_t   euid;
01068 
01069     euid = geteuid();
01070 
01071     if ( euid == 0 ) {
01072         struct sched_param  param;
01073 
01074         if ( sched_getparam( 0, &param) == -1 ) {
01075             throw Exception( __FILE__, __LINE__, "sched_getparam", errno);
01076         }
01077 
01078         param.sched_priority = origSchedPriority;
01079 
01080         if ( sched_setscheduler( 0, origSchedPolicy, &param) == -1 ) {
01081             throw Exception( __FILE__, __LINE__, "sched_setscheduler", errno);
01082         }
01083 
01084         reportEvent( 5, "reverted to original scheduling");
01085     }
01086 #endif // HAVE_SCHED_GETSCHEDULER && HAVE_SCHED_GETPARAM
01087 }
01088 
01089 
01090 /*------------------------------------------------------------------------------
01091  *  Run the encoder
01092  *----------------------------------------------------------------------------*/
01093 bool
01094 DarkIce :: encode ( void )                          throw ( Exception )
01095 {
01096     unsigned int       len;
01097     unsigned long      bytes;
01098 
01099     if ( !encConnector->open() ) {
01100         throw Exception( __FILE__, __LINE__, "can't open connector");
01101     }
01102     
01103     bytes = dsp->getSampleRate() *
01104             (dsp->getBitsPerSample() / 8UL) *
01105             dsp->getChannel() *
01106             duration;
01107                                                 
01108     len = encConnector->transfer( bytes, 4096, 1, 0 );
01109 
01110     reportEvent( 1, len, "bytes transfered to the encoders");
01111 
01112     encConnector->close();
01113 
01114     return true;
01115 }
01116 
01117 
01118 /*------------------------------------------------------------------------------
01119  *  Run
01120  *----------------------------------------------------------------------------*/
01121 int
01122 DarkIce :: run ( void )                             throw ( Exception )
01123 {
01124     reportEvent( 3, "encoding");
01125     
01126     if (enableRealTime) {
01127         setRealTimeScheduling();
01128     }
01129     encode();
01130     if (enableRealTime) {
01131         setOriginalScheduling();
01132     }
01133     reportEvent( 3, "encoding ends");
01134 
01135     return 0;
01136 }
01137 
01138 
01139 /*------------------------------------------------------------------------------
01140  
01141   $Source: /cvsroot/darkice/darkice/src/DarkIce.cpp,v $
01142 
01143   $Log: DarkIce.cpp,v $
01144   Revision 1.49  2006/03/23 09:34:57  darkeye
01145   fixed checking for mp3 and mp2 lib support for icecast-1.x streams
01146 
01147   Revision 1.48  2006/01/27 15:02:05  darkeye
01148   fixued issue of compiling without lame, but with twolame
01149 
01150   Revision 1.47  2006/01/25 22:47:15  darkeye
01151   added mpeg2 support, thanks to Nicholas J Humfrey
01152 
01153   Revision 1.46  2006/01/19 16:09:05  darkeye
01154   added check for bufferSecs setting not to be 0
01155 
01156   Revision 1.45  2005/10/22 10:34:21  darkeye
01157   added highpass and lowpass values to icecast2 sections
01158 
01159   Revision 1.44  2005/04/16 21:57:34  darkeye
01160   added AAC support through the faac codec, http://www.audiocoding.com/
01161 
01162   Revision 1.43  2005/04/11 19:27:43  darkeye
01163   added option to turn off automatic reconnect feature
01164 
01165   Revision 1.42  2005/04/04 08:36:17  darkeye
01166   commited changes to enable Jack support
01167   thanks to Nicholas J. Humfrey, njh@ecs.soton.ac.uk
01168 
01169   Revision 1.41  2005/04/03 05:12:20  jbebel
01170   Changed mechanism for testing the presence of the quality value such that
01171   zero is a valid option.
01172 
01173   Revision 1.40  2004/02/23 19:12:51  darkeye
01174   ported to NetBSD
01175 
01176   Revision 1.39  2004/02/19 06:47:06  darkeye
01177   finalized OpenBSD port
01178 
01179   Revision 1.38  2004/02/18 21:08:11  darkeye
01180   ported to OpenBSD (real-time scheduling not yet supported)
01181 
01182   Revision 1.37  2004/02/15 12:14:38  darkeye
01183   added patch to allow mp3 stream downsampling to mono for icecast2 as well
01184 
01185   Revision 1.36  2004/02/15 12:06:30  darkeye
01186   added ALSA support, thanks to Christian Forster
01187 
01188   Revision 1.35  2003/02/09 13:15:57  darkeye
01189   added feature for setting the TITLE comment field for vorbis streams
01190 
01191   Revision 1.34  2003/02/09 12:57:36  darkeye
01192   cosmetic changes to the fileAddDate option
01193 
01194   Revision 1.33  2002/11/20 16:52:05  wandereq
01195   added fileAddDate function
01196 
01197   Revision 1.32  2002/10/19 12:24:55  darkeye
01198   anged internals so that now each encoding/server connection is
01199   a separate thread
01200 
01201   Revision 1.31  2002/08/20 19:35:37  darkeye
01202   added possibility to specify maximum bitrate for Ogg Vorbis streams
01203 
01204   Revision 1.30  2002/08/20 18:37:49  darkeye
01205   added mp3 streaming possibility for icecast2
01206 
01207   Revision 1.29  2002/08/03 12:41:18  darkeye
01208   added possibility to stream in mono when recording in stereo
01209 
01210   Revision 1.28  2002/07/20 10:59:00  darkeye
01211   added support for Ogg Vorbis 1.0, removed support for rc2
01212 
01213   Revision 1.27  2002/04/13 11:26:00  darkeye
01214   added cbr, abr and vbr setting feature with encoding quality
01215 
01216   Revision 1.26  2002/03/28 16:43:11  darkeye
01217   enabled resampling and variable bitrates for vorbis (icecast2) streams
01218 
01219   Revision 1.25  2002/02/28 09:49:25  darkeye
01220   added possibility to save the encoded stream to a local file only
01221   (no streaming server needed)
01222 
01223   Revision 1.24  2002/02/20 11:54:11  darkeye
01224   added local dump file possibility
01225 
01226   Revision 1.23  2002/02/20 10:35:35  darkeye
01227   updated to work with Ogg Vorbis libs rc3 and current IceCast2 cvs
01228 
01229   Revision 1.22  2001/10/20 10:56:45  darkeye
01230   added possibility to disable highpass and lowpass filters for lame
01231 
01232   Revision 1.21  2001/10/19 12:39:42  darkeye
01233   created configure options to compile with or without lame / Ogg Vorbis
01234 
01235   Revision 1.20  2001/10/19 09:03:39  darkeye
01236   added support for resampling mp3 streams
01237 
01238   Revision 1.19  2001/09/14 19:31:06  darkeye
01239   added IceCast2 / vorbis support
01240 
01241   Revision 1.18  2001/09/11 15:05:21  darkeye
01242   added Solaris support
01243 
01244   Revision 1.17  2001/09/09 11:27:31  darkeye
01245   added support for ShoutCast servers
01246 
01247   Revision 1.16  2001/09/05 20:11:15  darkeye
01248   removed dependency on locally stored SGI STL header files
01249   now compiler-supplied C++ library STL header files are used
01250   compiles under GNU C++ 3
01251   hash_map (an SGI extension to STL) replaced with map
01252   std:: namespace prefix added to all STL class references
01253 
01254   Revision 1.15  2001/08/30 17:25:56  darkeye
01255   renamed configure.h to config.h
01256 
01257   Revision 1.14  2001/08/29 21:08:30  darkeye
01258   made some description options in the darkice config file optional
01259 
01260   Revision 1.13  2001/08/26 20:44:30  darkeye
01261   removed external command-line encoder support
01262   replaced it with a shared-object support for lame with the possibility
01263   of static linkage
01264 
01265   Revision 1.12  2000/12/20 12:36:47  darkeye
01266   added POSIX real-time scheduling
01267 
01268   Revision 1.11  2000/11/18 11:13:27  darkeye
01269   removed direct reference to cout, except from main.cpp
01270   all class use the Reporter interface to report events
01271 
01272   Revision 1.10  2000/11/17 15:50:48  darkeye
01273   added -Wall flag to compiler and eleminated new warnings
01274 
01275   Revision 1.9  2000/11/15 18:37:37  darkeye
01276   changed the transferable number of bytes to unsigned long
01277 
01278   Revision 1.8  2000/11/15 18:08:43  darkeye
01279   added multiple verbosity-level event reporting and verbosity command
01280   line option
01281 
01282   Revision 1.7  2000/11/13 19:38:55  darkeye
01283   moved command line parameter parsing from DarkIce.cpp to main.cpp
01284 
01285   Revision 1.6  2000/11/13 18:46:50  darkeye
01286   added kdoc-style documentation comments
01287 
01288   Revision 1.5  2000/11/10 20:16:21  darkeye
01289   first real tests with multiple streaming
01290 
01291   Revision 1.4  2000/11/09 22:09:46  darkeye
01292   added multiple outputs
01293   added configuration reading
01294   added command line processing
01295 
01296   Revision 1.3  2000/11/08 17:29:50  darkeye
01297   added configuration file reader
01298 
01299   Revision 1.2  2000/11/05 14:08:27  darkeye
01300   changed builting to an automake / autoconf environment
01301 
01302   Revision 1.1.1.1  2000/11/05 10:05:49  darkeye
01303   initial version
01304 
01305   
01306 ------------------------------------------------------------------------------*/
01307 

Generated on Fri May 19 15:36:48 2006 for DarkIce by  doxygen 1.4.4