- -( dyne // software :: culture :: events :: planet :: discussion :: museum \\ freaknet )- -
 
Main Page | Modules | Class Hierarchy | Class List | File List | Class Members | File Members

dev_sound.cpp

Go to the documentation of this file.
00001 /* MuSE - Multiple Streaming Engine
00002  * SoundDevice class interfacing Portaudio PABLIO library
00003  * Copyright (C) 2004 Denis Roio aka jaromil <jaromil@dyne.org>
00004  *
00005  * This source code is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Public License as published 
00007  * by the Free Software Foundation; either version 2 of the License,
00008  * or (at your option) any later version.
00009  *
00010  * This source code is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00013  * Please refer to the GNU Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Public License along with
00016  * this source code; if not, write to:
00017  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  
00019  "$Id: dev_sound.cpp,v 1.11 2005/04/05 00:05:56 xant Exp $"
00020  
00021  */
00022 
00023 #include <dev_sound.h>
00024 #include <jutils.h>
00025 #include <generic.h>
00026 #include <config.h>
00027 
00028 #define PA_SAMPLE_TYPE paFloat32
00029 #define PA_SAMPLES_PER_FRAME 2
00030 #define PA_NUM_SECONDS 5
00031 #define FRAMES_PER_BUFFER   (64)
00032 #define PA_PIPE_SIZE (IN_BUFFER*2)*4
00033 
00034 #define INPUT_DEVICE  Pa_GetDefaultInputDeviceID()
00035 #define OUTPUT_DEVICE Pa_GetDefaultOutputDeviceID()
00036 
00037 
00038 #ifdef HAVE_JACK
00039 int dev_jack_process(jack_nframes_t nframes, void *arg) {
00040 jack_nframes_t opframes;
00041 SoundDevice *dev = (SoundDevice*)arg;
00042 if(!dev->jack) return 0; // just return
00043 
00044 // take output from pipe and send it to jack
00045 dev->jack_out_buf = (jack_default_audio_sample_t*)
00046 jack_port_get_buffer(dev->jack_out_port,nframes);
00047 opframes = dev->jack_out_pipe->read
00048 (nframes * sizeof(float) * 2 , dev->jack_out_buf);
00049 
00050 // take input from jack and send it in pipe
00051 dev->jack_in_buf = (jack_default_audio_sample_t*)
00052 jack_port_get_buffer(dev->jack_in_port,nframes);
00053 dev->jack_in_pipe->write // does the float2int conversion in one pass
00054 (nframes * sizeof(float) * 2 , dev->jack_in_buf);
00055 
00056 return 0;
00057 }
00058 
00059 void dev_jack_shutdown(void *arg) {
00060 SoundDevice *dev = (SoundDevice*)arg;
00061 // close the jack channels
00062 dev->jack = false;
00063 jack_port_unregister(dev->jack_client, dev->jack_in_port);
00064 jack_port_unregister(dev->jack_client, dev->jack_out_port);
00065 jack_deactivate(dev->jack_client);
00066 delete dev->jack_in_pipe;
00067 delete dev->jack_out_pipe;
00068 }
00069 #endif
00070 
00071 
00072 SoundDevice::SoundDevice() {
00073 memset(&input_device,0,sizeof(input_device));
00074 memset(&output_device,0,sizeof(output_device));
00075 pa_dev.input = &input_device;
00076 pa_dev.output = &output_device;
00077 input_device.pipe = new Pipe(PA_PIPE_SIZE);
00078 input_device.pipe->set_block(false,true);
00079 output_device.pipe = new Pipe(PA_PIPE_SIZE);
00080 output_device.pipe->set_block(true,false);
00081 input_device.pipe->set_output_type("copy_float_to_int16");
00082 output_device.pipe->set_input_type("copy_int16_to_float");
00083 jack = false;
00084 jack_in = false;
00085 jack_out = false;
00086 }
00087 
00088 SoundDevice::~SoundDevice() {
00089 close();
00090 }
00091 
00092 static int pa_process( void *inputBuffer, void *outputBuffer, 
00093         unsigned long framesPerBuffer, 
00094         PaTimestamp outTime, void *userData )
00095 {
00096 int readBytes;
00097 PaDevices *dev = (PaDevices *)userData;
00098 long len = framesPerBuffer * (PA_SAMPLES_PER_FRAME*sizeof(PA_SAMPLE_TYPE));
00099   if(inputBuffer != NULL) {
00100     readBytes = dev->input->pipe->write(len,inputBuffer);
00101   }
00102   if(outputBuffer != NULL) {
00103     readBytes = dev->output->pipe->read(len,outputBuffer);
00104         
00105     /* Zero out remainder of buffer if we run out of data. */
00106  //   for( i=numRead;i<len;i++) {
00107   //    ((char *)outputBuffer)[i] = 0;
00108    // }
00109   }
00110   return 0;
00111 }
00112 
00113 bool SoundDevice::input(bool state) {
00114   bool res = false;
00115   if(jack) return true;
00116   if(!res) res = pa_open(state,PaInput);
00117   return res;
00118 }
00119 
00120 PaError SoundDevice::pa_real_open(int mode) {
00121   return Pa_OpenStream( ((mode & PaInput) == PaInput)?&input_device.stream:&output_device.stream,
00122     ((mode & PaInput) == PaInput)?input_device.id:paNoDevice,
00123     ((mode & PaInput) == PaInput)?2:0,
00124     PA_SAMPLE_TYPE,
00125     NULL,
00126     ((mode & PaOutput) == PaOutput)?output_device.id:paNoDevice,
00127     ((mode & PaOutput) == PaOutput)?2:0,
00128     PA_SAMPLE_TYPE,
00129     NULL,
00130     SAMPLE_RATE,
00131     FRAMES_PER_BUFFER,
00132     0,  /* number of buffers, if zero then use default minimum */
00133     0, // paClipOff,     /* we won't output out of range samples so don't bother clipping them */
00134     pa_process,
00135     &pa_dev );
00136 }
00137 
00138 bool SoundDevice::pa_open(bool state,int mode) {
00139   PaDevInfo *dev,*other;
00140   int creq,oreq;
00141   char dir[7];
00142   if(mode == PaInput) { // input requested
00143     dev = &input_device;
00144     other = &output_device;
00145     creq = PaInput;
00146     oreq = PaOutput;
00147     strcpy(dir,"input");
00148     dev->id = Pa_GetDefaultInputDeviceID();
00149   }
00150   else if(mode == PaOutput) { // output requested
00151     dev = &output_device;
00152     other = &input_device;
00153     creq = PaOutput;
00154     oreq = PaInput;
00155     strcpy(dir,"output");
00156     dev->id = Pa_GetDefaultOutputDeviceID();
00157   }
00158   if(state && ((pa_mode & creq) != creq)) {
00159     dev->info = (PaDeviceInfo*)Pa_GetDeviceInfo( dev->id );
00160     if(dev->info) func("%s device: %s",dir,dev->info->name);
00161     else {
00162       error("%s device not available",dir);
00163       return false;
00164     }
00165     if((pa_mode & oreq) == oreq) {
00166       /* input device is already opened...check if we are trying to open the same device */
00167       if(other->info) { 
00168         if(strcmp(other->info->name,dev->info->name) == 0) {
00169           Pa_StopStream( other->stream );
00170           Pa_CloseStream( other->stream );
00171           err = pa_real_open(PaInput|PaOutput);
00172           if(err == paNoError ) output_device.stream = input_device.stream;
00173                 }
00174                 else {
00175           err = pa_real_open(mode);
00176                 }
00177       }
00178       else {
00179         error("Full duplex has been requested but we don't have portaudio information");
00180         return false;
00181       }
00182     }
00183     else {
00184       err = pa_real_open(mode);
00185     }
00186     if( err != paNoError) {
00187       Pa_Terminate();
00188       error("error opening %s sound device: %s",dir,Pa_GetErrorText( err ) );
00189       return false;
00190     }
00191     else {
00192       err = Pa_StartStream(dev->stream);
00193       if(err != paNoError) {
00194          error("error starting %s audio stream: %s",dir,Pa_GetErrorText( err ) );
00195          return false;
00196       }
00197       pa_mode = pa_mode | creq;
00198     }
00199   } else if(!state && dev->stream) { // XXX - i have to check if this is still right
00200     
00201     if((pa_mode & creq) == creq) {
00202        if((pa_mode & oreq) == oreq) {
00203          pa_mode = oreq;
00204        }
00205        else {
00206          Pa_StopStream(dev->stream);
00207          Pa_CloseStream(dev->stream);
00208          pa_mode = PaNull;
00209        }
00210     }
00211     dev->stream = NULL;
00212     dev->info = NULL;
00213     dev->pipe->flush();
00214         //delete dev->pipe;
00215   }
00216   return true;
00217 }
00218 
00219 bool SoundDevice::output(bool state) {
00220   bool res = false;
00221   if(jack) return true;
00222   if(!res) res = pa_open(state,PaOutput);
00223   return res;
00224 }
00225 
00226 bool SoundDevice::open(bool read, bool write) {
00227 
00228   //  notice("detecting sound device");
00229 
00230 #ifdef HAVE_JACK
00231   // we try to open up a jack client
00232   jack_sample_size = sizeof(jack_default_audio_sample_t);
00233   if(!jack) // check if it is not allready on
00234     if( (jack_client = jack_client_new("MuSE")) !=0 ) {
00235       notice("jack audio daemon detected");
00236       act("hooking in/out sound channels");
00237       warning("this feature is still experimental and won't work!");
00238       warning("you need to stop jack and free the audio card");
00239       jack = true;
00240       jack_samplerate = jack_get_sample_rate(jack_client);
00241       jack_set_process_callback(jack_client, dev_jack_process, this);    
00242       jack_on_shutdown(jack_client,dev_jack_shutdown,this);
00243 
00244       jack_in_pipe = new Pipe();
00245       jack_in_pipe->set_output_type("copy_float_to_int16");
00246       jack_in_pipe->set_block(false,true);
00247 
00248       jack_out_pipe = new Pipe();
00249       jack_out_pipe->set_input_type("copy_int16_to_float");
00250       jack_in_pipe->set_block(true,false);
00251 
00252       // open the jack input channel
00253       jack_in_port = jack_port_register(jack_client, "capture",
00254                                         JACK_DEFAULT_AUDIO_TYPE,
00255                                         JackPortIsInput, 0);
00256       // open the jack output channel
00257       jack_out_port = jack_port_register(jack_client, "playback",
00258                                          JACK_DEFAULT_AUDIO_TYPE,
00259                                          JackPortIsOutput, 0);
00260       
00261       jack_activate(jack_client);
00262       return true;
00263     }
00264 #endif
00265   if( ! output(write) ) return false;
00266   
00267   //if( ! input(read) ) return false;
00268   
00269   return true;
00270 }
00271 
00272 void SoundDevice::close() {
00273   if((pa_mode&PaInput) == PaInput) {
00274         if((pa_mode&PaOutput) == PaOutput) {
00275           pa_mode = PaOutput;
00276           if(output_device.stream == input_device.stream)
00277             output_device.stream = NULL;
00278         }
00279         else pa_mode = PaNull;
00280         if(input_device.stream) {
00281        Pa_StopStream( input_device.stream);
00282        Pa_CloseStream( input_device.stream );
00283        input_device.stream = NULL;
00284         }
00285     input_device.pipe->flush();
00286     //delete input_device.pipe;
00287   }
00288 
00289   if((pa_mode&PaOutput) == PaOutput) {
00290     if(output_device.stream) {
00291        Pa_StopStream( output_device.stream);
00292        Pa_CloseStream( output_device.stream );
00293        output_device.stream = NULL;
00294         }
00295     output_device.pipe->flush();
00296         if((pa_mode&PaInput) == PaInput)
00297                 pa_mode = PaInput;
00298         else pa_mode = PaNull;
00299   }
00300     //delete output_device.pipe;
00301 }
00302 
00303 int SoundDevice::read(void *buf, int len) {
00304   // len is in samples: 4*2 32bit stereo
00305   int res = -1;
00306 
00307   if(jack) {
00308 
00309     res = jack_in_pipe->read(len*2,buf);
00310 
00311   } else if(input_device.stream) { // portaudio
00312 
00313     // takes number of left and right frames (stereo / 2)
00314     res = input_device.pipe->read(len*2,buf);
00315   }  
00316   return res;
00317 }
00318 
00319 int SoundDevice::write(void *buf, int len) {
00320   // len is in samples, for bytes must *2 (16bit)
00321   int res = -1;
00322 
00323   if(jack) { // jack audio daemon
00324 
00325     res = jack_out_pipe->write(len*2,buf);
00326 
00327   } else if(output_device.stream) { // portaudio
00328         res = output_device.pipe->write(len,buf);
00329   }
00330   return res;
00331 }
00332 
00333 void SoundDevice::flush_output() {
00334    output_device.pipe->flush();
00335 }
00336 void SoundDevice::flush_input() {
00337    input_device.pipe->flush();
00338 }