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