Project

General

Profile

0001-Add-Sndio-plugin.patch

Brad Smith, May 12, 2012 05:52

View differences:

configure.ac
623 623
	OUTPUT_PLUGINS="$OUTPUT_PLUGINS sdlout"
624 624
fi
625 625

  
626
dnl *** sndio output
627

  
628
AC_ARG_ENABLE(sndio,
629
    [  --disable-sndio         disable sndio output plugin (default=enabled) ],
630
    [have_sndio=$enableval],
631
    [have_sndio=yes]
632
)
633

  
634
if test "x$have_sndio" = "xyes"; then
635
	AC_CHECK_HEADER(sndio.h,, have_sndio=no)
636
        if test x$have_sndio = xyes; then  
637
		AC_CHECK_LIB(sndio, sio_open, haves_sndio=yes, have_sndio=no)
638
        fi
639
fi
640

  
641
if test "x$have_sndio" = "xyes"; then
642
	OUTPUT_PLUGINS="$OUTPUT_PLUGINS sndio"
643
	SNDIO_LIBS="-lsndio"
644
	AC_SUBST(SNDIO_LIBS)
645
fi
626 646

  
627 647
dnl *** amidi-plug (note: to avoid checking twice ALSA, this should appear somewhere after the alsa output plugin check)
628 648

  
......
1075 1095
echo "  --------------"
1076 1096
echo "  Open Sound System (oss4):               $enable_oss4"
1077 1097
echo "  Advanced Linux Sound Arch. (alsa):      $have_alsa"
1098
echo "  Sndio (sndio):                          $have_sndio"
1078 1099
echo "  PulseAudio (pulse):                     $have_pulse"
1079 1100
echo "  Jack Audio Connection Kit (jack):       $enable_jack"
1080 1101
echo "  Simple DirectMedia Layer (sdlout):      $enable_sdlout"
extra.mk.in
98 98
SIDPLAY2_LIBS ?= @SIDPLAY2_LIBS@
99 99
SNDFILE_CFLAGS ?= @SNDFILE_CFLAGS@
100 100
SNDFILE_LIBS ?= @SNDFILE_LIBS@
101
SNDIO_LIBS ?= @SNDIO_LIBS@
101 102
WAVPACK_CFLAGS ?= @WAVPACK_CFLAGS@
102 103
WAVPACK_LIBS ?= @WAVPACK_LIBS@
103 104
XCOMPOSITE_CFLAGS ?= @XCOMPOSITE_CFLAGS@
src/sndio/Makefile
1
PLUGIN = sndio${PLUGIN_SUFFIX}
2

  
3
SRCS =	sndio.c
4

  
5
include ../../buildsys.mk
6
include ../../extra.mk
7

  
8
plugindir := ${plugindir}/${OUTPUT_PLUGIN_DIR}
9

  
10
CFLAGS += ${PLUGIN_CFLAGS}
11
CPPFLAGS += ${PLUGIN_CPPFLAGS} ${GTK_CFLAGS} ${GLIB_CFLAGS} -I../..
12
LIBS += ${GTK_LIBS} ${GLIB_LIBS} ${SNDIO_LIBS}
src/sndio/sndio.c
1
/*
2
 * Copyright (c) 2008, 2009 Thomas Pfaff <tpfaff@tp76.info>
3
 * Copyright (c) 2012 Alexandre Ratchov <alex@caoua.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17

  
18
#include <errno.h>
19
#include <poll.h>
20
#include <pthread.h>
21
#include <sndio.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <gtk/gtk.h>
26
#include <audacious/plugin.h>
27
#include <audacious/misc.h>
28
#include <audacious/i18n.h>
29
#include <audacious/plugin.h>
30
#include <libaudgui/libaudgui.h>
31
#include <libaudgui/libaudgui-gtk.h>
32

  
33
#include "config.h"
34

  
35
/*
36
 * minimum output buffer size in milliseconds
37
 */
