1 /* Extended Module Player
2  * Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
3  *
4  * This file is part of the Extended Module Player and is distributed
5  * under the terms of the GNU General Public License. See the COPYING
6  * file for more information.
7  */
8 
9 #include <alsa/asoundlib.h>
10 #include <alsa/pcm.h>
11 #include "sound.h"
12 
13 static snd_pcm_t *pcm_handle;
14 
init(struct options * options)15 static int init(struct options *options)
16 {
17 	char **parm = options->driver_parm;
18 	snd_pcm_hw_params_t *hwparams;
19 	int ret;
20 	unsigned int channels, fmt;
21 	unsigned int btime = 250000;	/* 250ms */
22 	unsigned int ptime = 50000;	/* 50ms */
23 	char *card_name = "default";
24 	unsigned int rate = options->rate;
25 	int format = options->format;
26 
27 	parm_init(parm);
28 	chkparm1("buffer", btime = 1000 * strtoul(token, NULL, 0));
29 	chkparm1("period", btime = 1000 * strtoul(token, NULL, 0));
30 	chkparm1("card", card_name = token);
31 	parm_end();
32 
33 	if ((ret = snd_pcm_open(&pcm_handle, card_name,
34 		SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
35 		fprintf(stderr, "Unable to initialize ALSA pcm device %s: %s\n",
36 						card_name, snd_strerror(ret));
37 		return -1;
38 	}
39 
40 	channels = format & XMP_FORMAT_MONO ? 1 : 2;
41 	if (format & XMP_FORMAT_UNSIGNED) {
42 		fmt = format & XMP_FORMAT_8BIT ?
43 				SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_U16;
44 	} else {
45 		fmt = format & XMP_FORMAT_8BIT ?
46 				SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_S16;
47 	}
48 
49 	snd_pcm_hw_params_alloca(&hwparams);
50 	snd_pcm_hw_params_any(pcm_handle, hwparams);
51 	snd_pcm_hw_params_set_access(pcm_handle, hwparams,
52 				SND_PCM_ACCESS_RW_INTERLEAVED);
53 	snd_pcm_hw_params_set_format(pcm_handle, hwparams, fmt);
54 	snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0);
55 	snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &channels);
56 	snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &btime, 0);
57 	snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &ptime, 0);
58 	snd_pcm_nonblock(pcm_handle, 0);
59 
60 	if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
61 		fprintf(stderr, "Unable to set ALSA output parameters: %s\n",
62 					snd_strerror(ret));
63 		return -1;
64 	}
65 
66 	if ((ret = snd_pcm_prepare(pcm_handle)) < 0) {
67 		fprintf(stderr, "Unable to prepare ALSA: %s\n",
68 					snd_strerror(ret));
69 		return -1;
70 	}
71 
72 	if (channels == 1) {
73 		format |= XMP_FORMAT_MONO;
74 	} else {
75 		format &= ~XMP_FORMAT_MONO;
76 	}
77 
78 	options->rate = rate;
79 	options->format = format;
80 
81 	return 0;
82 }
83 
play(void * b,int i)84 static void play(void *b, int i)
85 {
86 	int frames;
87 
88 	frames = snd_pcm_bytes_to_frames(pcm_handle, i);
89 	if (snd_pcm_writei(pcm_handle, b, frames) < 0) {
90 		snd_pcm_prepare(pcm_handle);
91 	}
92 }
93 
deinit(void)94 static void deinit(void)
95 {
96 	snd_pcm_close(pcm_handle);
97 }
98 
flush(void)99 static void flush(void)
100 {
101 	snd_pcm_drain(pcm_handle);
102 }
103 
onpause(void)104 static void onpause(void)
105 {
106 }
107 
onresume(void)108 static void onresume(void)
109 {
110 }
111 
112 static const char *const help[] = {
113 	"buffer=num", "Set the ALSA buffer time in milliseconds",
114 	"period=num", "Set the ALSA period time in milliseconds",
115 	"card <name>", "Select sound card to use",
116 	NULL
117 };
118 
119 struct sound_driver sound_alsa = {
120 	"alsa",
121 	"ALSA PCM audio",
122 	help,
123 	init,
124 	deinit,
125 	play,
126 	flush,
127 	onpause,
128 	onresume
129 };
130