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