~ubuntu-branches/ubuntu/lucid/mpg123/lucid

« back to all changes in this revision

Viewing changes to src/audio_alsa.c

Tags: upstream-0.60
ImportĀ upstreamĀ versionĀ 0.60

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
        audio_alsa: sound output with Advanced Linux Sound Architecture 1.x API
 
3
 
 
4
        copyright 2006 by the mpg123 project - free software under the terms of the LGPL 2.1
 
5
        see COPYING and AUTHORS files in distribution or http://mpg123.de
 
6
 
 
7
        written by Clemens Ladisch <clemens@ladisch.de>
 
8
*/
 
9
 
 
10
#include "config.h"
 
11
#include "mpg123.h"
 
12
#include "debug.h"
 
13
#include <errno.h>
 
14
 
 
15
/* make ALSA 0.9.x compatible to the 1.0.x API */
 
16
#define ALSA_PCM_NEW_HW_PARAMS_API
 
17
#define ALSA_PCM_NEW_SW_PARAMS_API
 
18
 
 
19
#include <alsa/asoundlib.h>
 
20
 
 
21
/* My laptop has probs playing low-sampled files with only 0.5s buffer... this should be a user setting -- ThOr */
 
22
#define BUFFER_LENGTH 0.5       /* in seconds */
 
23
 
 
24
static const struct {
 
25
        snd_pcm_format_t alsa;
 
26
        int mpg123;
 
27
} format_map[] = {
 
28
        { SND_PCM_FORMAT_S16,    AUDIO_FORMAT_SIGNED_16   },
 
29
        { SND_PCM_FORMAT_U16,    AUDIO_FORMAT_UNSIGNED_16 },
 
30
        { SND_PCM_FORMAT_U8,     AUDIO_FORMAT_UNSIGNED_8  },
 
31
        { SND_PCM_FORMAT_S8,     AUDIO_FORMAT_SIGNED_8    },
 
32
        { SND_PCM_FORMAT_A_LAW,  AUDIO_FORMAT_ALAW_8      },
 
33
        { SND_PCM_FORMAT_MU_LAW, AUDIO_FORMAT_ULAW_8      },
 
34
};
 
35
#define NUM_FORMATS (sizeof format_map / sizeof format_map[0])
 
36
 
 
37
static int initialize_device(struct audio_info_struct *ai);
 
38
 
 
39
int audio_open(struct audio_info_struct *ai)
 
