prevent popup deactivation while button_down
[goodguy/history.git] / cinelerra-5.0 / cinelerra / audiooss.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22 #include "audioconfig.h"
23 #include "audiodevice.h"
24 #include "audiooss.h"
25 #include "clip.h"
26 #include "condition.h"
27 #include "errno.h"
28 #include "playbackconfig.h"
29 #include "preferences.h"
30 #include "recordconfig.h"
31
32 #include <string.h>
33
34 #ifdef HAVE_OSS
35
36 // These are only available in commercial OSS
37
38 #ifndef AFMT_S32_LE
39 #define AFMT_S32_LE      0x00001000
40 #define AFMT_S32_BE      0x00002000
41 #endif
42
43
44 // Synchronie multiple devices by using threads
45
46 OSSThread::OSSThread(AudioOSS *device)
47  : Thread(1, 0, 0)
48 {
49         rd = 0;
50         wr = 0;
51         done = 0;
52         this->device = device;
53         input_lock = new Condition(0, "OSSThread::input_lock");
54         output_lock = new Condition(1, "OSSThread::output_lock");
55         read_lock = new Condition(0, "OSSThread::read_lock");
56         write_lock = new Condition(0, "OSSThread::write_lock");
57 }
58
59 OSSThread::~OSSThread()
60 {
61         done = 1;
62         input_lock->unlock();
63         Thread::join();
64         delete input_lock;
65         delete output_lock;
66         delete read_lock;
67         delete write_lock;
68 }
69
70 void OSSThread::run()
71 {
72         while(!done) {
73                 input_lock->lock("OSSThread::run 1");
74                 if(done) return;
75                 if(rd) {
76                         read(fd, data, bytes);
77                         read_lock->unlock();
78                 }
79                 else if(wr) {
80                         int count = bytes;
81                         unsigned char *bp = data;
82                         while( count > 0 ) {
83                                 Thread::enable_cancel();
84                                 int ret = write(fd, bp, bytes);
85                                 Thread::disable_cancel();
86                                 if( ret < 0 ) break;
87                                 bp += ret;
88                                 count -= ret;
89                         }
90                         write_lock->unlock();
91                 }
92                 output_lock->unlock();
93         }
94 }
95
96 void OSSThread::write_data(int fd, unsigned char *data, int bytes)
97 {
98         output_lock->lock("OSSThread::write_data");
99         wr = 1;
100         rd = 0;
101         this->data = data;
102         this->bytes = bytes;
103         this->fd = fd;
104         input_lock->unlock();
105 }
106
107 void OSSThread::read_data(int fd, unsigned char *data, int bytes)
108 {
109         output_lock->lock("OSSThread::read_data");
110         wr = 0;
111         rd = 1;
112         this->data = data;
113         this->bytes = bytes;
114         this->fd = fd;
115         input_lock->unlock();
116 }
117
118 void OSSThread::wait_read()
119 {
120         read_lock->lock("OSSThread::wait_read");
121 }
122
123 void OSSThread::wait_write()
124 {
125         write_lock->lock("OSSThread::wait_write");
126 }
127
128
129
130
131
132
133
134
135
136
137
138 AudioOSS::AudioOSS(AudioDevice *device)
139  : AudioLowLevel(device)
140 {
141         for(int i = 0; i < MAXDEVICES; i++) {
142                 dsp_in[i] = dsp_out[i] = -1;
143                 thread[i] = 0;
144                 data[i] = 0;
145                 data_allocated[i] = 0;
146         }
147 }
148
149 AudioOSS::~AudioOSS()
150 {
151 }
152
153 int AudioOSS::open_input()
154 {
155         device->in_bits = device->in_config->oss_in_bits;
156 // 24 bits not available in OSS
157         if(device->in_bits == 24) device->in_bits = 32;
158
159         for(int i = 0; i < MAXDEVICES; i++) {
160                 if(device->in_config->oss_enable[i]) {
161 //printf("AudioOSS::open_input 10\n");
162                         dsp_in[i] = open(device->in_config->oss_in_device[i], O_RDONLY/* | O_NDELAY*/);
163 //printf("AudioOSS::open_input 20\n");
164                         if(dsp_in[i] < 0) fprintf(stderr, "AudioOSS::open_input %s: %s\n", 
165                                 device->in_config->oss_in_device[i], 
166                                 strerror(errno));
167
168                         int format = get_fmt(device->in_config->oss_in_bits);
169                         int buffer_info = sizetofrag(device->in_samples, 
170                                 device->get_ichannels(), 
171                                 device->in_config->oss_in_bits);
172
173                         set_cloexec_flag(dsp_in[i], 1);
174
175 // For the ice1712 the buffer must be maximum or no space will be allocated.
176                         if(device->idriver == AUDIO_OSS_ENVY24) buffer_info = 0x7fff000f;
177                         if(ioctl(dsp_in[i], SNDCTL_DSP_SETFRAGMENT, &buffer_info)) printf("SNDCTL_DSP_SETFRAGMENT failed.\n");
178                         if(ioctl(dsp_in[i], SNDCTL_DSP_SETFMT, &format) < 0) printf("SNDCTL_DSP_SETFMT failed\n");
179                         int channels = device->get_ichannels();
180                         if(ioctl(dsp_in[i], SNDCTL_DSP_CHANNELS, &channels) < 0) printf("SNDCTL_DSP_CHANNELS failed\n");
181                         if(ioctl(dsp_in[i], SNDCTL_DSP_SPEED, &device->in_samplerate) < 0) printf("SNDCTL_DSP_SPEED failed\n");
182
183                         audio_buf_info recinfo;
184                         ioctl(dsp_in[i], SNDCTL_DSP_GETISPACE, &recinfo);
185
186 //printf("AudioOSS::open_input fragments=%d fragstotal=%d fragsize=%d bytes=%d\n", 
187 //      recinfo.fragments, recinfo.fragstotal, recinfo.fragsize, recinfo.bytes);
188
189                         thread[i] = new OSSThread(this);
190                         thread[i]->start();
191                 }
192         }
193         return 0;
194 }
195
196 int AudioOSS::open_output()
197 {
198         device->out_bits = device->out_config->oss_out_bits;
199 // OSS only supports 8, 16, and 32
200         if(device->out_bits == 24) device->out_bits = 32;
201
202         for(int i = 0; i < MAXDEVICES; i++) {
203                 if(device->out_config->oss_enable[i]) {
204 // Linux 2.4.18 no longer supports allocating the maximum buffer size.
205 // Need the shrink fragment size in preferences until it works.
206                         dsp_out[i] = 
207                                 open(device->out_config->oss_out_device[i], 
208                                         O_WRONLY /*| O_NDELAY*/);
209                         if(dsp_out[i] < 0) perror("AudioOSS::open_output");
210
211                         int format = get_fmt(device->out_config->oss_out_bits);
212                         int buffer_info = sizetofrag(device->out_samples, 
213                                 device->get_ochannels(), 
214                                 device->out_config->oss_out_bits);
215                         audio_buf_info playinfo;
216
217                         set_cloexec_flag(dsp_out[i], 1);
218
219 // For the ice1712 the buffer must be maximum or no space will be allocated.
220                         if(device->odriver == AUDIO_OSS_ENVY24) buffer_info = 0x7fff000f;
221                         if(ioctl(dsp_out[i], SNDCTL_DSP_SETFRAGMENT, &buffer_info)) printf("SNDCTL_DSP_SETFRAGMENT 2 failed.\n");
222                         if(ioctl(dsp_out[i], SNDCTL_DSP_SETFMT, &format) < 0) printf("SNDCTL_DSP_SETFMT 2 failed\n");
223                         int channels = device->get_ochannels();
224                         if(ioctl(dsp_out[i], SNDCTL_DSP_CHANNELS, &channels) < 0) printf("SNDCTL_DSP_CHANNELS 2 failed\n");
225                         if(ioctl(dsp_out[i], SNDCTL_DSP_SPEED, &device->out_samplerate) < 0) printf("SNDCTL_DSP_SPEED 2 failed\n");
226                         ioctl(dsp_out[i], SNDCTL_DSP_GETOSPACE, &playinfo);
227 // printf("AudioOSS::open_output fragments=%d fragstotal=%d fragsize=%d bytes=%d\n", 
228 // playinfo.fragments, playinfo.fragstotal, playinfo.fragsize, playinfo.bytes);
229                         device->device_buffer = playinfo.bytes;
230                         thread[i] = new OSSThread(this);
231                         thread[i]->start();
232                 }
233         }
234         return 0;
235 }
236
237 int AudioOSS::sizetofrag(int samples, int channels, int bits)
238 {
239         int testfrag = 2, fragsize = 1;
240         samples *= channels * bits / 8;
241         while(testfrag < samples) {
242                 fragsize++;
243                 testfrag *= 2;
244         }
245 //printf("AudioOSS::sizetofrag %d\n", fragsize);
246         return (4 << 16) | fragsize;
247 }
248
249 int AudioOSS::get_fmt(int bits)
250 {
251         switch(bits) {
252         case 32: return AFMT_S32_LE; break;
253         case 16: return AFMT_S16_LE; break;
254         case 8:  return AFMT_S8;  break;
255         }
256         return AFMT_S16_LE;
257 }
258
259
260 int AudioOSS::close_all()
261 {
262 //printf("AudioOSS::close_all 1\n");
263         for(int i = 0; i < MAXDEVICES; i++) {
264                 if(dsp_in[i] >= 0) {
265                         ioctl(dsp_in[i], SNDCTL_DSP_RESET, 0);         
266                         close(dsp_in[i]);
267                         dsp_in[i] = -1;
268                 }
269
270                 if(dsp_out[i] >= 0) {
271 //printf("AudioOSS::close_all 2\n");
272                         ioctl(dsp_out[i], SNDCTL_DSP_RESET, 0);        
273                         close(dsp_out[i]);     
274                         dsp_out[i] = -1;
275                 }
276
277                 if(thread[i]) delete thread[i];
278                 if(data[i]) delete [] data[i];
279         }
280         return 0;
281 }
282
283 int AudioOSS::set_cloexec_flag(int desc, int value)
284 {
285         int oldflags = fcntl (desc, F_GETFD, 0);
286         if (oldflags < 0) return oldflags;
287         if(value != 0) 
288                 oldflags |= FD_CLOEXEC;
289         else
290                 oldflags &= ~FD_CLOEXEC;
291         return fcntl(desc, F_SETFD, oldflags);
292 }
293
294 int64_t AudioOSS::device_position()
295 {
296         count_info info;
297         if(!ioctl(get_output(0), SNDCTL_DSP_GETOPTR, &info))
298         {
299 //printf("AudioOSS::device_position %d %d %d\n", info.bytes, device->get_obits(), device->get_ochannels());
300 // workaround for ALSA OSS emulation driver's bug
301 // the problem is that if the first write to sound device was not full lenght fragment then 
302 // _GETOPTR returns insanely large numbers at first moments of play
303                 if (info.bytes > 2100000000) return 0;
304                 int frame = device->get_ochannels() * device->get_obits()/8;
305                 return info.bytes / frame;
306         }
307         return 0;
308 }
309
310 int AudioOSS::interrupt_playback()
311 {
312 //printf("AudioOSS::interrupt_playback 1\n");
313         for(int i = 0; i < MAXDEVICES; i++) {
314                 if(thread[i]) {
315                         thread[i]->done = 1;
316                         thread[i]->input_lock->unlock();
317                         thread[i]->write_lock->unlock();
318                         thread[i]->cancel();
319                 }
320         }
321 //printf("AudioOSS::interrupt_playback 100\n");
322         return 0;
323 }
324
325 int AudioOSS::read_buffer(char *buffer, int bytes)
326 {
327         int sample_size = device->get_ibits() / 8;
328         int out_frame_size = device->get_ichannels() * sample_size;
329         int samples = bytes / out_frame_size;
330
331 //printf("AudioOSS::read_buffer 1 %d\n", bytes);
332 // Fill temp buffers
333         for(int i = 0; i < MAXDEVICES; i++) {
334                 if(thread[i]) {
335                         int in_frame_size = device->get_ichannels() * sample_size;
336
337                         if(data[i] && data_allocated[i] < bytes) {
338                                 delete [] data[i];
339                                 data[i] = 0;
340                         }
341                         if(!data[i]) {
342                                 data[i] = new unsigned char[bytes];
343                                 data_allocated[i] = bytes;
344                         }
345
346                         thread[i]->read_data(get_input(i), data[i], samples * in_frame_size);
347                 }
348         }
349
350 //printf("AudioOSS::read_buffer 1 %d\n", device->get_ibits());
351         for(int i = 0, out_channel = 0; i < MAXDEVICES; i++) {
352                 if(thread[i]) {
353                         thread[i]->wait_read();
354
355                         for(int in_channel = 0; 
356                                 in_channel < device->get_ichannels(); 
357                                 in_channel++) {
358                                 int in_frame_size = device->get_ichannels() * sample_size;
359
360                                 for(int k = 0; k < samples; k++) {
361                                         for(int l = 0; 
362                                                 l < sample_size;
363                                                 l++) {
364                                                 buffer[out_channel * sample_size + k * out_frame_size + l] = 
365                                                         data[i][in_channel * sample_size + k * in_frame_size + l];
366                                         }
367                                 }
368                                 out_channel++;
369                         }
370                 }
371         }
372 //printf("AudioOSS::read_buffer 2\n");
373         return 0;
374 }
375
376 int AudioOSS::write_buffer(char *buffer, int bytes)
377 {
378         int sample_size = device->get_obits() / 8;
379         int in_frame_size = device->get_ochannels() * sample_size;
380         int samples = bytes / in_frame_size;
381
382         for(int i = 0, in_channel = 0; i < MAXDEVICES; i++) {
383                 if( thread[i] ) {
384                         int out_frame_size = device->get_ochannels() * sample_size;
385                         if(data[i] && data_allocated[i] < bytes) {
386                                 delete [] data[i];
387                                 data[i] = 0;
388                         }
389                         if(!data[i]) {
390                                 data[i] = new unsigned char[bytes];
391                                 data_allocated[i] = bytes;
392                         }
393                         
394                         for(int out_channel = 0;
395                                 out_channel < device->get_ochannels();
396                                 out_channel++) {
397                                 
398                                 for(int k = 0; k < samples; k++) {
399                                         for(int l = 0; l < sample_size; l++) {
400                                                 data[i][out_channel * sample_size + k * out_frame_size + l] = 
401                                                         buffer[in_channel * sample_size + k * in_frame_size + l];
402                                         }
403                                 }
404                                 in_channel++;
405                         }
406                         
407                         thread[i]->write_data(get_output(i), data[i], samples * out_frame_size);
408                 }
409         }
410         for(int i = 0; i < MAXDEVICES; i++) {
411                 if( thread[i] ) thread[i]->wait_write();
412         }
413         return 0;
414 }
415
416 int AudioOSS::flush_device()
417 {
418         for(int i = 0; i < MAXDEVICES; i++)
419                 if(thread[i]) ioctl(get_output(i), SNDCTL_DSP_SYNC, 0);
420         return 0;
421 }
422
423 int AudioOSS::get_output(int number)
424 {
425         return dsp_out[number];
426 }
427
428 int AudioOSS::get_input(int number)
429 {
430         return dsp_in[number];
431 }
432
433 #endif // HAVE_OSS