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

JackDspSource.cpp

Go to the documentation of this file.
00001 /*------------------------------------------------------------------------------
00002 
00003    Copyright (c) 2005 Nicholas Humfrey. All rights reserved.
00004 
00005    Tyrell DarkIce
00006 
00007    File     : JackDspSource.cpp
00008    Version  : $Revision: 1.1 $
00009    Author   : $Author: darkeye $
00010    Location : $Source: /cvsroot/darkice/darkice/src/JackDspSource.cpp,v $
00011    
00012    Copyright notice:
00013 
00014     This program is free software; you can redistribute it and/or
00015     modify it under the terms of the GNU General Public License  
00016     as published by the Free Software Foundation; either version 2
00017     of the License, or (at your option) any later version.
00018    
00019     This program is distributed in the hope that it will be useful,
00020     but WITHOUT ANY WARRANTY; without even the implied warranty of 
00021     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
00022     GNU General Public License for more details.
00023    
00024     You should have received a copy of the GNU General Public License
00025     along with this program; if not, write to the Free Software
00026     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00027 
00028 ------------------------------------------------------------------------------*/
00029 
00030 /* ============================================================ include files */
00031 
00032 #include "AudioSource.h"
00033 
00034 #ifdef SUPPORT_JACK_DSP
00035 // only compile this code if there is support for it
00036 
00037 #ifdef HAVE_CONFIG_H
00038 #include "config.h"
00039 #endif
00040 
00041 #ifdef HAVE_UNISTD_H
00042 #include <unistd.h>
00043 #else
00044 #error need unistd.h
00045 #endif
00046 
00047 #ifdef HAVE_STRING_H
00048 #include <string.h>
00049 #else
00050 #error need string.h
00051 #endif
00052 
00053 #ifdef HAVE_SYS_TYPES_H
00054 #include <sys/types.h>
00055 #else
00056 #error need sys/types.h
00057 #endif
00058 
00059 #ifdef HAVE_MATH_H
00060 #include <math.h>
00061 #else
00062 #error need math.h
00063 #endif
00064 
00065 #include "Util.h"
00066 #include "Exception.h"
00067 #include "JackDspSource.h"
00068 
00069 
00070 /* ===================================================  local data structures */
00071 
00072 
00073 /* ================================================  local constants & macros */
00074 
00075 /*------------------------------------------------------------------------------
00076  *  File identity
00077  *----------------------------------------------------------------------------*/
00078 static const char fileid[] = "$Id: JackDspSource.cpp,v 1.1 2005/04/04 08:36:17 darkeye Exp $";
00079 
00080 
00081 /* ===============================================  local function prototypes */
00082 
00083 
00084 /* =============================================================  module code */
00085 
00086 /*------------------------------------------------------------------------------
00087  *  Initialize the object
00088  *----------------------------------------------------------------------------*/
00089 void
00090 JackDspSource :: init ( const char* name )           throw ( Exception )
00091 {
00092     // Set defaults
00093     ports[0]     = NULL;        // Left Port
00094     ports[1]     = NULL;        // Right Port
00095     rb[0]        = NULL;        // Left Ring Buffer
00096     rb[1]        = NULL;        // Right Ring Buffer
00097     client       = NULL;
00098     auto_connect = false;       // Default is to not auto connect the JACK ports
00099     tmp_buffer   = NULL;        // Buffer big enough for one 'read' of audio
00100 
00101     // Auto connect the ports ?
00102     if ( Util::strEq( name, "jack_auto", 9) ) {
00103         auto_connect = true;
00104     }
00105     
00106     // Check the sample size
00107     if (getBitsPerSample() != 16) {
00108         throw Exception( __FILE__, __LINE__,
00109                         "JackDspSource doesn't support non 16-bit samples");
00110     }
00111 }
00112 
00113 
00114 
00115 /*------------------------------------------------------------------------------
00116  *  De-initialize the object
00117  *----------------------------------------------------------------------------*/
00118 void
00119 JackDspSource :: strip ( void )                      throw ( Exception )
00120 {
00121     if ( isOpen() ) {
00122         close();
00123     }
00124     
00125     // Free the temporary buffer
00126     if (tmp_buffer) {
00127         free(tmp_buffer);
00128         tmp_buffer = NULL;
00129     }
00130 
00131 }
00132 
00133 /*------------------------------------------------------------------------------
00134  *  Attempt to connect up the JACK ports automatically
00135  *   - Just connect left&right to the first two output ports we find
00136  *----------------------------------------------------------------------------*/
00137 void
00138 JackDspSource :: do_auto_connect ( void )                   throw ( Exception )
00139 {
00140     const char **all_ports;
00141     unsigned int ch = 0;
00142     int          i;
00143 
00144     Reporter::reportEvent( 10, "JackDspSource :: do_auto_connect");
00145     
00146     // Get a list of all the jack ports
00147     all_ports = jack_get_ports (client, NULL, NULL, JackPortIsOutput);
00148     if (!ports) {
00149         throw Exception( __FILE__, __LINE__, "jack_get_ports() returned NULL.");
00150     }
00151     
00152     // Step through each port name
00153     for (i = 0; all_ports[i]; ++i) {
00154 
00155         const char* in  = all_ports[i];
00156         const char* out = jack_port_name( ports[ch] );
00157         
00158         Reporter::reportEvent( 2, "Connecting", in, "to", out);
00159         
00160         if (jack_connect(client, in, out)) {
00161             throw Exception( __FILE__, __LINE__,
00162                             "Failed to jack_connect() ports", in, out);
00163         }
00164     
00165         // Found enough ports ?
00166         if (++ch >= getChannel()) break;
00167     }
00168     
00169     free( all_ports );
00170 
00171 }
00172 
00173 
00174 /*------------------------------------------------------------------------------
00175  *  Open the audio source
00176  *----------------------------------------------------------------------------*/
00177 bool
00178 JackDspSource :: open ( void )                       throw ( Exception )
00179 {
00180     char         client_name[255];
00181     size_t       rb_size;
00182     unsigned int c;
00183     
00184     if ( isOpen() ) {
00185         return false;
00186     }
00187 
00188     // Register client with Jack
00189     snprintf(client_name, 255, "darkice-%d", getpid());
00190     if ((client = jack_client_new(client_name)) == NULL) {
00191         throw Exception( __FILE__, __LINE__, "JACK server not running?");
00192     }
00193     Reporter::reportEvent( 1, "Registering as JACK client", client_name);
00194 
00195 
00196     // Check the sample rate is correct
00197     if (jack_get_sample_rate( client ) != (jack_nframes_t)getSampleRate()) {
00198         throw Exception( __FILE__, __LINE__,
00199                         "JACK server sample rate is different than "
00200                         "sample rate in darkice config file");
00201     }
00202 
00203 
00204     // Register ports with Jack
00205     if (getChannel() == 1) {
00206         if (!(ports[0] = jack_port_register(client,
00207                                             "mono",
00208                                             JACK_DEFAULT_AUDIO_TYPE,
00209                                             JackPortIsInput,
00210                                             0))) {
00211             throw Exception( __FILE__, __LINE__,
00212                             "Cannot register input port", "mono");
00213         }
00214     } else if (getChannel() == 2) {
00215         if (!(ports[0] = jack_port_register(client,
00216                                             "left",
00217                                             JACK_DEFAULT_AUDIO_TYPE,
00218                                             JackPortIsInput,
00219                                             0))) {
00220             throw Exception( __FILE__, __LINE__,
00221                             "Cannot register input port", "left");
00222         }
00223         if (!(ports[1] = jack_port_register(client,
00224                                             "right",
00225                                             JACK_DEFAULT_AUDIO_TYPE,
00226                                             JackPortIsInput, 0))) {
00227             throw Exception( __FILE__, __LINE__,
00228                             "Cannot register input port", "right");
00229         }
00230     } else {
00231         throw Exception( __FILE__, __LINE__,
00232                         "Invalid number of channels", getChannel());
00233     }
00234 
00235 
00236     // Create a ring buffer for each channel
00237     rb_size = 2
00238             * jack_get_sample_rate(client)
00239             * sizeof (jack_default_audio_sample_t);
00240     for (c=0; c<getChannel(); c++) {
00241         rb[c] = jack_ringbuffer_create(rb_size);
00242         if (!rb[c]) {
00243             throw Exception( __FILE__, __LINE__,
00244                             "Failed to create ringbuffer for", "channel", c);
00245         }
00246     }
00247 
00248 
00249     // Set the callbacks
00250     jack_on_shutdown(client, JackDspSource::shutdown_callback, (void*)this);
00251     if (jack_set_process_callback(client,
00252                                   JackDspSource::process_callback,
00253                                   (void*)this)) {
00254         throw Exception( __FILE__, __LINE__, "Failed to set process callback");
00255     }    
00256 
00257     // Activate client
00258     if (jack_activate(client)) {
00259         throw Exception( __FILE__, __LINE__, "Can't activate client");
00260     }
00261     
00262     // Attempt to automatically connect up our input ports to something ?
00263     if (auto_connect) {
00264         do_auto_connect();
00265     }
00266     
00267     return true;
00268 }
00269 
00270 
00271 /*------------------------------------------------------------------------------
00272  *  Check wether read() would return anything
00273  *----------------------------------------------------------------------------*/
00274 bool
00275 JackDspSource :: canRead ( unsigned int   sec,
00276                            unsigned int   usec )    throw ( Exception )
00277 {
00278     size_t available=0;
00279     
00280     if ( !isOpen() ) {
00281         return false;
00282     }
00283     
00284     // How many bytes available in ring buffer ?
00285     available = jack_ringbuffer_read_space( rb[0] );
00286     if (available)    return true;
00287     
00288     // Sleep and check again
00289     // FIXME: should we really sleep the full duration ?
00290     usleep( (sec*1000000) + usec );
00291     
00292     available = jack_ringbuffer_read_space( rb[0] );
00293     if (available) {
00294         return true;
00295     } else {
00296         return false;
00297     }
00298 }
00299 
00300 
00301 /*------------------------------------------------------------------------------
00302  *  Read from the audio source
00303  *----------------------------------------------------------------------------*/
00304 unsigned int
00305 JackDspSource :: read (   void          * buf,
00306                           unsigned int    len )     throw ( Exception )
00307 {
00308     jack_nframes_t samples         = len / 2 / getChannel();
00309     jack_nframes_t samples_read[2] = {0,0};
00310     short        * output          = (short*)buf;
00311     unsigned int c, n;
00312 
00313     if ( !isOpen() ) {
00314         return 0;
00315     }
00316 
00317 
00318     // Ensure the temporary buffer is big enough
00319     tmp_buffer = (jack_default_audio_sample_t*)realloc(tmp_buffer,
00320                              samples * sizeof( jack_default_audio_sample_t ) );
00321     if (!tmp_buffer) {
00322         throw Exception( __FILE__, __LINE__, "realloc on tmp_buffer failed");
00323     }
00324 
00325 
00326     for (c=0; c<getChannel(); c++)
00327     {    
00328         // Copy frames from ring buffer to temporary buffer
00329         // and then convert samples to output buffer
00330         int bytes_read = jack_ringbuffer_read(rb[c],
00331                                              (char*)tmp_buffer,
00332                               samples * sizeof( jack_default_audio_sample_t ));
00333         samples_read[c] = bytes_read / sizeof( jack_default_audio_sample_t );
00334         
00335 
00336         // Convert samples from float to short and put in output buffer
00337         for(n=0; n<samples_read[c]; n++) {
00338             int tmp = lrintf(tmp_buffer[n] * 32768.0f);
00339             if (tmp > SHRT_MAX) {
00340                 output[n*getChannel()+c] = SHRT_MAX;
00341             } else if (tmp < SHRT_MIN) {
00342                 output[n*getChannel()+c] = SHRT_MIN;
00343             } else {
00344                 output[n*getChannel()+c] = (short) tmp;
00345             }
00346         }
00347     }
00348 
00349     // Didn't get as many samples as we wanted ?
00350     if (getChannel() == 2 && samples_read[0] != samples_read[1]) {
00351         Reporter::reportEvent( 2,
00352                               "Warning: Read a different number of samples "
00353                               "for left and right channels");
00354     }
00355 
00356     // Return the number of bytes put in the output buffer
00357     return samples_read[0] * 2 * getChannel();
00358 }
00359 
00360 
00361 /*------------------------------------------------------------------------------
00362  *  Close the audio source
00363  *----------------------------------------------------------------------------*/
00364 void
00365 JackDspSource :: close ( void )                  throw ( Exception )
00366 {
00367     unsigned int i;
00368 
00369     if ( !isOpen() ) {
00370         return;
00371     }
00372 
00373     for(i = 0; i < getChannel(); i++) {
00374         // Close the port for channel
00375         if ( ports[i] ) {
00376             jack_port_unregister( client, ports[i] );
00377             ports[i] = NULL;
00378         }
00379         
00380         // Free up the ring buffer for channel
00381         if ( rb[i] ) {
00382             jack_ringbuffer_free( rb[i] );
00383             rb[i] = NULL;
00384         }
00385     }
00386 
00387     /* Leave the jack graph */
00388     if (client) {
00389         jack_client_close(client);
00390         client = NULL;
00391     }
00392 
00393 }
00394 
00395 
00396 /*------------------------------------------------------------------------------
00397  *  Callback called by JACK when audio is available
00398  *
00399  *  Don't do anything too expensive here
00400  *      - just shove audio samples in ring buffer
00401  *----------------------------------------------------------------------------*/
00402 int
00403 JackDspSource :: process_callback( jack_nframes_t nframes, void *arg )
00404 {
00405     JackDspSource* self     = (JackDspSource*)arg;
00406     size_t         to_write = sizeof (jack_default_audio_sample_t) * nframes;
00407     unsigned int   c;
00408     
00409     // Wait until it is ready
00410     if (self->client == NULL) {
00411         return 0;
00412     }
00413 
00414     /* copy data to ringbuffer; one per channel */
00415     for (c=0; c < self->getChannel(); c++) {    
00416         char *buf  = (char*)jack_port_get_buffer(self->ports[c], nframes);
00417         size_t len = jack_ringbuffer_write(self->rb[c], buf, to_write);
00418         if (len < to_write) {
00419             Reporter::reportEvent( 1, "failed to write to ring ruffer");
00420             return 1;
00421          }
00422     }
00423 
00424     // Success
00425     return 0;
00426 }
00427 
00428 /*------------------------------------------------------------------------------
00429  *  Callback called when 
00430  *----------------------------------------------------------------------------*/
00431 void
00432 JackDspSource :: shutdown_callback( void *arg )
00433 {
00434     //JackDspSource* self = (JackDspSource*)arg;
00435 
00436     Reporter::reportEvent( 1, "JackDspSource :: shutdown_callback");
00437 }
00438 
00439 
00440 #endif // SUPPORT_JACK_DSP
00441 
00442 
00443 /*------------------------------------------------------------------------------
00444  
00445   $Source: /cvsroot/darkice/darkice/src/JackDspSource.cpp,v $
00446 
00447   $Log: JackDspSource.cpp,v $
00448   Revision 1.1  2005/04/04 08:36:17  darkeye
00449   commited changes to enable Jack support
00450   thanks to Nicholas J. Humfrey, njh@ecs.soton.ac.uk
00451 
00452   
00453 ------------------------------------------------------------------------------*/
00454 

Generated on Sat Oct 22 13:17:04 2005 for DarkIce by  doxygen 1.4.4