13 #include "sample-util.h"
16 snd_pcm_t
*pcm_handle
;
19 unsigned n_io_sources
;
21 size_t frame_size
, fragment_size
;
22 struct pa_memchunk memchunk
, silence
;
25 static const char* const valid_modargs
[] = {
31 #define DEFAULT_SINK_NAME "alsa_output"
32 #define DEFAULT_DEVICE "plughw:0,0"
34 static void xrun_recovery(struct userdata
*u
) {
37 fprintf(stderr
, "*** XRUN ***\n");
39 if (snd_pcm_prepare(u
->pcm_handle
) < 0)
40 fprintf(stderr
, "snd_pcm_prepare() failed\n");
43 static void do_write(struct userdata
*u
) {
47 struct pa_memchunk
*memchunk
= NULL
;
48 snd_pcm_sframes_t frames
;
50 if (u
->memchunk
.memblock
)
51 memchunk
= &u
->memchunk
;
53 if (pa_sink_render(u
->sink
, u
->fragment_size
, &u
->memchunk
) < 0)
54 memchunk
= &u
->silence
;
56 memchunk
= &u
->memchunk
;
59 assert(memchunk
->memblock
&& memchunk
->memblock
->data
&& memchunk
->length
&& memchunk
->memblock
->length
&& (memchunk
->length
% u
->frame_size
) == 0);
61 assert(u
->pcm_handle
);
62 assert(memchunk
->memblock
);
63 assert(memchunk
->memblock
->data
);
64 assert(memchunk
->length
);
65 assert(u
->frame_size
);
67 if ((frames
= snd_pcm_writei(u
->pcm_handle
, memchunk
->memblock
->data
+ memchunk
->index
, memchunk
->length
/ u
->frame_size
)) < 0) {
68 if (frames
== -EPIPE
) {
73 fprintf(stderr
, "snd_pcm_writei() failed\n");
77 if (memchunk
== &u
->memchunk
) {
78 memchunk
->index
+= frames
* u
->frame_size
;
79 memchunk
->length
-= frames
* u
->frame_size
;
81 if (memchunk
->length
== 0) {
82 pa_memblock_unref(memchunk
->memblock
);
83 memchunk
->memblock
= NULL
;
84 memchunk
->index
= memchunk
->length
= 0;
92 static void io_callback(struct pa_mainloop_api
*a
, void *id
, int fd
, enum pa_mainloop_api_io_events events
, void *userdata
) {
93 struct userdata
*u
= userdata
;
96 if (snd_pcm_state(u
->pcm_handle
) == SND_PCM_STATE_XRUN
)
102 static uint32_t sink_get_latency_cb(struct pa_sink
*s
) {
103 struct userdata
*u
= s
->userdata
;
104 snd_pcm_sframes_t frames
;
105 assert(s
&& u
&& u
->sink
);
107 if (snd_pcm_delay(u
->pcm_handle
, &frames
) < 0) {
108 fprintf(stderr
, __FILE__
": failed to get delay\n");
109 s
->get_latency
= NULL
;
116 return pa_samples_usec(frames
* u
->frame_size
, &s
->sample_spec
);
119 int pa_module_init(struct pa_core
*c
, struct pa_module
*m
) {
120 struct pa_modargs
*ma
= NULL
;
122 struct userdata
*u
= NULL
;
123 snd_pcm_hw_params_t
*hwparams
;
125 struct pollfd
*pfds
, *ppfd
;
126 struct pa_sample_spec ss
;
128 snd_pcm_uframes_t buffer_size
;
131 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
132 fprintf(stderr
, __FILE__
": failed to parse module arguments\n");
136 u
= malloc(sizeof(struct userdata
));
138 memset(u
, 0, sizeof(struct userdata
));
141 if (snd_pcm_open(&u
->pcm_handle
, dev
= pa_modargs_get_value(ma
, "device", DEFAULT_DEVICE
), SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
) < 0) {
142 fprintf(stderr
, "Error opening PCM device %s\n", dev
);
146 ss
.format
= PA_SAMPLE_S16LE
;
151 buffer_size
= periods
*256;
153 snd_pcm_hw_params_alloca(&hwparams
);
154 if (snd_pcm_hw_params_any(u
->pcm_handle
, hwparams
) < 0 ||
155 snd_pcm_hw_params_set_access(u
->pcm_handle
, hwparams
, SND_PCM_ACCESS_RW_INTERLEAVED
) < 0 ||
156 snd_pcm_hw_params_set_format(u
->pcm_handle
, hwparams
, SND_PCM_FORMAT_S16_LE
) < 0 ||
157 snd_pcm_hw_params_set_rate_near(u
->pcm_handle
, hwparams
, &ss
.rate
, NULL
) < 0 ||
158 snd_pcm_hw_params_set_channels(u
->pcm_handle
, hwparams
, ss
.channels
) < 0 ||
159 snd_pcm_hw_params_set_periods_near(u
->pcm_handle
, hwparams
, &periods
, NULL
) < 0 ||
160 snd_pcm_hw_params_set_buffer_size_near(u
->pcm_handle
, hwparams
, &buffer_size
) < 0 ||
161 snd_pcm_hw_params(u
->pcm_handle
, hwparams
) < 0) {
162 fprintf(stderr
, "Error setting HW params.\n");
166 u
->sink
= pa_sink_new(c
, pa_modargs_get_value(ma
, "sink_name", DEFAULT_SINK_NAME
), 0, &ss
);
169 u
->sink
->get_latency
= sink_get_latency_cb
;
170 u
->sink
->userdata
= u
;
171 pa_sink_set_owner(u
->sink
, m
);
172 u
->sink
->description
= pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev
);
174 u
->n_io_sources
= snd_pcm_poll_descriptors_count(u
->pcm_handle
);
176 pfds
= malloc(sizeof(struct pollfd
) * u
->n_io_sources
);
178 if (snd_pcm_poll_descriptors(u
->pcm_handle
, pfds
, u
->n_io_sources
) < 0) {
179 printf("Unable to obtain poll descriptors for playback.\n");
184 u
->io_sources
= malloc(sizeof(void*) * u
->n_io_sources
);
185 assert(u
->io_sources
);
187 for (i
= 0, ios
= u
->io_sources
, ppfd
= pfds
; i
< u
->n_io_sources
; i
++, ios
++, ppfd
++) {
188 *ios
= c
->mainloop
->source_io(c
->mainloop
, ppfd
->fd
,
189 ((ppfd
->events
& POLLIN
) ? PA_MAINLOOP_API_IO_EVENT_INPUT
: 0) |
190 ((ppfd
->events
& POLLOUT
) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT
: 0), io_callback
, u
);
196 u
->frame_size
= pa_sample_size(&ss
);
197 u
->fragment_size
= buffer_size
*u
->frame_size
/periods
;
199 fprintf(stderr
, __FILE__
": using %u fragments of size %u bytes.\n", periods
, u
->fragment_size
);
201 u
->silence
.memblock
= pa_memblock_new(u
->silence
.length
= u
->fragment_size
);
202 assert(u
->silence
.memblock
);
203 pa_silence_memblock(u
->silence
.memblock
, &ss
);
204 u
->silence
.index
= 0;
206 u
->memchunk
.memblock
= NULL
;
207 u
->memchunk
.index
= u
->memchunk
.length
= 0;
220 pa_module_done(c
, m
);
225 void pa_module_done(struct pa_core
*c
, struct pa_module
*m
) {
229 if ((u
= m
->userdata
)) {
233 pa_sink_free(u
->sink
);
235 for (ios
= u
->io_sources
, i
= 0; i
< u
->n_io_sources
; i
++, ios
++)
236 c
->mainloop
->cancel_io(c
->mainloop
, *ios
);
240 snd_pcm_drop(u
->pcm_handle
);
241 snd_pcm_close(u
->pcm_handle
);
244 if (u
->memchunk
.memblock
)
245 pa_memblock_unref(u
->memchunk
.memblock
);
246 if (u
->silence
.memblock
)
247 pa_memblock_unref(u
->silence
.memblock
);