38
#define BUFFER_SIZE_MIN	250
39

  
40
bool_t	sndio_init(void);
41
void	sndio_cleanup(void);
42
void	sndio_about(void);
43
int	sndio_take_message(const char *, const void *, int);
44
void	sndio_configure(void);
45
void	sndio_get_volume(int *, int *);
46
void	sndio_set_volume(int, int);
47
bool_t	sndio_open(int, int, int);
48
void	sndio_close(void);
49
int	sndio_buffer_free(void);
50
void    sndio_period_wait(void);
51
void	sndio_write(void *, int);
52
void	sndio_pause(bool_t);
53
void	sndio_flush(int);
54
int	sndio_output_time(void);
55
int	sndio_written_time(void);
56
void	sndio_drain(void);
57
void	sndio_set_written_time(int);
58

  
59
void	onmove_cb(void *, int);
60
void	onvol_cb(void *, unsigned);
61

  
62
void	configure_win_ok_cb(GtkWidget *, gpointer);
63

  
64
static struct sio_par par;
65
static struct sio_hdl *hdl;
66
static long long rdpos;
67
static long long wrpos;
68
static int paused, restarted, volume;
69
static int pause_pending, flush_pending, volume_pending;
70
static int bytes_per_sec;
71
static pthread_mutex_t mtx;
72

  
73
static GtkWidget *configure_win;
74
static GtkWidget *adevice_entry;
75
static gchar *audiodev;
76

  
77
AUD_OUTPUT_PLUGIN
78
(
79
	.name = "sndio",
80
	.init = sndio_init,
81
	.cleanup = sndio_cleanup,
82
	.about = sndio_about,
83
	.configure = sndio_configure,
84
	.probe_priority = 2,
85
	.get_volume = sndio_get_volume,
86
	.set_volume = sndio_set_volume,
87
	.open_audio = sndio_open,
88
	.write_audio = sndio_write,
89
	.close_audio = sndio_close,
90
	.buffer_free = sndio_buffer_free,
91
	.period_wait = sndio_period_wait,
92
	.flush = sndio_flush,
93
	.pause = sndio_pause,
94
	.output_time = sndio_output_time,
95
	.written_time = sndio_written_time,
96
	.set_written_time = sndio_set_written_time,
97
	.drain = sndio_drain
98
)
99

  
100
static struct fmt_to_par {
101
	int fmt, bits, sig, le;
102
} fmt_to_par[] = {
103
	{FMT_S8,      8, 1, 0}, {FMT_U8,      8, 1, 0},
104
	{FMT_S16_LE, 16, 1, 1}, {FMT_S16_BE, 16, 1, 0},
105
	{FMT_U16_LE, 16, 0, 1},	{FMT_U16_BE, 16, 0, 0},
106
	{FMT_S24_LE, 24, 1, 1},	{FMT_S24_BE, 24, 1, 0},
107
	{FMT_U24_LE, 24, 0, 1},	{FMT_U24_BE, 24, 0, 0},
108
	{FMT_S32_LE, 32, 1, 1},	{FMT_S32_BE, 32, 1, 0},
109
	{FMT_U32_LE, 32, 0, 1},	{FMT_U32_BE, 32, 0, 0}
110
};
111

  
112
static const gchar * const sndio_defaults[] = {
113
	"volume", "100",
114
	"audiodev", "",
115
	NULL,
116
};
117

  
118
static void
119
reset(void)
120
{
121
	if (!restarted) {
122
		restarted = 1;
123
		sio_stop(hdl);
124
		sio_start(hdl);
125
		rdpos = wrpos;
126
	}
127
}
128

  
129
static void
130
wait_ready(void)
131
{
132
	int n;
133
	struct pollfd pfds[16];
134

  
135
	if (volume_pending) {
136
		sio_setvol(hdl, volume * SIO_MAXVOL / 100);
137
		volume_pending = 0;
138
	}
139
	if (flush_pending) {
140
		reset();
141
		flush_pending = 0;
142
	}
143
	if (pause_pending) {
144
		if (paused)
145
			reset();
146
		pause_pending = 0;
147
	}
148
	if (paused) {
149
		pthread_mutex_unlock(&mtx);
150
		usleep(20000);
151
		pthread_mutex_lock(&mtx);
152
		return;
153
	}
154
	n = sio_pollfd(hdl, pfds, POLLOUT);
155
	if (n != 0) {
156
		pthread_mutex_unlock(&mtx);
157
		while (poll(pfds, n, -1) < 0) {
158
			if (errno != EINTR) {
159
				perror("poll");
160
				exit(1);
161
			}
162
		}
163
		pthread_mutex_lock(&mtx);
164
	}
165
	(void)sio_revents(hdl, pfds);
166
}
167

  
168
bool_t
169
sndio_init(void)
170
{
171
	pthread_mutex_init(&mtx, NULL);
172

  
173
	aud_config_set_defaults("sndio", sndio_defaults);
174
	volume = aud_get_int("sndio", "volume");
175
	audiodev = aud_get_string("sndio", "audiodev");
176

  
177
	return (1);
178
}
179

  
180
void
181
sndio_cleanup(void)
182
{
183
	aud_set_int("sndio", "volume", volume);
184
	aud_set_string("sndio", "audiodev", audiodev);
185
	pthread_mutex_destroy(&mtx);
186
}
187

  
188
void
189
sndio_about(void)
190
{
191
	static GtkWidget *about = NULL;
192

  
193
	audgui_simple_message(&about, GTK_MESSAGE_INFO,
194
	    _("About Sndio Output Plugin"),
195
	    _("Sndio Output Plugin\n\n"
196
	    "Written by Thomas Pfaff <tpfaff@tp76.info>\n"));
197
}
198

  
199
void
200
sndio_get_volume(int *l, int *r)
201
{
202
	pthread_mutex_lock(&mtx);
203
	*l = *r = volume;
204
	pthread_mutex_unlock(&mtx);
205
}
206

  
207
void
208
sndio_set_volume(int l, int r)
209
{
210
	/* Ignore balance control, so use unattenuated channel. */
211
	pthread_mutex_lock(&mtx);
212
	volume = l > r ? l : r;
213
	volume_pending = 1;
214
	pthread_mutex_unlock(&mtx);
215
}
216

  
217
bool_t
218
sndio_open(int fmt, int rate, int nch)
219
{
220
	int i;
221
	struct sio_par askpar;
222
	GtkWidget *dialog = NULL;
223
	unsigned buffer_size;
224

  
225
	hdl = sio_open(strlen(audiodev) > 0 ? audiodev : NULL, SIO_PLAY, 1);
226
	if (!hdl) {
227
		g_warning("failed to open audio device %s", audiodev);
228
		return (0);
229
	}
230
	sio_initpar(&askpar);
231
	for (i = 0; ; i++) {
232
		if (i == sizeof(fmt_to_par) / sizeof(struct fmt_to_par)) {
233
			g_warning("unknown format %d requested", fmt);
234
			sndio_close();
235
			return 0;
236
		}
237
		if (fmt_to_par[i].fmt == fmt)
238
			break;
239
	}
240
	askpar.bits = fmt_to_par[i].bits;
241
	askpar.bps = SIO_BPS(askpar.bits);
242
	askpar.sig = fmt_to_par[i].sig;
243
	if (askpar.bits > 8)
244
		askpar.le = fmt_to_par[i].le;
245
	askpar.pchan = nch;
246
	askpar.rate = rate;
247
	buffer_size = aud_get_int(NULL, "output_buffer_size");
248
	if (buffer_size < BUFFER_SIZE_MIN)
249
		buffer_size = BUFFER_SIZE_MIN;
250
	askpar.appbufsz = buffer_size * rate / 1000;
251
	if (!sio_setpar(hdl, &askpar) || !sio_getpar(hdl, &par)) {
252
		g_warning("failed to set parameters");
253
		sndio_close();
254
		return (0);
255
	}
256
	if ((par.bps > 1 && par.le != askpar.le) ||
257
	    (par.bits < par.bps * 8 && !par.msb) ||
258
	    par.bps != askpar.bps ||
259
	    par.sig != askpar.sig ||
260
	    par.pchan != askpar.pchan ||
261
            par.rate != askpar.rate) {
262
		g_warning("parameters not supported by the audio device");
263
		audgui_simple_message(&dialog, GTK_MESSAGE_INFO,
264
		    _("Unsupported format"),
265
		    _("A format not supported by the audio device "
266
		    "was requested.\n\n"
267
		    "Please try again with the sndiod(1) server running."));
268
		sndio_close();
269
		return (0);
270
	}
271
	rdpos = 0;
272
	wrpos = 0;
273
	sio_onmove(hdl, onmove_cb, NULL);
274
	sio_onvol(hdl, onvol_cb, NULL);
275
	sio_setvol(hdl, volume * SIO_MAXVOL / 100);
276
	if (!sio_start(hdl)) {
277
		g_warning("failed to start audio device");
278
		sndio_close();
279
		return (0);
280
	}
281
	pause_pending = flush_pending = volume_pending = 0;
282
	bytes_per_sec = par.bps * par.pchan * par.rate;
283
	restarted = 1;
284
	paused = 0;
285
	return (1);
286
}
287

  
288
void
289
sndio_write(void *ptr, int length)
290
{
291
	unsigned n;
292

  
293
	pthread_mutex_lock(&mtx);
294
	for (;;) {
295
		if (paused)
296
			break;
297
		restarted = 0;
298
		n = sio_write(hdl, ptr, length);
299
		if (n == 0 && sio_eof(hdl))
300
			return;
301
		wrpos += n;
302
		length -= n;
303
		ptr = (char *)ptr + n;
304
		if (length == 0)
305
			break;
306
		wait_ready();
307
	}
308
	pthread_mutex_unlock(&mtx);
309
}
310

  
311
void
312
sndio_close(void)
313
{
314
	if (!hdl)
315
		return;
316
	sio_close(hdl);
317
	hdl = NULL;
318
}
319

  
320
int
321
sndio_buffer_free(void)
322
{
323
	return paused ? 0 : par.round * par.pchan * par.bps;
324
}
325

  
326
void
327
sndio_period_wait(void)
328
{
329
	pthread_mutex_lock(&mtx);
330
	wait_ready();
331
	pthread_mutex_unlock(&mtx);
332
}
333

  
334
void
335
sndio_flush(int time)
336
{
337
	pthread_mutex_lock(&mtx);
338
	rdpos = wrpos = (long long)time * bytes_per_sec / 1000;
339
	flush_pending = 1;
340
	pthread_mutex_unlock(&mtx);
341
}
342

  
343
void
344
sndio_pause(bool_t flag)
345
{	
346
	pthread_mutex_lock(&mtx);
347
	paused = flag;
348
	pause_pending = 1;
349
	pthread_mutex_unlock(&mtx);
350
}
351

  
352
void
353
sndio_drain(void)
354
{
355
	/* sndio always drains */
356
}
357

  
358
int
359
sndio_output_time(void)
360
{
361
	int time;
362

  
363
	pthread_mutex_lock(&mtx);
364
	time = rdpos * 1000 / bytes_per_sec;
365
	pthread_mutex_unlock(&mtx);
366
	return time;
367
}
368

  
369
int
370
sndio_written_time(void)
371
{
372
	int time;
373

  
374
	pthread_mutex_lock(&mtx);
375
	time = wrpos * 1000 / bytes_per_sec;
376
	pthread_mutex_unlock(&mtx);
377
	return time;
378
}
379

  
380
void
381
sndio_set_written_time(int time)
382
{
383
	int used;
384

  
385
	pthread_mutex_lock(&mtx);
386
	wrpos = time * bytes_per_sec / 1000;
387
	used = wrpos - rdpos;
388
	rdpos = time * bytes_per_sec / 1000;
389
	wrpos = rdpos + used;
390
	pthread_mutex_unlock(&mtx);
391
}
392

  
393
void
394
onmove_cb(void *addr, int delta)
395
{
396
	rdpos += delta * (int)(par.bps * par.pchan);
397
}
398

  
399
void
400
onvol_cb(void *addr, unsigned ctl)
401
{
402
	/* Update volume only if it actually changed */
403
	if (ctl != volume * SIO_MAXVOL / 100)
404
		volume = ctl * 100 / SIO_MAXVOL;
405
}
406

  
407
void
408
configure_win_ok_cb(GtkWidget *w, gpointer data)
409
{
410
	strlcpy(audiodev, gtk_entry_get_text(GTK_ENTRY(adevice_entry)),
411
	    PATH_MAX);
412
	aud_set_string("sndio", "audiodev", audiodev);
413
	gtk_widget_destroy(configure_win);
414
}
415

  
416
void
417
sndio_configure(void)
418
{
419
	GtkWidget *vbox;
420
	GtkWidget *adevice_frame, *adevice_text, *adevice_vbox;
421
	GtkWidget *bbox, *ok, *cancel;
422

  
423
	if (configure_win) {
424
		gtk_window_present(GTK_WINDOW(configure_win));
425
		return;
426
	}
427

  
428
	configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
429
	g_signal_connect(configure_win, "destroy",
430
	    G_CALLBACK(gtk_widget_destroyed), &configure_win);
431

  
432
	gtk_window_set_title(GTK_WINDOW(configure_win), _("sndio device"));
433
	gtk_window_set_resizable(GTK_WINDOW(configure_win), FALSE);
434
	gtk_window_set_position(GTK_WINDOW(configure_win), GTK_WIN_POS_MOUSE);
435
	gtk_container_set_border_width(GTK_CONTAINER(configure_win), 10);
436

  
437
	vbox = gtk_vbox_new(FALSE, 5);
438
	gtk_container_add(GTK_CONTAINER(configure_win), vbox);
439
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
440

  
441
	adevice_frame = gtk_frame_new(_("Audio device:"));
442
	gtk_box_pack_start(GTK_BOX(vbox), adevice_frame, FALSE, FALSE, 0);
443

  
444
	adevice_vbox = gtk_vbox_new(FALSE, 5);
445
	gtk_container_set_border_width(GTK_CONTAINER(adevice_vbox), 5);
446
	gtk_container_add(GTK_CONTAINER(adevice_frame), adevice_vbox);
447

  
448
	adevice_text = gtk_label_new(_("(empty means default)"));
449
	gtk_box_pack_start(GTK_BOX(adevice_vbox), adevice_text, TRUE, TRUE, 0);
450

  
451
	adevice_entry = gtk_entry_new();
452
	gtk_entry_set_text(GTK_ENTRY(adevice_entry), audiodev);
453
	gtk_box_pack_start(GTK_BOX(adevice_vbox), adevice_entry, TRUE, TRUE, 0);
454

  
455
	bbox = gtk_hbutton_box_new();
456
	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
457
	gtk_box_set_spacing(GTK_BOX(bbox), 5);
458
	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
459

  
460
	ok = gtk_button_new_with_label(_("OK"));
461
	g_signal_connect(ok, "clicked",
462
	    G_CALLBACK(configure_win_ok_cb), NULL);
463

  
464
	gtk_widget_set_can_default(ok, TRUE);
465
	gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
466
	gtk_widget_grab_default(ok);
467

  
468
	cancel = gtk_button_new_with_label(_("Cancel"));
469
	g_signal_connect(cancel, "clicked",
470
	    G_CALLBACK(gtk_widget_destroy), &configure_win);
471

  
472
	gtk_widget_set_can_default(cancel, TRUE);
473
	gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
474

  
475
	gtk_widget_show_all(configure_win);
476
}
0
-