1 /*
2 * madplay - MPEG audio decoder and player
3 * Copyright (C) 2000-2004 Robert Leslie
4 * QNX audio output module (C) 2001 Steven Grimm
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * $Id: audio_qnx.c,v 1.9 2004/01/23 09:41:31 rob Exp $
21 */
22
23 # ifdef HAVE_CONFIG_H
24 # include "config.h"
25 # endif
26
27 # include "global.h"
28
29 # include <stdio.h>
30 # include <string.h>
31 # include <sys/asoundlib.h>
32 # include <stdlib.h>
33 # include <mad.h>
34
35 # include "gettext.h"
36
37 # include "audio.h"
38
39 # if defined(WORDS_BIGENDIAN)
40 # define audio_pcm_s16 audio_pcm_s16be
41 # else
42 # define audio_pcm_s16 audio_pcm_s16le
43 # endif
44
45 static snd_pcm_t *pcm_handle;
46 static snd_mixer_t *mixer_handle;
47 static int card = -1, dev = -1, frag_size = 8192, frags_max = 32;
48
fill_int(char * name,int * var)49 static void fill_int(char *name, int *var)
50 {
51 char *tmp = getenv(name);
52
53 if (tmp != NULL)
54 *var = atoi(tmp);
55 }
56
57 static
init(struct audio_init * init)58 int init(struct audio_init *init)
59 {
60 fill_int("MADPLAY_CARD", &card);
61 fill_int("MADPLAY_DEV", &dev);
62 fill_int("MADPLAY_FRAG_SIZE", &frag_size);
63 fill_int("MADPLAY_FRAGS_MAX", &frags_max);
64
65 if (card == -1)
66 {
67 if (snd_pcm_open_preferred(&pcm_handle, &card, &dev,
68 SND_PCM_OPEN_PLAYBACK) < 0)
69 {
70 audio_error = _("can't open sound card");
71 return -1;
72 }
73 }
74 else
75 {
76 if (snd_pcm_open(&pcm_handle, card, dev,
77 SND_PCM_OPEN_PLAYBACK) < 0)
78 {
79 audio_error = _("can't open sound card");
80 return -1;
81 }
82 }
83
84 if (snd_pcm_plugin_set_disable(pcm_handle, PLUGIN_DISABLE_MMAP) < 0)
85 {
86 audio_error = _("can't disable mmap mode");
87 return -1;
88 }
89
90 return 0;
91 }
92
93 static
config(struct audio_config * config)94 int config(struct audio_config *config)
95 {
96 snd_pcm_channel_params_t pp;
97 snd_pcm_channel_setup_t setup;
98 snd_mixer_group_t group;
99
100 memset(&pp, 0, sizeof(pp));
101 memset(&setup, 0, sizeof(setup));
102 memset(&group, 0, sizeof(group));
103
104 pp.mode = SND_PCM_MODE_BLOCK;
105 pp.channel = SND_PCM_CHANNEL_PLAYBACK;
106 pp.start_mode = SND_PCM_START_FULL;
107 pp.stop_mode = SND_PCM_STOP_STOP;
108
109
110 /*
111 pp.buf.stream.queue_size = 8192; // No docs on these fields!!!
112 pp.buf.stream.fill = 6144;
113 pp.buf.stream.max_fill = 8192;
114 */
115 pp.buf.block.frag_size = frag_size;
116 pp.buf.block.frags_max = frags_max;
117 pp.buf.block.frags_min = 1;
118
119 pp.format.interleave = 1;
120 pp.format.rate = config->speed;
121 pp.format.voices = config->channels;
122 pp.format.format = SND_PCM_SFMT_S16_LE;
123
124 /* RSL: indicate 16-bit output */
125 config->precision = 16;
126
127 if (snd_pcm_plugin_params(pcm_handle, &pp) < 0)
128 {
129 audio_error = _("can't configure device");
130 return -1;
131 }
132
133 if (snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
134 {
135 audio_error = _("can't prepare device");
136 return -1;
137 }
138
139 setup.channel = SND_PCM_CHANNEL_PLAYBACK;
140 setup.mixer_gid = &group.gid;
141 if (snd_pcm_plugin_setup(pcm_handle, &setup) < 0)
142 {
143 audio_error = _("can't set up plugin");
144 return -1;
145 }
146
147 if (snd_mixer_open(&mixer_handle, 0, setup.mixer_device) < 0)
148 {
149 audio_error = _("can't open mixer");
150 return -1;
151 }
152
153 return 0;
154 }
155
156 static
play(struct audio_play * play)157 int play(struct audio_play *play)
158 {
159 unsigned char data[MAX_NSAMPLES * 2 * 2];
160 int written = 0;
161 int total_bytes;
162
163 audio_pcm_s16le(data, play->nsamples,
164 play->samples[0], play->samples[1], play->mode, play->stats);
165
166 total_bytes = (play->samples[1] ? 4 : 2) * play->nsamples;
167
168 while ((written += snd_pcm_plugin_write(pcm_handle, data + written,
169 total_bytes - written)) < total_bytes)
170 {
171 snd_pcm_channel_status_t status;
172
173 memset(&status, 0, sizeof(status));
174 if (snd_pcm_plugin_status(pcm_handle, &status) < 0)
175 {
176 audio_error = _("can't get status to recover playback");
177 return -1;
178 }
179
180 if (status.status == SND_PCM_STATUS_READY ||
181 status.status == SND_PCM_STATUS_UNDERRUN)
182 {
183 if (snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
184 {
185 audio_error = _("can't prepare device to recover playback");
186 return -1;
187 }
188 }
189
190 if (written < 0)
191 written = 0;
192 }
193
194 return 0;
195 }
196
197 static
stop(struct audio_stop * stop)198 int stop(struct audio_stop *stop)
199 {
200 snd_pcm_plugin_playback_drain(pcm_handle);
201 return 0;
202 }
203
204 static
finish(struct audio_finish * finish)205 int finish(struct audio_finish *finish)
206 {
207 snd_pcm_plugin_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
208 snd_pcm_close(pcm_handle);
209
210 return 0;
211 }
212
audio_qnx(union audio_control * control)213 int audio_qnx(union audio_control *control)
214 {
215 audio_error = 0;
216
217 switch (control->command) {
218 case AUDIO_COMMAND_INIT:
219 return init(&control->init);
220
221 case AUDIO_COMMAND_CONFIG:
222 return config(&control->config);
223
224 case AUDIO_COMMAND_PLAY:
225 return play(&control->play);
226
227 case AUDIO_COMMAND_STOP:
228 return stop(&control->stop);
229
230 case AUDIO_COMMAND_FINISH:
231 return finish(&control->finish);
232 }
233
234 return 0;
235 }
236