40
{
 
41
        const char *pcm_name;
 
42
        snd_pcm_t *pcm;
 
43
 
 
44
        pcm_name = ai->device ? ai->device : "default";
 
45
        if (snd_pcm_open(&pcm, pcm_name, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
 
46
                fprintf(stderr, "audio_open(): cannot open device %s\n", pcm_name);
 
47
                return -1;
 
48
        }
 
49
        ai->handle = pcm;
 
50
        if (ai->format != -1) {
 
51
                /* we're going to play: initalize sample format */
 
52
                return initialize_device(ai);
 
53
        } else {
 
54
                /* query mode; sample format will be set for each query */
 
55
                return 0;
 
56
        }
 
57
}
 
58
 
 
59
static int rates_match(long int desired, unsigned int actual)
 
60
{
 
61
        return actual * 100 > desired * (100 - AUDIO_RATE_TOLERANCE) &&
 
62
               actual * 100 < desired * (100 + AUDIO_RATE_TOLERANCE);
 
63
}
 
64
 
 
65
static int initialize_device(struct audio_info_struct *ai)
 
66
{
 
67
        snd_pcm_hw_params_t *hw;
 
68
        int i;
 
69
        snd_pcm_format_t format;
 
70
        unsigned int rate;
 
71
        snd_pcm_uframes_t buffer_size;
 
72
        snd_pcm_uframes_t period_size;
 
73
        snd_pcm_sw_params_t *sw;
 
74
        snd_pcm_uframes_t boundary;
 
75
 
 
76
        snd_pcm_hw_params_alloca(&hw);
 
77
        if (snd_pcm_hw_params_any(ai->handle, hw) < 0) {
 
78
                fprintf(stderr, "initialize_device(): no configuration available\n");
 
79
                return -1;
 
80
        }
 
81
        if (snd_pcm_hw_params_set_access(ai->handle, hw, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
 
82
                fprintf(stderr, "initialize_device(): device does not support interleaved access\n");
 
83
                return -1;
 
84
        }
 
85
        format = SND_PCM_FORMAT_UNKNOWN;
 
86
        for (i = 0; i < NUM_FORMATS; ++i) {
 
87
                if (ai->format == format_map[i].mpg123) {
 
88
                        format = format_map[i].alsa;
 
89
                        break;
 
90
                }
 
91
        }
 
92
        if (format == SND_PCM_FORMAT_UNKNOWN) {
 
93
                fprintf(stderr, "initialize_device(): invalid sample format %d\n", ai->format);
 
94
                errno = EINVAL;
 
95
                return -1;
 
96
        }
 
97
        if (snd_pcm_hw_params_set_format(ai->handle, hw, format) < 0) {
 
98
                fprintf(stderr, "initialize_device(): cannot set format %s\n", snd_pcm_format_name(format));
 
99
                return -1;
 
100
        }
 
101
        if (snd_pcm_hw_params_set_channels(ai->handle, hw, ai->channels) < 0) {
 
102
                fprintf(stderr, "initialize_device(): cannot set %d channels\n", ai->channels);
 
103
                return -1;
 
104
        }
 
105
        rate = ai->rate;
 
106
        if (snd_pcm_hw_params_set_rate_near(ai->handle, hw, &rate, NULL) < 0) {
 
107
                fprintf(stderr, "initialize_device(): cannot set rate %u\n", rate);
 
108
                return -1;
 
109
        }
 
110
        if (!rates_match(ai->rate, rate)) {
 
111
                fprintf(stderr, "initialize_device(): rate %ld not available, using %u\n", ai->rate, rate);
 
112
                /* return -1; */
 
113
        }
 
114
        buffer_size = rate * BUFFER_LENGTH;
 
115
        if (snd_pcm_hw_params_set_buffer_size_near(ai->handle, hw, &buffer_size) < 0) {
 
116
                fprintf(stderr, "initialize_device(): cannot set buffer size\n");
 
117
                return -1;
 
118
        }
 
119
        period_size = buffer_size / 4;
 
120
        if (snd_pcm_hw_params_set_period_size_near(ai->handle, hw, &period_size, NULL) < 0) {
 
121
                fprintf(stderr, "initialize_device(): cannot set period size\n");
 
122
                return -1;
 
123
        }
 
124
        if (snd_pcm_hw_params(ai->handle, hw) < 0) {
 
125
                fprintf(stderr, "initialize_device(): cannot set hw params\n");
 
126
                return -1;
 
127
        }
 
128
 
 
129
        snd_pcm_sw_params_alloca(&sw);
 
130
        if (snd_pcm_sw_params_current(ai->handle, sw) < 0) {
 
131
                fprintf(stderr, "initialize_device(): cannot get sw params\n");
 
132
                return -1;
 
133
        }
 
134
        /* start playing after the first write */
 
135
        if (snd_pcm_sw_params_set_start_threshold(ai->handle, sw, 1) < 0) {
 
136
                fprintf(stderr, "initialize_device(): cannot set start threshold\n");
 
137
                return -1;
 
138
        }
 
139
        if (snd_pcm_sw_params_get_boundary(sw, &boundary) < 0) {
 
140
                fprintf(stderr, "initialize_device(): cannot get boundary\n");
 
141
                return -1;
 
142
        }
 
143
        /* never stop on underruns */
 
144
        if (snd_pcm_sw_params_set_stop_threshold(ai->handle, sw, boundary) < 0) {
 
145
                fprintf(stderr, "initialize_device(): cannot set stop threshold\n");
 
146
                return -1;
 
147
        }
 
148
        /* wake up on every interrupt */
 
149
        if (snd_pcm_sw_params_set_avail_min(ai->handle, sw, 1) < 0) {
 
150
                fprintf(stderr, "initialize_device(): cannot set min avail\n");
 
151
                return -1;
 
152
        }
 
153
        /* always write as many frames as possible */
 
154
        if (snd_pcm_sw_params_set_xfer_align(ai->handle, sw, 1) < 0) {
 
155
                fprintf(stderr, "initialize_device(): cannot set transfer alignment\n");
 
156
                return -1;
 
157
        }
 
158
        /* play silence when there is an underrun */
 
159
        if (snd_pcm_sw_params_set_silence_size(ai->handle, sw, boundary) < 0) {
 
160
                fprintf(stderr, "initialize_device(): cannot set silence size\n");
 
161
                return -1;
 
162
        }
 
163
        if (snd_pcm_sw_params(ai->handle, sw) < 0) {
 
164
                fprintf(stderr, "initialize_device(): cannot set sw params\n");
 
165
                return -1;
 
166
        }
 
167
        return 0;
 
168
}
 
169
 
 
170
int audio_get_formats(struct audio_info_struct *ai)
 
171
{
 
172
        snd_pcm_hw_params_t *hw;
 
173
        unsigned int rate;
 
174
        int supported_formats, i;
 
175
 
 
176
        snd_pcm_hw_params_alloca(&hw);
 
177
        if (snd_pcm_hw_params_any(ai->handle, hw) < 0) {
 
178
                fprintf(stderr, "audio_get_formats(): no configuration available\n");
 
179
                return -1;
 
180
        }
 
181
        if (snd_pcm_hw_params_set_access(ai->handle, hw, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
 
182
                return -1;
 
183
        if (snd_pcm_hw_params_set_channels(ai->handle, hw, ai->channels) < 0)
 
184
                return 0;
 
185
        rate = ai->rate;
 
186
        if (snd_pcm_hw_params_set_rate_near(ai->handle, hw, &rate, NULL) < 0)
 
187
                return -1;
 
188
        if (!rates_match(ai->rate, rate))
 
189
                return 0;
 
190
        supported_formats = 0;
 
191
        for (i = 0; i < NUM_FORMATS; ++i) {
 
192
                if (snd_pcm_hw_params_test_format(ai->handle, hw, format_map[i].alsa) == 0)
 
193
                        supported_formats |= format_map[i].mpg123;
 
194
        }
 
195
        return supported_formats;
 
196
}
 
197
 
 
198
int audio_play_samples(struct audio_info_struct *ai, unsigned char *buf, int bytes)
 
199
{
 
200
        snd_pcm_uframes_t frames;
 
201
        snd_pcm_sframes_t written;
 
202
 
 
203
#if SND_LIB_VERSION >= 0x000901
 
204
        snd_pcm_sframes_t delay;
 
205
        if (snd_pcm_delay(ai->handle, &delay) >= 0 && delay < 0)
 
206
                /* underrun - move the application pointer forward to catch up */
 
207
                snd_pcm_forward(ai->handle, -delay);
 
208
#endif
 
209
        frames = snd_pcm_bytes_to_frames(ai->handle, bytes);
 
210
        written = snd_pcm_writei(ai->handle, buf, frames);
 
211
        if (written >= 0)
 
212
                return snd_pcm_frames_to_bytes(ai->handle, written);
 
213
        else
 
214
                return written;
 
215
}
 
216
 
 
217
void audio_queueflush(struct audio_info_struct *ai)
 
218
{
 
219
        /* is this the optimal solution? - we should figure out what we really whant from this function */
 
220
        snd_pcm_drop(ai->handle);
 
221
        snd_pcm_prepare(ai->handle);
 
222
}
 
223
 
 
224
int audio_close(struct audio_info_struct *ai)
 
225
{
 
226
        if(ai->handle != NULL) /* be really generous for being called without any device opening */
 
227
        {
 
228
                if (snd_pcm_state(ai->handle) == SND_PCM_STATE_RUNNING)
 
229
                        snd_pcm_drain(ai->handle);
 
230
                return snd_pcm_close(ai->handle);
 
231
        }
 
232
        else return 0;
 
233
}