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

audiooss.C

Go to the documentation of this file.
00001 #include "audioconfig.h"
00002 #include "audiodevice.h"
00003 #include "audiooss.h"
00004 #include "clip.h"
00005 #include "condition.h"
00006 #include "errno.h"
00007 #include "playbackconfig.h"
00008 #include "preferences.h"
00009 #include "recordconfig.h"
00010 
00011 #include <string.h>
00012 
00013 #ifdef HAVE_OSS
00014 
00015 // These are only available in commercial OSS
00016 
00017 #ifndef AFMT_S32_LE
00018 #define AFMT_S32_LE      0x00001000
00019 #define AFMT_S32_BE      0x00002000
00020 #endif
00021 
00022 
00023 // Synchronie multiple devices by using threads
00024 
00025 OSSThread::OSSThread(AudioOSS *device)
00026  : Thread(1, 0, 0)
00027 {
00028         rd = 0;
00029         wr = 0;
00030         done = 0;
00031         this->device = device;
00032         input_lock = new Condition(0, "OSSThread::input_lock");
00033         output_lock = new Condition(1, "OSSThread::output_lock");
00034         read_lock = new Condition(0, "OSSThread::read_lock");
00035         write_lock = new Condition(0, "OSSThread::write_lock");
00036 }
00037 
00038 OSSThread::~OSSThread()
00039 {
00040         done = 1;
00041         input_lock->unlock();
00042         Thread::join();
00043         delete input_lock;
00044         delete output_lock;
00045         delete read_lock;
00046         delete write_lock;
00047 }
00048 
00049 void OSSThread::run()
00050 {
00051         while(!done)
00052         {
00053                 input_lock->lock("OSSThread::run 1");
00054                 if(rd)
00055                 {
00056                         int result = read(fd, data, bytes);
00057                         read_lock->unlock();
00058                 }
00059                 else
00060                 if(wr)
00061                 {
00062                         if(done) return;
00063 
00064 
00065                         Thread::enable_cancel();
00066                         write(fd, data, bytes);
00067                         Thread::disable_cancel();
00068 
00069 
00070                         if(done) return;
00071                         write_lock->unlock();
00072                 }
00073                 output_lock->unlock();
00074         }
00075 }
00076 
00077 void OSSThread::write_data(int fd, unsigned char *data, int bytes)
00078 {
00079         output_lock->lock("OSSThread::write_data");
00080         wr = 1;
00081         rd = 0;
00082         this->data = data;
00083         this->bytes = bytes;
00084         this->fd = fd;
00085         input_lock->unlock();
00086 }
00087 
00088 void OSSThread::read_data(int fd, unsigned char *data, int bytes)
00089 {
00090         output_lock->lock("OSSThread::read_data");
00091         wr = 0;
00092         rd = 1;
00093         this->data = data;
00094         this->bytes = bytes;
00095         this->fd = fd;
00096         input_lock->unlock();
00097 }
00098 
00099 void OSSThread::wait_read()
00100 {
00101         read_lock->lock("OSSThread::wait_read");
00102 }
00103 
00104 void OSSThread::wait_write()
00105 {
00106         write_lock->lock("OSSThread::wait_write");
00107 }
00108 
00109 
00110 
00111 
00112 
00113 
00114 
00115 
00116 
00117 
00118 
00119 AudioOSS::AudioOSS(AudioDevice *device)
00120  : AudioLowLevel(device)
00121 {
00122         for(int i = 0; i < MAXDEVICES; i++)
00123         {
00124                 dsp_in[i] = dsp_out[i] = dsp_duplex[i] = 0;
00125                 thread[i] = 0;
00126                 data[i] = 0;
00127                 data_allocated[i] = 0;
00128         }
00129 }
00130 
00131 AudioOSS::~AudioOSS()
00132 {
00133 }
00134 
00135 int AudioOSS::open_input()
00136 {
00137         device->in_channels = 0;
00138         for(int i = 0; i < MAXDEVICES; i++)
00139         {
00140                 if(device->in_config->oss_enable[i])
00141                         device->in_channels += device->in_config->oss_in_channels[i];
00142         }
00143         device->in_bits = device->in_config->oss_in_bits;
00144 // 24 bits not available in OSS
00145         if(device->in_bits == 24) device->in_bits = 32;
00146 
00147         for(int i = 0; i < MAXDEVICES; i++)
00148         {
00149                 if(device->in_config->oss_enable[i])
00150                 {
00151 //printf("AudioOSS::open_input 10\n");
00152                         dsp_in[i] = open(device->in_config->oss_in_device[i], O_RDONLY/* | O_NDELAY*/);
00153 //printf("AudioOSS::open_input 20\n");
00154                         if(dsp_in[i] < 0) fprintf(stderr, "AudioOSS::open_input %s: %s\n", 
00155                                 device->in_config->oss_in_device[i], 
00156                                 strerror(errno));
00157 
00158                         int format = get_fmt(device->in_config->oss_in_bits);
00159                         int buffer_info = sizetofrag(device->in_samples, 
00160                                 device->in_config->oss_in_channels[i], 
00161                                 device->in_config->oss_in_bits);
00162 
00163                         set_cloexec_flag(dsp_in[i], 1);
00164 
00165 //printf("AudioOSS::open_input %d %d %d\n", device->in_samples, device->in_config->oss_in_channels[i], device->in_config->oss_in_bits);
00166 // For the ice1712 the buffer must be maximum or no space will be allocated.
00167                         if(device->driver == AUDIO_OSS_ENVY24) buffer_info = 0x7fff000f;
00168                         if(ioctl(dsp_in[i], SNDCTL_DSP_SETFRAGMENT, &buffer_info)) printf("SNDCTL_DSP_SETFRAGMENT failed.\n");
00169                         if(ioctl(dsp_in[i], SNDCTL_DSP_SETFMT, &format) < 0) printf("SNDCTL_DSP_SETFMT failed\n");
00170                         if(ioctl(dsp_in[i], SNDCTL_DSP_CHANNELS, &device->in_config->oss_in_channels[i]) < 0) printf("SNDCTL_DSP_CHANNELS failed\n");
00171                         if(ioctl(dsp_in[i], SNDCTL_DSP_SPEED, &device->in_samplerate) < 0) printf("SNDCTL_DSP_SPEED failed\n");
00172 
00173                         audio_buf_info recinfo;
00174                         ioctl(dsp_in[i], SNDCTL_DSP_GETISPACE, &recinfo);
00175 
00176 //printf("AudioOSS::open_input fragments=%d fragstotal=%d fragsize=%d bytes=%d\n", 
00177 //      recinfo.fragments, recinfo.fragstotal, recinfo.fragsize, recinfo.bytes);
00178 
00179                         thread[i] = new OSSThread(this);
00180                         thread[i]->start();
00181                 }
00182         }
00183         return 0;
00184 }
00185 
00186 int AudioOSS::open_output()
00187 {
00188 //printf("AudioOSS::open_output 1\n");
00189         device->out_channels = 0;
00190         
00191         for(int i = 0; i < MAXDEVICES; i++)
00192         {
00193                 if(device->out_config->oss_enable[i])
00194                         device->out_channels += device->out_config->oss_out_channels[i];
00195         }
00196         device->out_bits = device->out_config->oss_out_bits;
00197 // OSS only supports 8, 16, and 32
00198         if(device->out_bits == 24) device->out_bits = 32;
00199 
00200         for(int i = 0; i < MAXDEVICES; i++)
00201         {
00202                 if(device->out_config->oss_enable[i])
00203                 {
00204 // Linux 2.4.18 no longer supports allocating the maximum buffer size.
00205 // Need the shrink fragment size in preferences until it works.
00206                         dsp_out[i] = 
00207                                 open(device->out_config->oss_out_device[i], 
00208                                         O_WRONLY /*| O_NDELAY*/);
00209                         if(dsp_out[i] < 0) perror("AudioOSS::open_output");
00210 
00211                         int format = get_fmt(device->out_config->oss_out_bits);
00212                         int buffer_info = sizetofrag(device->out_samples, 
00213                                 device->out_config->oss_out_channels[i], 
00214                                 device->out_config->oss_out_bits);
00215                         audio_buf_info playinfo;
00216 
00217                         set_cloexec_flag(dsp_out[i], 1);
00218 
00219 // For the ice1712 the buffer must be maximum or no space will be allocated.
00220                         if(device->driver == AUDIO_OSS_ENVY24) buffer_info = 0x7fff000f;
00221                         if(ioctl(dsp_out[i], SNDCTL_DSP_SETFRAGMENT, &buffer_info)) printf("SNDCTL_DSP_SETFRAGMENT 2 failed.\n");
00222                         if(ioctl(dsp_out[i], SNDCTL_DSP_SETFMT, &format) < 0) printf("SNDCTL_DSP_SETFMT 2 failed\n");
00223                         if(ioctl(dsp_out[i], SNDCTL_DSP_CHANNELS, &device->out_config->oss_out_channels[i]) < 0) printf("SNDCTL_DSP_CHANNELS 2 failed\n");
00224                         if(ioctl(dsp_out[i], SNDCTL_DSP_SPEED, &device->out_samplerate) < 0) printf("SNDCTL_DSP_SPEED 2 failed\n");
00225                         ioctl(dsp_out[i], SNDCTL_DSP_GETOSPACE, &playinfo);
00226 // printf("AudioOSS::open_output fragments=%d fragstotal=%d fragsize=%d bytes=%d\n", 
00227 // playinfo.fragments, playinfo.fragstotal, playinfo.fragsize, playinfo.bytes);
00228                         device->device_buffer = playinfo.bytes;
00229                         thread[i] = new OSSThread(this);
00230                         thread[i]->start();
00231                 }
00232         }
00233         return 0;
00234 }
00235 
00236 int AudioOSS::open_duplex()
00237 {
00238         device->duplex_channels = 0;
00239         for(int i = 0; i < MAXDEVICES; i++)
00240         {
00241                 if(device->out_config->oss_enable[i])
00242                         device->duplex_channels += device->out_config->oss_out_channels[i];
00243         }
00244         device->duplex_bits = device->out_config->oss_out_bits;
00245         if(device->duplex_bits == 24) device->duplex_bits = 32;
00246 
00247         for(int i = 0; i < MAXDEVICES; i++)
00248         {
00249                 if(device->out_config->oss_enable[i])
00250                 {
00251                         dsp_duplex[i] = open(device->out_config->oss_out_device[i], O_RDWR/* | O_NDELAY*/);
00252                         if(dsp_duplex[i] < 0) perror("AudioOSS::open_duplex");
00253 
00254                         int format = get_fmt(device->out_config->oss_out_bits);
00255                         int buffer_info = sizetofrag(device->duplex_samples, 
00256                                 device->out_config->oss_out_channels[i], 
00257                                 device->out_config->oss_out_bits);
00258                         audio_buf_info playinfo;
00259 
00260                         set_cloexec_flag(dsp_duplex[i], 1);
00261 
00262 // For the ice1712 the buffer must be maximum or no space will be allocated.
00263                         if(device->driver == AUDIO_OSS_ENVY24) buffer_info = 0x7fff000f;
00264                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_SETFRAGMENT, &buffer_info)) printf("SNDCTL_DSP_SETFRAGMENT failed.\n");
00265                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_SETDUPLEX, 1) == -1) printf("SNDCTL_DSP_SETDUPLEX failed\n");
00266                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_SETFMT, &format) < 0) printf("SNDCTL_DSP_SETFMT failed\n");
00267                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_CHANNELS, &device->out_config->oss_out_channels[i]) < 0) printf("SNDCTL_DSP_CHANNELS failed\n");
00268                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_SPEED, &device->duplex_samplerate) < 0) printf("SNDCTL_DSP_SPEED failed\n");
00269                         ioctl(dsp_duplex[i], SNDCTL_DSP_GETOSPACE, &playinfo);
00270                         device->device_buffer = playinfo.bytes;
00271                         thread[i] = new OSSThread(this);
00272                         thread[i]->start();
00273                 }
00274         }
00275         return 0;
00276 }
00277 
00278 int AudioOSS::sizetofrag(int samples, int channels, int bits)
00279 {
00280         int testfrag = 2, fragsize = 1;
00281         samples *= channels * bits / 8;
00282         while(testfrag < samples)
00283         {
00284                 fragsize++;
00285                 testfrag *= 2;
00286         }
00287 //printf("AudioOSS::sizetofrag %d\n", fragsize);
00288         return (4 << 16) | fragsize;
00289 }
00290 
00291 int AudioOSS::get_fmt(int bits)
00292 {
00293         switch(bits)
00294         {
00295                 case 32: return AFMT_S32_LE; break;
00296                 case 16: return AFMT_S16_LE; break;
00297                 case 8:  return AFMT_S8;  break;
00298         }
00299         return AFMT_S16_LE;
00300 }
00301 
00302 
00303 int AudioOSS::close_all()
00304 {
00305 //printf("AudioOSS::close_all 1\n");
00306         for(int i = 0; i < MAXDEVICES; i++)
00307         {
00308                 if(dsp_in[i]) 
00309                 {
00310                         ioctl(dsp_in[i], SNDCTL_DSP_RESET, 0);         
00311                         close(dsp_in[i]);      
00312                 }
00313 
00314                 if(dsp_out[i]) 
00315                 {
00316 //printf("AudioOSS::close_all 2\n");
00317                         ioctl(dsp_out[i], SNDCTL_DSP_RESET, 0);        
00318                         close(dsp_out[i]);     
00319                 }
00320 
00321                 if(dsp_duplex[i]) 
00322                 {
00323                         ioctl(dsp_duplex[i], SNDCTL_DSP_RESET, 0);     
00324                         close(dsp_duplex[i]);  
00325                 }
00326                 
00327                 if(thread[i]) delete thread[i];
00328                 if(data[i]) delete [] data[i];
00329         }
00330         return 0;
00331 }
00332 
00333 int AudioOSS::set_cloexec_flag(int desc, int value)
00334 {
00335         int oldflags = fcntl (desc, F_GETFD, 0);
00336         if (oldflags < 0) return oldflags;
00337         if(value != 0) 
00338                 oldflags |= FD_CLOEXEC;
00339         else
00340                 oldflags &= ~FD_CLOEXEC;
00341         return fcntl(desc, F_SETFD, oldflags);
00342 }
00343 
00344 int64_t AudioOSS::device_position()
00345 {
00346         count_info info;
00347         if(!ioctl(get_output(0), SNDCTL_DSP_GETOPTR, &info))
00348         {
00349 //printf("AudioOSS::device_position %d %d %d\n", info.bytes, device->get_obits(), device->get_ochannels());
00350 // workaround for ALSA OSS emulation driver's bug
00351 // the problem is that if the first write to sound device was not full lenght fragment then 
00352 // _GETOPTR returns insanely large numbers at first moments of play
00353                 if (info.bytes > 2100000000) 
00354                         return 0;
00355                 else
00356                         return info.bytes / 
00357                                 (device->get_obits() / 8) / 
00358                                 device->get_ochannels();
00359         }
00360         return 0;
00361 }
00362 
00363 int AudioOSS::interrupt_playback()
00364 {
00365 //printf("AudioOSS::interrupt_playback 1\n");
00366         for(int i = 0; i < MAXDEVICES; i++)
00367         {
00368                 if(thread[i])
00369                 {
00370                         thread[i]->cancel();
00371                         thread[i]->write_lock->unlock();
00372                 }
00373         }
00374 //printf("AudioOSS::interrupt_playback 100\n");
00375         return 0;
00376 }
00377 
00378 int AudioOSS::read_buffer(char *buffer, int bytes)
00379 {
00380         int sample_size = device->get_ibits() / 8;
00381         int out_frame_size = device->get_ichannels() * sample_size;
00382         int samples = bytes / out_frame_size;
00383 
00384 //printf("AudioOSS::read_buffer 1 %d\n", bytes);
00385 // Fill temp buffers
00386         for(int i = 0; i < MAXDEVICES; i++)
00387         {
00388                 if(thread[i])
00389                 {
00390                         int in_frame_size = device->in_config->oss_in_channels[i] * sample_size;
00391 
00392                         if(data[i] && data_allocated[i] < bytes)
00393                         {
00394                                 delete [] data[i];
00395                                 data[i] = 0;
00396                         }
00397                         if(!data[i])
00398                         {
00399                                 data[i] = new unsigned char[bytes];
00400                                 data_allocated[i] = bytes;
00401                         }
00402 
00403                         thread[i]->read_data(get_input(i), data[i], samples * in_frame_size);
00404                 }
00405         }
00406 
00407 //printf("AudioOSS::read_buffer 1 %d\n", device->get_ibits());
00408         for(int i = 0, out_channel = 0; i < MAXDEVICES; i++)
00409         {
00410                 if(thread[i])
00411                 {
00412                         thread[i]->wait_read();
00413 
00414                         for(int in_channel = 0; 
00415                                 in_channel < device->in_config->oss_in_channels[i]; 
00416                                 in_channel++)
00417                         {
00418                                 int in_frame_size = device->in_config->oss_in_channels[i] * sample_size;
00419 
00420                                 for(int k = 0; k < samples; k++)
00421                                 {
00422                                         for(int l = 0; 
00423                                                 l < sample_size;
00424                                                 l++)
00425                                         {
00426                                                 buffer[out_channel * sample_size + k * out_frame_size + l] = 
00427                                                         data[i][in_channel * sample_size + k * in_frame_size + l];
00428                                         }
00429                                 }
00430                                 out_channel++;
00431                         }
00432                 }
00433         }
00434 //printf("AudioOSS::read_buffer 2\n");
00435         return 0;
00436 }
00437 
00438 int AudioOSS::write_buffer(char *buffer, int bytes)
00439 {
00440         int sample_size = device->get_obits() / 8;
00441         int in_frame_size = device->get_ochannels() * sample_size;
00442         int samples = bytes / in_frame_size;
00443 
00444         for(int i = 0, in_channel = 0; i < MAXDEVICES; i++)
00445         {
00446                 if(thread[i])
00447                 {
00448                         int out_frame_size = device->out_config->oss_out_channels[i] * sample_size;
00449                         if(data[i] && data_allocated[i] < bytes)
00450                         {
00451                                 delete [] data[i];
00452                                 data[i] = 0;
00453                         }
00454                         if(!data[i])
00455                         {
00456                                 data[i] = new unsigned char[bytes];
00457                                 data_allocated[i] = bytes;
00458                         }
00459                         
00460                         for(int out_channel = 0;
00461                                 out_channel < device->out_config->oss_out_channels[i];
00462                                 out_channel++)
00463                         {
00464                                 
00465                                 for(int k = 0; k < samples; k++)
00466                                 {
00467                                         for(int l = 0; l < sample_size; l++)
00468                                         {
00469                                                 data[i][out_channel * sample_size + k * out_frame_size + l] = 
00470                                                         buffer[in_channel * sample_size + k * in_frame_size + l];
00471                                         }
00472                                 }
00473                                 in_channel++;
00474                         }
00475                         
00476                         thread[i]->write_data(get_output(i), data[i], samples * out_frame_size);
00477                 }
00478         }
00479         for(int i = 0, in_channel = 0; i < MAXDEVICES; i++)
00480         {
00481                 if(thread[i])
00482                 {
00483                         thread[i]->wait_write();
00484                 }
00485         }
00486         return 0;
00487 }
00488 
00489 int AudioOSS::flush_device()
00490 {
00491         for(int i = 0; i < MAXDEVICES; i++)
00492                 if(thread[i]) ioctl(get_output(i), SNDCTL_DSP_SYNC, 0);
00493         return 0;
00494 }
00495 
00496 int AudioOSS::get_output(int number)
00497 {
00498         if(device->w) return dsp_out[number];
00499         else if(device->d) return dsp_duplex[number];
00500         return 0;
00501 }
00502 
00503 int AudioOSS::get_input(int number)
00504 {
00505         if(device->r) return dsp_in[number];
00506         else if(device->d) return dsp_duplex[number];
00507         return 0;
00508 }
00509 
00510 #endif // HAVE_OSS

Generated on Sun Jan 8 13:38:50 2006 for Cinelerra-svn by  doxygen 1.4.4