1 /*
2 * Copyright (C) 2002 2003 2004 2005 2006 2008 2010, Magnus Hjorth
3 *
4 * This file is part of mhWaveEdit.
5 *
6 * mhWaveEdit 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 * mhWaveEdit 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 mhWaveEdit; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21
22 #if defined(HAVE_SYS_SOUNDCARD_H)
23 #include <sys/soundcard.h>
24 #elif defined(HAVE_SOUNDCARD_H)
25 #include <soundcard.h>
26 #else
27 #warning "Header file not found for OSS driver!"
28 #endif
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <sys/types.h>
39
40 #include <gtk/gtk.h>
41
42 #include "ringbuf.h"
43 #include "um.h"
44 #include "main.h"
45 #include "gettext.h"
46 #include "mainloop.h"
47
48 #define OSS_PCMFILE_PLAYBACK "OSSdevice"
49 #define OSS_PCMFILE_RECORD "OSSRecDevice"
50
51 #ifdef __OpenBSD__
52 #define OSS_PCMFILE_DEFAULT "/dev/audio"
53 #else
54 #define OSS_PCMFILE_DEFAULT "/dev/dsp"
55 #endif
56
57 #define OSS_NOSELECT "OSSAvoidSelect"
58 #define OSS_NOSELECT_DEFAULT FALSE
59
60 static Ringbuf *oss_output_buffer;
61 static int oss_fd;
62 static int oss_format;
63 static int oss_samplesize;
64 static int oss_samplerate;
65 static int oss_channels;
66 static int oss_samplebytes_in,oss_samplebytes_out;
67 static gboolean oss_noselect = FALSE;
68 static gpointer play_source;
69
70 static int oss_output_flush(gpointer tsource, GTimeVal *tv, gpointer user_data);
71
oss_init(gboolean silent)72 static gboolean oss_init(gboolean silent)
73 {
74 oss_output_buffer = ringbuf_new(
75 inifile_get_guint32(INI_SETTING_SOUNDBUFSIZE,
76 INI_SETTING_SOUNDBUFSIZE_DEFAULT));
77 oss_noselect = inifile_get_gboolean(OSS_NOSELECT,OSS_NOSELECT_DEFAULT);
78 play_source = mainloop_time_source_add(NULL, oss_output_flush, NULL);
79 /* When autodetecting, this is the last tested driver before the dummy,
80 * so we might as well return TRUE */
81 return TRUE;
82 }
83
oss_quit(void)84 static void oss_quit(void)
85 {
86 mainloop_time_source_free(play_source);
87 ringbuf_free(oss_output_buffer);
88 }
89
oss_errdlg_open(char * filename,int flags,gboolean silent)90 static int oss_errdlg_open(char *filename, int flags, gboolean silent)
91 {
92 int fd;
93 gchar *c;
94 fd = open(filename,flags);
95 if (fd == -1 && !silent) {
96 c = g_strdup_printf(_("Could not open '%s': %s"),filename,strerror(errno));
97 user_error(c);
98 g_free(c);
99 }
100 return fd;
101 }
102
oss_errdlg_ioctl(int filedes,int cmd,void * arg,gboolean silent)103 static int oss_errdlg_ioctl(int filedes, int cmd, void *arg, gboolean silent)
104 {
105 int i;
106 gchar *c;
107 i = ioctl(filedes,cmd,arg);
108 if (i == -1 && errno != EPIPE && !silent) {
109 c = g_strdup_printf(_("Error in sound driver: ioctl failed: %s"),strerror(errno));
110 user_error(c);
111 g_free(c);
112 }
113 return i;
114 }
115
oss_try_format(Dataformat * format,gboolean input,gboolean silent)116 static gint oss_try_format(Dataformat *format, gboolean input, gboolean silent)
117 {
118 gchar *fname;
119 int i,j;
120 /* For now, we just refuse floating-point format. We should probably
121 * use a fallback format instead */
122 if (format->type == DATAFORMAT_FLOAT) return -1;
123 /* Determine the right format */
124 oss_samplesize = format->samplesize;
125 switch (oss_samplesize) {
126 case 1:
127 oss_format = (format->sign) ? AFMT_S8 : AFMT_U8;
128 break;
129 case 2:
130 if (!format->bigendian)
131 oss_format = (format->sign) ? AFMT_S16_LE : AFMT_U16_LE;
132 else
133 oss_format = (format->sign) ? AFMT_S16_BE : AFMT_U16_BE;
134 break;
135 case 3:
136 case 4:
137 if (!format->sign) return -1;
138 if (format->packing == 2) return -1;
139 /* This is really hairy, but the AFMT_S32-constants don't seem to be
140 * defined in all soundcard.h files */
141 if (format->bigendian) {
142 #ifdef AFMT_S32_BE
143 oss_format = AFMT_S32_BE;
144 #else
145 return -1;
146 #endif
147 } else {
148 #ifdef AFMT_S32_LE
149 oss_format = AFMT_S32_LE;
150 #else
151 return -1;
152 #endif
153 }
154 break;
155 default:
156 g_assert_not_reached();
157 }
158 /* Open the file */
159 fname = inifile_get(OSS_PCMFILE_PLAYBACK,OSS_PCMFILE_DEFAULT);
160 if (input)
161 fname = inifile_get(OSS_PCMFILE_RECORD, fname);
162 oss_fd = oss_errdlg_open(fname, input ? O_RDONLY : O_WRONLY, silent);
163 if (oss_fd == -1) return silent?-1:+1;
164 /* Try to set the format */
165 j = oss_format;
166 i = oss_errdlg_ioctl(oss_fd, SNDCTL_DSP_SETFMT, &j, silent);
167 if (i == -1 || j != oss_format) {
168 close(oss_fd);
169 oss_fd=-1;
170 return (i != -1 || silent)?-1:+1;
171 }
172 /* Try to set the number of channels */
173 j = oss_channels = format->channels;
174 i = oss_errdlg_ioctl(oss_fd, SNDCTL_DSP_CHANNELS, &j, silent);
175 if (i==-1 || j != oss_channels) {
176 close(oss_fd);
177 oss_fd=-1;
178 return (i != -1 || silent)?-1:+1;
179 }
180 /* Try to set the sample rate */
181 j = oss_samplerate = format->samplerate;
182 i = oss_errdlg_ioctl(oss_fd, SNDCTL_DSP_SPEED, &j, silent);
183 /* FIXME: Variable tolerance (esp. for input) */
184 /* Currently tolerates 5% difference between requested/received samplerate
185 */
186 if (i==-1 || abs(j-oss_samplerate) > oss_samplerate/20) {
187 close(oss_fd);
188 oss_fd=-1;
189 return (i != -1 || silent)?-1:+1;
190 }
191 /* Everything went well! */
192 oss_samplebytes_in = oss_samplebytes_out = oss_channels * oss_samplesize;
193 if (oss_samplesize == 3)
194 oss_samplebytes_out += oss_channels;
195 return 0;
196 }
197
oss_output_select_format(Dataformat * format,gboolean silent,GVoidFunc ready_func)198 static gint oss_output_select_format(Dataformat *format, gboolean silent,
199 GVoidFunc ready_func)
200 {
201 return oss_try_format(format,FALSE,silent);
202 }
203
oss_input_select_format(Dataformat * format,gboolean silent,GVoidFunc ready_func)204 static gint oss_input_select_format(Dataformat *format, gboolean silent,
205 GVoidFunc ready_func)
206 {
207 return oss_try_format(format,TRUE,silent);
208 }
209
oss_errdlg_write(int fd,gchar * buffer,size_t size)210 static int oss_errdlg_write(int fd, gchar *buffer, size_t size)
211 {
212 ssize_t s;
213 gchar *c;
214 s = write(fd,buffer,size);
215 if (s == -1) {
216 c = g_strdup_printf(_("Error in sound driver: write failed: %s"),strerror(errno));
217 user_error(c);
218 g_free(c);
219 }
220 return s;
221 }
222
oss_errdlg_read(int fd,gchar * buffer,size_t size)223 static int oss_errdlg_read(int fd, gchar *buffer, size_t size)
224 {
225 ssize_t s;
226 gchar *c;
227 s = read(fd,buffer,size);
228 if (s == -1) {
229 c = g_strdup_printf(_("Error in sound driver: read failed: %s"),strerror(errno));
230 user_error(c);
231 g_free(c);
232 }
233 return s;
234 }
235
oss_output_flush(gpointer ts,GTimeVal * tv,gpointer ud)236 static int oss_output_flush(gpointer ts, GTimeVal *tv, gpointer ud)
237 {
238 fd_set a;
239 struct timeval t = { 0 };
240 int i;
241 guint u;
242 gchar *c;
243 audio_buf_info info;
244 /* printf("oss_output_flush: ringbuf_available: %d\n",ringbuf_available(oss_output_buffer)); */
245 /* Do we have anything to write at all? */
246 if (ringbuf_available(oss_output_buffer) == 0) return 0;
247 /* Do a select call and see if it's ready for writing */
248 if (!oss_noselect) {
249 FD_ZERO(&a);
250 FD_SET(oss_fd, &a);
251 i = select(FD_SETSIZE, NULL, &a, NULL, &t);
252 if (i==-1) {
253 c = g_strdup_printf(_("Error in sound driver: select failed: %s"),strerror(errno));
254 user_error(c);
255 return 2000;
256 }
257 if (i==0) return 50;
258 }
259 #ifdef SNDCTL_DSP_GETOSPACE
260 /* Now do an ioctl call to check the number of writable bytes */
261 i = oss_errdlg_ioctl(oss_fd, SNDCTL_DSP_GETOSPACE, &info, TRUE);
262 if (i == -1 && errno == EPIPE) {
263 /* This is a workaround for a bug in ALSA 0.9.6 OSS emulation, where
264 * this call can return "broken pipe", especially after a
265 * SNDCTL_DSP_RESET call. */
266 info.fragments = 1024;
267 info.fragsize = 1*2*3*4;
268 } else if (i == -1) return 0;
269 #else
270 /* Fill in dummy values */
271 info.fragments = 1024;
272 info.fragsize = 1*2*3*4;
273 #endif
274 /* Calculate the number of bytes to write (into u) */
275 u = ringbuf_available(oss_output_buffer);
276 if (u > info.fragsize)
277 u -= u % info.fragsize;
278 u = MIN(u, info.fragments*info.fragsize);
279 if (u == 0) return 50;
280 /* Write it out! */
281 c = g_malloc(u);
282 ringbuf_dequeue(oss_output_buffer, c, u);
283 i = oss_errdlg_write(oss_fd, c, u);
284 g_free(c);
285 return 10;
286 }
287
oss_output_want_data(void)288 static gboolean oss_output_want_data(void)
289 {
290 /* oss_output_flush(); */
291 return (ringbuf_freespace(oss_output_buffer) >= oss_samplebytes_out);
292 }
293
oss_output_play(gchar * buffer,guint bufsize)294 static guint oss_output_play(gchar *buffer, guint bufsize)
295 {
296 guint u=0;
297 GTimeVal t;
298 guint bs0 = bufsize;
299 /* 24-bit samples need padding */
300 if (oss_samplesize == 3) {
301 while (bufsize >= 3 && ringbuf_freespace(oss_output_buffer)>=oss_samplebytes_out) {
302 for (u=0; u<oss_channels; u++) {
303 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
304 ringbuf_enqueue(oss_output_buffer,"",1);
305 ringbuf_enqueue(oss_output_buffer,buffer,3);
306 #else
307 ringbuf_enqueue(oss_output_buffer,buffer,3);
308 ringbuf_enqueue(oss_output_buffer,"",1);
309 #endif
310 buffer += 3;
311 bufsize -= 3;
312 u += 3;
313 }
314 }
315 }
316 /* Normal operation */
317 else if (bufsize) {
318 u = ringbuf_freespace(oss_output_buffer);
319 if (u > bufsize) u = bufsize;
320 /* Make sure we store whole samples */
321 u -= u%(oss_samplebytes_out);
322 if (u>0)
323 u = ringbuf_enqueue(oss_output_buffer, buffer, u);
324 buffer += u;
325 bufsize -= u;
326 }
327 if (ringbuf_freespace(oss_output_buffer) < oss_samplebytes_out &&
328 !mainloop_time_source_enabled(play_source)) {
329 puts("Starting time source");
330 g_get_current_time(&t);
331 mainloop_time_source_restart(play_source,&t);
332 }
333 g_assert(bufsize == 0 || bufsize >= oss_samplebytes_in);
334 printf("oss_output_play, bs0: %d, u: %d\n",bs0,u);
335 /* oss_output_flush(); */
336 return bs0 ? u : ringbuf_available(oss_output_buffer);
337 }
338
oss_output_clear_buffers(void)339 static void oss_output_clear_buffers(void)
340 {
341 #ifdef SNDCTL_DSP_RESET
342 ioctl(oss_fd, SNDCTL_DSP_RESET);
343 #endif
344 ringbuf_drain(oss_output_buffer);
345 }
346
347
oss_output_stop(gboolean must_flush)348 static gboolean oss_output_stop(gboolean must_flush)
349 {
350 if (oss_fd == -1) return TRUE;
351 if (must_flush)
352 while (ringbuf_available(oss_output_buffer)>0) {
353 oss_output_flush(NULL,NULL,NULL);
354 do_yield(TRUE);
355 }
356 else
357 oss_output_clear_buffers();
358 close(oss_fd);
359 oss_fd = -1;
360 /* printf("oss_stop: oss_delay_time = %f\n",oss_delay_time); */
361 ringbuf_drain(oss_output_buffer);
362 return must_flush;
363 }
364
oss_input_stop(void)365 static void oss_input_stop(void)
366 {
367 if (oss_fd >= 0) close(oss_fd);
368 oss_fd = -1;
369 }
370
oss_input_store(Ringbuf * buffer)371 static void oss_input_store(Ringbuf *buffer)
372 {
373 fd_set a;
374 struct timeval t = { 0 };
375 int i;
376 guint u;
377 gchar *c;
378 audio_buf_info info;
379 /* Do a select call to see if data is available */
380 if (!oss_noselect) {
381 FD_ZERO(&a);
382 FD_SET(oss_fd, &a);
383 i = select(FD_SETSIZE, &a, NULL, NULL, &t);
384 if (i==-1) {
385 c = g_strdup_printf(_("Error in sound driver: select failed: %s"),strerror(errno));
386 user_error(c);
387 return;
388 }
389 if (i==0) return;
390 }
391
392 /* Calculate the number of bytes to read (into buffer) */
393 /* If we don't know how much data is available, don't read more than 0.1
394 * seconds to avoid GUI latency... */
395 u = ringbuf_freespace(buffer);
396 #ifdef SNDCTL_DSP_GETISPACE
397 /* Now do an ioctl call to check the number of readable bytes */
398 /* Note: It seems as ALSA 0.9's OSS emulation returns 0 for this call... */
399 i = oss_errdlg_ioctl(oss_fd, SNDCTL_DSP_GETISPACE, &info, TRUE);
400 if (i > 0 && i < u) u=i;
401 else u = MIN(u,oss_samplesize*oss_channels*oss_samplerate/50);
402 #else
403 u = MIN(u,oss_samplesize*oss_channels*oss_samplerate/50);
404 #endif
405 if (u == 0) return;
406 /* Read it! */
407 c = g_malloc(u);
408 i = oss_errdlg_read(oss_fd, c, u);
409 if (i != -1)
410 ringbuf_enqueue(buffer, c, i);
411 g_free(c);
412 }
413
414 struct oss_prefdlg {
415 GtkWindow *wnd;
416 GtkEntry *pcmdev_playback;
417 GtkEntry *pcmdev_record;
418 GtkToggleButton *noselect;
419 };
420
oss_preferences_ok(GtkButton * button,struct oss_prefdlg * pd)421 static void oss_preferences_ok(GtkButton *button, struct oss_prefdlg *pd)
422 {
423 inifile_set(OSS_PCMFILE_PLAYBACK,
424 (char *)gtk_entry_get_text(pd->pcmdev_playback));
425 inifile_set(OSS_PCMFILE_RECORD,
426 (char *)gtk_entry_get_text(pd->pcmdev_record));
427 oss_noselect = gtk_toggle_button_get_active(pd->noselect);
428 inifile_set_gboolean(OSS_NOSELECT,oss_noselect);
429 gtk_widget_destroy(GTK_WIDGET(pd->wnd));
430 }
431
oss_preferences(void)432 static void oss_preferences(void)
433 {
434 GtkWidget *a,*b,*c,*d;
435 struct oss_prefdlg *pd;
436 gchar *q;
437 pd = g_malloc(sizeof(struct oss_prefdlg));
438 a = gtk_window_new(GTK_WINDOW_DIALOG);
439 gtk_window_set_modal(GTK_WINDOW(a),TRUE);
440 gtk_window_set_title(GTK_WINDOW(a),_("OSS preferences"));
441 gtk_window_set_position(GTK_WINDOW(a),GTK_WIN_POS_CENTER);
442 gtk_container_set_border_width(GTK_CONTAINER(a),5);
443 gtk_signal_connect_object(GTK_OBJECT(a),"destroy",GTK_SIGNAL_FUNC(g_free),
444 (GtkObject *)pd);
445 pd->wnd = GTK_WINDOW(a);
446 b = gtk_vbox_new(FALSE,5);
447 gtk_container_add(GTK_CONTAINER(a),b);
448 c = gtk_hbox_new(FALSE,3);
449 gtk_container_add(GTK_CONTAINER(b),c);
450 d = gtk_label_new(_("Playback device file:"));
451 gtk_container_add(GTK_CONTAINER(c),d);
452 d = gtk_entry_new();
453 q = inifile_get(OSS_PCMFILE_PLAYBACK,OSS_PCMFILE_DEFAULT);
454 gtk_entry_set_text(GTK_ENTRY(d),q);
455 gtk_container_add(GTK_CONTAINER(c),d);
456 pd->pcmdev_playback = GTK_ENTRY(d);
457 c = gtk_hbox_new(FALSE,3);
458 gtk_container_add(GTK_CONTAINER(b),c);
459 d = gtk_label_new(_("Recording device file:"));
460 gtk_container_add(GTK_CONTAINER(c),d);
461 d = gtk_entry_new();
462 gtk_entry_set_text(GTK_ENTRY(d),inifile_get(OSS_PCMFILE_RECORD,q));
463 gtk_container_add(GTK_CONTAINER(c),d);
464 pd->pcmdev_record = GTK_ENTRY(d);
465 c = gtk_check_button_new_with_label(_("Avoid select calls (try this if "
466 "recording locks up)"));
467 gtk_container_add(GTK_CONTAINER(b),c);
468 oss_noselect = inifile_get_gboolean(OSS_NOSELECT,OSS_NOSELECT_DEFAULT);
469 pd->noselect = GTK_TOGGLE_BUTTON(c);
470 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(c),oss_noselect);
471 c = gtk_hseparator_new();
472 gtk_container_add(GTK_CONTAINER(b),c);
473 c = gtk_hbutton_box_new();
474 gtk_container_add(GTK_CONTAINER(b),c);
475 d = gtk_button_new_with_label(_("OK"));
476 gtk_signal_connect(GTK_OBJECT(d),"clicked",
477 GTK_SIGNAL_FUNC(oss_preferences_ok),pd);
478 gtk_container_add(GTK_CONTAINER(c),d);
479 d = gtk_button_new_with_label(_("Close"));
480 gtk_signal_connect_object(GTK_OBJECT(d),"clicked",
481 GTK_SIGNAL_FUNC(gtk_widget_destroy),
482 GTK_OBJECT(a));
483 gtk_container_add(GTK_CONTAINER(c),d);
484 gtk_widget_show_all(a);
485 }
486
487