1 /*
2 * gbsplay is a Gameboy sound player
3 *
4 * 2006 (C) by Tobias Diedrich <ranma+gbsplay@tdiedrich.de>
5 *
6 * Licensed under GNU GPL v1 or, at your option, any later version.
7 */
8
9 #include "common.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <alsa/asoundlib.h>
18
19 #include "plugout.h"
20
21 /* Handle for the PCM device */
22 snd_pcm_t *pcm_handle;
23
24 #if BYTE_ORDER == LITTLE_ENDIAN
25 #define SND_PCM_FORMAT_S16_NE SND_PCM_FORMAT_S16_LE
26 #else
27 #define SND_PCM_FORMAT_S16_NE SND_PCM_FORMAT_S16_BE
28 #endif
29
alsa_open(enum plugout_endian endian,long rate)30 static long regparm alsa_open(enum plugout_endian endian, long rate)
31 {
32 const char *pcm_name = "default";
33 int fmt, err;
34 unsigned exact_rate;
35 snd_pcm_hw_params_t *hwparams;
36
37 switch (endian) {
38 case PLUGOUT_ENDIAN_BIG: fmt = SND_PCM_FORMAT_S16_BE; break;
39 case PLUGOUT_ENDIAN_LITTLE: fmt = SND_PCM_FORMAT_S16_LE; break;
40 default:
41 case PLUGOUT_ENDIAN_NATIVE: fmt = SND_PCM_FORMAT_S16_NE; break;
42 }
43
44 snd_pcm_hw_params_alloca(&hwparams);
45
46 if ((err = snd_pcm_open(&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
47 fprintf(stderr, _("Could not open ALSA PCM device '%s': %s\n"), pcm_name, snd_strerror(err));
48 return -1;
49 }
50
51 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
52 fprintf(stderr, _("snd_pcm_hw_params_any failed: %s\n"), snd_strerror(err));
53 return -1;
54 }
55
56 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
57 fprintf(stderr, _("snd_pcm_hw_params_set_access failed: %s\n"), snd_strerror(err));
58 return -1;
59 }
60
61 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, fmt)) < 0) {
62 fprintf(stderr, _("snd_pcm_hw_params_set_format failed: %s\n"), snd_strerror(err));
63 return -1;
64 }
65
66 exact_rate = rate;
67 if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0)) < 0) {
68 fprintf(stderr, _("snd_pcm_hw_params_set_rate_near failed: %s\n"), snd_strerror(err));
69 return -1;
70 }
71 if (rate != exact_rate) {
72 fprintf(stderr, _("Requested rate %ldHz, got %dHz.\n"),
73 rate, exact_rate);
74 }
75
76 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2)) < 0) {
77 fprintf(stderr, _("snd_pcm_hw_params_set_channels failed: %s\n"), snd_strerror(err));
78 return -1;
79 }
80
81 if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, 4, 0)) < 0) {
82 fprintf(stderr, _("snd_pcm_hw_params_set_periods failed: %s\n"), snd_strerror(err));
83 }
84
85 if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, 8192)) < 0) {
86 fprintf(stderr, _("snd_pcm_hw_params_set_buffer_size failed: %s\n"), snd_strerror(err));
87 }
88
89 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
90 fprintf(stderr, _("snd_pcm_hw_params failed: %s\n"), snd_strerror(err));
91 return -1;
92 }
93
94 return 0;
95 }
96
is_suspended(snd_pcm_sframes_t retval)97 static long is_suspended(snd_pcm_sframes_t retval)
98 {
99 #ifdef HAVE_ESTRPIPE
100 return retval == -ESTRPIPE;
101 #else
102 return snd_pcm_state(pcm_handle) == SND_PCM_STATE_SUSPENDED;
103 #endif
104 }
105
alsa_write(const void * buf,size_t count)106 static ssize_t regparm alsa_write(const void *buf, size_t count)
107 {
108 snd_pcm_sframes_t retval;
109
110 do {
111 retval = snd_pcm_writei(pcm_handle, buf, count / 4);
112 if (!is_suspended(retval))
113 break;
114
115 /* resume from suspend */
116 while (snd_pcm_resume(pcm_handle) == -EAGAIN)
117 sleep(1);
118 } while (1);
119 if (retval < 0) {
120 fprintf(stderr, _("snd_pcm_writei failed: %s\n"), snd_strerror(retval));
121 snd_pcm_prepare(pcm_handle);
122 }
123 return retval;
124 }
125
alsa_close()126 static void regparm alsa_close()
127 {
128 snd_pcm_drop(pcm_handle);
129 snd_pcm_close(pcm_handle);
130 }
131
132 const struct output_plugin plugout_alsa = {
133 .name = "alsa",
134 .description = "ALSA sound driver",
135 .open = alsa_open,
136 .write = alsa_write,
137 .close = alsa_close,
138 };
139