r125: This commit was manufactured by cvs2svn to create tag 'r1_1_7-last'.
[cinelerra_cv/mob.git] / hvirtual / cinelerra / audiooss.C
blob0213fa098a51d70b0d50ea484b18ade006fdc1ea
1 #include "audioconfig.h"
2 #include "audiodevice.h"
3 #include "audiooss.h"
4 #include "clip.h"
5 #include "errno.h"
6 #include "playbackconfig.h"
7 #include "preferences.h"
8 #include "recordconfig.h"
10 #include <string.h>
12 #ifdef HAVE_OSS
14 // These are only available in commercial OSS
16 #ifndef AFMT_S32_LE
17 #define AFMT_S32_LE      0x00001000
18 #define AFMT_S32_BE      0x00002000
19 #endif
22 // Synchronie multiple devices by using threads
24 OSSThread::OSSThread(AudioOSS *device)
25  : Thread(1, 0, 0)
27         rd = 0;
28         wr = 0;
29         done = 0;
30         this->device = device;
31         input_lock.lock();
34 OSSThread::~OSSThread()
36         done = 1;
37         input_lock.unlock();
38         Thread::join();
41 void OSSThread::run()
43         while(!done)
44         {
45                 input_lock.lock();
46                 if(rd)
47                 {
48                         int result = read(fd, data, bytes);
49                         read_lock.unlock();
50                 }
51                 else
52                 if(wr)
53                 {
54 //fwrite(data, bytes, 1, stdout);
55 //printf("OSSThread::run %d %d\n", bytes, device->device->get_device_buffer());
56                         if(done) return;
57 //printf("OSSThread::run 1\n");
58                         Thread::enable_cancel();
59                         write(fd, data, bytes);
60                         Thread::disable_cancel();
61 //printf("OSSThread::run 10\n");
62                         if(done) return;
63                         write_lock.unlock();
64                 }
65                 output_lock.unlock();
66         }
69 void OSSThread::write_data(int fd, unsigned char *data, int bytes)
71         output_lock.lock();
72         wr = 1;
73         rd = 0;
74         this->data = data;
75         this->bytes = bytes;
76         this->fd = fd;
77         write_lock.lock();
78         input_lock.unlock();
81 void OSSThread::read_data(int fd, unsigned char *data, int bytes)
83         output_lock.lock();
84         wr = 0;
85         rd = 1;
86         this->data = data;
87         this->bytes = bytes;
88         this->fd = fd;
89         read_lock.lock();
90         input_lock.unlock();
93 void OSSThread::wait_read()
95         read_lock.lock();
96         read_lock.unlock();
99 void OSSThread::wait_write()
101         write_lock.lock();
102         write_lock.unlock();
115 AudioOSS::AudioOSS(AudioDevice *device)
116  : AudioLowLevel(device)
118         for(int i = 0; i < MAXDEVICES; i++)
119         {
120                 dsp_in[i] = dsp_out[i] = dsp_duplex[i] = 0;
121                 thread[i] = 0;
122                 data[i] = 0;
123                 data_allocated[i] = 0;
124         }
127 AudioOSS::~AudioOSS()
131 int AudioOSS::open_input()
133         device->in_channels = 0;
134         for(int i = 0; i < MAXDEVICES; i++)
135         {
136                 if(device->in_config->oss_enable[i])
137                         device->in_channels += device->in_config->oss_in_channels[i];
138         }
139         device->in_bits = device->in_config->oss_in_bits;
140 // 24 bits not available in OSS
141         if(device->in_bits == 24) device->in_bits = 32;
143         for(int i = 0; i < MAXDEVICES; i++)
144         {
145                 if(device->in_config->oss_enable[i])
146                 {
147                         dsp_in[i] = open(device->in_config->oss_in_device[i], O_RDONLY/* | O_NDELAY*/);
148                         if(dsp_in[i] < 0) fprintf(stderr, "AudioOSS::open_input %s: %s\n", 
149                                 device->in_config->oss_in_device[i], 
150                                 strerror(errno));
152                         int format = get_fmt(device->in_config->oss_in_bits);
153                         int buffer_info = sizetofrag(device->in_samples, 
154                                 device->in_config->oss_in_channels[i], 
155                                 device->in_config->oss_in_bits);
157                         set_cloexec_flag(dsp_in[i], 1);
159 //printf("AudioOSS::open_input %d %d %d\n", device->in_samples, device->in_config->oss_in_channels[i], device->in_config->oss_in_bits);
160 // For the ice1712 the buffer must be maximum or no space will be allocated.
161                         if(device->driver == AUDIO_OSS_ENVY24) buffer_info = 0x7fff000f;
162                         if(ioctl(dsp_in[i], SNDCTL_DSP_SETFRAGMENT, &buffer_info)) printf("SNDCTL_DSP_SETFRAGMENT failed.\n");
163                         if(ioctl(dsp_in[i], SNDCTL_DSP_SETFMT, &format) < 0) printf("SNDCTL_DSP_SETFMT failed\n");
164                         if(ioctl(dsp_in[i], SNDCTL_DSP_CHANNELS, &device->in_config->oss_in_channels[i]) < 0) printf("SNDCTL_DSP_CHANNELS failed\n");
165                         if(ioctl(dsp_in[i], SNDCTL_DSP_SPEED, &device->in_samplerate) < 0) printf("SNDCTL_DSP_SPEED failed\n");
167                         audio_buf_info recinfo;
168                         ioctl(dsp_in[i], SNDCTL_DSP_GETISPACE, &recinfo);
170 //printf("AudioOSS::open_input fragments=%d fragstotal=%d fragsize=%d bytes=%d\n", 
171 //      recinfo.fragments, recinfo.fragstotal, recinfo.fragsize, recinfo.bytes);
173                         thread[i] = new OSSThread(this);
174                         thread[i]->start();
175                 }
176         }
177         return 0;
180 int AudioOSS::open_output()
182 //printf("AudioOSS::open_output 1\n");
183         device->out_channels = 0;
184         
185         for(int i = 0; i < MAXDEVICES; i++)
186         {
187                 if(device->out_config->oss_enable[i])
188                         device->out_channels += device->out_config->oss_out_channels[i];
189         }
190         device->out_bits = device->out_config->oss_out_bits;
191 // OSS only supports 8, 16, and 32
192         if(device->out_bits == 24) device->out_bits = 32;
194         for(int i = 0; i < MAXDEVICES; i++)
195         {
196                 if(device->out_config->oss_enable[i])
197                 {
198 // Linux 2.4.18 no longer supports allocating the maximum buffer size.
199 // Need the shrink fragment size in preferences until it works.
200                         dsp_out[i] = 
201                                 open(device->out_config->oss_out_device[i], 
202                                         O_WRONLY /*| O_NDELAY*/);
203                         if(dsp_out[i] < 0) perror("AudioOSS::open_output");
205                         int format = get_fmt(device->out_config->oss_out_bits);
206                         int buffer_info = sizetofrag(device->out_samples, 
207                                 device->out_config->oss_out_channels[i], 
208                                 device->out_config->oss_out_bits);
209                         audio_buf_info playinfo;
211                         set_cloexec_flag(dsp_out[i], 1);
213 // For the ice1712 the buffer must be maximum or no space will be allocated.
214                         if(device->driver == AUDIO_OSS_ENVY24) buffer_info = 0x7fff000f;
215                         if(ioctl(dsp_out[i], SNDCTL_DSP_SETFRAGMENT, &buffer_info)) printf("SNDCTL_DSP_SETFRAGMENT 2 failed.\n");
216                         if(ioctl(dsp_out[i], SNDCTL_DSP_SETFMT, &format) < 0) printf("SNDCTL_DSP_SETFMT 2 failed\n");
217                         if(ioctl(dsp_out[i], SNDCTL_DSP_CHANNELS, &device->out_config->oss_out_channels[i]) < 0) printf("SNDCTL_DSP_CHANNELS 2 failed\n");
218                         if(ioctl(dsp_out[i], SNDCTL_DSP_SPEED, &device->out_samplerate) < 0) printf("SNDCTL_DSP_SPEED 2 failed\n");
219                         ioctl(dsp_out[i], SNDCTL_DSP_GETOSPACE, &playinfo);
220 // printf("AudioOSS::open_output fragments=%d fragstotal=%d fragsize=%d bytes=%d\n", 
221 // playinfo.fragments, playinfo.fragstotal, playinfo.fragsize, playinfo.bytes);
222                         device->device_buffer = playinfo.bytes;
223                         thread[i] = new OSSThread(this);
224                         thread[i]->start();
225                 }
226         }
227         return 0;
230 int AudioOSS::open_duplex()
232         device->duplex_channels = 0;
233         for(int i = 0; i < MAXDEVICES; i++)
234         {
235                 if(device->out_config->oss_enable[i])
236                         device->duplex_channels += device->out_config->oss_out_channels[i];
237         }
238         device->duplex_bits = device->out_config->oss_out_bits;
239         if(device->duplex_bits == 24) device->duplex_bits = 32;
241         for(int i = 0; i < MAXDEVICES; i++)
242         {
243                 if(device->out_config->oss_enable[i])
244                 {
245                         dsp_duplex[i] = open(device->out_config->oss_out_device[i], O_RDWR/* | O_NDELAY*/);
246                         if(dsp_duplex[i] < 0) perror("AudioOSS::open_duplex");
248                         int format = get_fmt(device->out_config->oss_out_bits);
249                         int buffer_info = sizetofrag(device->duplex_samples, 
250                                 device->out_config->oss_out_channels[i], 
251                                 device->out_config->oss_out_bits);
252                         audio_buf_info playinfo;
254                         set_cloexec_flag(dsp_duplex[i], 1);
256 // For the ice1712 the buffer must be maximum or no space will be allocated.
257                         if(device->driver == AUDIO_OSS_ENVY24) buffer_info = 0x7fff000f;
258                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_SETFRAGMENT, &buffer_info)) printf("SNDCTL_DSP_SETFRAGMENT failed.\n");
259                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_SETDUPLEX, 1) == -1) printf("SNDCTL_DSP_SETDUPLEX failed\n");
260                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_SETFMT, &format) < 0) printf("SNDCTL_DSP_SETFMT failed\n");
261                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_CHANNELS, &device->out_config->oss_out_channels[i]) < 0) printf("SNDCTL_DSP_CHANNELS failed\n");
262                         if(ioctl(dsp_duplex[i], SNDCTL_DSP_SPEED, &device->duplex_samplerate) < 0) printf("SNDCTL_DSP_SPEED failed\n");
263                         ioctl(dsp_duplex[i], SNDCTL_DSP_GETOSPACE, &playinfo);
264                         device->device_buffer = playinfo.bytes;
265                         thread[i] = new OSSThread(this);
266                         thread[i]->start();
267                 }
268         }
269         return 0;
272 int AudioOSS::sizetofrag(int samples, int channels, int bits)
274         int testfrag = 2, fragsize = 1;
275         samples *= channels * bits / 8;
276         while(testfrag < samples)
277         {
278                 fragsize++;
279                 testfrag *= 2;
280         }
281 //printf("AudioOSS::sizetofrag %d\n", fragsize);
282         return (4 << 16) | fragsize;
285 int AudioOSS::get_fmt(int bits)
287         switch(bits)
288         {
289                 case 32: return AFMT_S32_LE; break;
290                 case 16: return AFMT_S16_LE; break;
291                 case 8:  return AFMT_S8;  break;
292         }
293         return AFMT_S16_LE;
297 int AudioOSS::close_all()
299 //printf("AudioOSS::close_all 1\n");
300         for(int i = 0; i < MAXDEVICES; i++)
301         {
302                 if(dsp_in[i]) 
303                 {
304                         ioctl(dsp_in[i], SNDCTL_DSP_RESET, 0);         
305                         close(dsp_in[i]);      
306                 }
308                 if(dsp_out[i]) 
309                 {
310 //printf("AudioOSS::close_all 2\n");
311                         ioctl(dsp_out[i], SNDCTL_DSP_RESET, 0);        
312                         close(dsp_out[i]);     
313                 }
315                 if(dsp_duplex[i]) 
316                 {
317                         ioctl(dsp_duplex[i], SNDCTL_DSP_RESET, 0);     
318                         close(dsp_duplex[i]);  
319                 }
320                 
321                 if(thread[i]) delete thread[i];
322                 if(data[i]) delete data[i];
323         }
324         return 0;
327 int AudioOSS::set_cloexec_flag(int desc, int value)
329         int oldflags = fcntl (desc, F_GETFD, 0);
330         if (oldflags < 0) return oldflags;
331         if(value != 0) 
332                 oldflags |= FD_CLOEXEC;
333         else
334                 oldflags &= ~FD_CLOEXEC;
335         return fcntl(desc, F_SETFD, oldflags);
338 int64_t AudioOSS::device_position()
340         count_info info;
341         if(!ioctl(get_output(0), SNDCTL_DSP_GETOPTR, &info))
342         {
343 //printf("AudioOSS::device_position %d %d %d\n", info.bytes, device->get_obits(), device->get_ochannels());
344                 return info.bytes / 
345                         (device->get_obits() / 8) / 
346                         device->get_ochannels();
347         }
348         return 0;
351 int AudioOSS::interrupt_playback()
353 //printf("AudioOSS::interrupt_playback 1\n");
354         for(int i = 0; i < MAXDEVICES; i++)
355         {
356                 if(thread[i])
357                 {
358                         thread[i]->cancel();
359                         thread[i]->write_lock.unlock();
360                 }
361         }
362 //printf("AudioOSS::interrupt_playback 100\n");
363         return 0;
366 int AudioOSS::read_buffer(char *buffer, int bytes)
368         int sample_size = device->get_ibits() / 8;
369         int out_frame_size = device->get_ichannels() * sample_size;
370         int samples = bytes / out_frame_size;
372 //printf("AudioOSS::read_buffer 1 %d\n", bytes);
373 // Fill temp buffers
374         for(int i = 0; i < MAXDEVICES; i++)
375         {
376                 if(thread[i])
377                 {
378                         int in_frame_size = device->in_config->oss_in_channels[i] * sample_size;
380                         if(data[i] && data_allocated[i] < bytes)
381                         {
382                                 delete data[i];
383                                 data[i] = 0;
384                         }
385                         if(!data[i])
386                         {
387                                 data[i] = new unsigned char[bytes];
388                                 data_allocated[i] = bytes;
389                         }
391                         thread[i]->read_data(get_input(i), data[i], samples * in_frame_size);
392                 }
393         }
395 //printf("AudioOSS::read_buffer 1 %d\n", device->get_ibits());
396         for(int i = 0, out_channel = 0; i < MAXDEVICES; i++)
397         {
398                 if(thread[i])
399                 {
400                         thread[i]->wait_read();
402                         for(int in_channel = 0; 
403                                 in_channel < device->in_config->oss_in_channels[i]; 
404                                 in_channel++)
405                         {
406                                 int in_frame_size = device->in_config->oss_in_channels[i] * sample_size;
408                                 for(int k = 0; k < samples; k++)
409                                 {
410                                         for(int l = 0; 
411                                                 l < sample_size;
412                                                 l++)
413                                         {
414                                                 buffer[out_channel * sample_size + k * out_frame_size + l] = 
415                                                         data[i][in_channel * sample_size + k * in_frame_size + l];
416                                         }
417                                 }
418                                 out_channel++;
419                         }
420                 }
421         }
422 //printf("AudioOSS::read_buffer 2\n");
423         return 0;
426 int AudioOSS::write_buffer(char *buffer, int bytes)
428         int sample_size = device->get_obits() / 8;
429         int in_frame_size = device->get_ochannels() * sample_size;
430         int samples = bytes / in_frame_size;
432         for(int i = 0, in_channel = 0; i < MAXDEVICES; i++)
433         {
434                 if(thread[i])
435                 {
436                         int out_frame_size = device->out_config->oss_out_channels[i] * sample_size;
437                         if(data[i] && data_allocated[i] < bytes)
438                         {
439                                 delete data[i];
440                                 data[i] = 0;
441                         }
442                         if(!data[i])
443                         {
444                                 data[i] = new unsigned char[bytes];
445                                 data_allocated[i] = bytes;
446                         }
447                         
448                         for(int out_channel = 0;
449                                 out_channel < device->out_config->oss_out_channels[i];
450                                 out_channel++)
451                         {
452                                 
453                                 for(int k = 0; k < samples; k++)
454                                 {
455                                         for(int l = 0; l < sample_size; l++)
456                                         {
457                                                 data[i][out_channel * sample_size + k * out_frame_size + l] = 
458                                                         buffer[in_channel * sample_size + k * in_frame_size + l];
459                                         }
460                                 }
461                                 in_channel++;
462                         }
463                         
464                         thread[i]->write_data(get_output(i), data[i], samples * out_frame_size);
465                 }
466         }
467         for(int i = 0, in_channel = 0; i < MAXDEVICES; i++)
468         {
469                 if(thread[i])
470                 {
471                         thread[i]->wait_write();
472                 }
473         }
474         return 0;
477 int AudioOSS::flush_device(int number)
479 printf("AudioOSS::flush_device 1 %d\n", number);
480         ioctl(get_output(number), SNDCTL_DSP_SYNC, 0);
481         return 0;
484 int AudioOSS::get_output(int number)
486         if(device->w) return dsp_out[number];
487         else if(device->d) return dsp_duplex[number];
488         return 0;
491 int AudioOSS::get_input(int number)
493         if(device->r) return dsp_in[number];
494         else if(device->d) return dsp_duplex[number];
495         return 0;
498 #endif // HAVE_OSS