Mercurial > mplayer.hg
comparison libao2/ao_openal.c @ 17633:e0ef5688cce7
OpenAL audio support, actual output is mono-only (no positioning yet).
author | reimar |
---|---|
date | Thu, 16 Feb 2006 20:45:25 +0000 |
parents | |
children | 87c049a38c36 |
comparison
equal
deleted
inserted
replaced
17632:3a67b7ce8b3b | 17633:e0ef5688cce7 |
---|---|
1 /* | |
2 * ao_openal.c - OpenAL audio output driver for MPlayer | |
3 * | |
4 * This driver is under the same license as MPlayer. | |
5 * (http://www.mplayerhq.hu) | |
6 * | |
7 * Copyleft 2006 by Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.de) | |
8 */ | |
9 | |
10 #include <inttypes.h> | |
11 #include <AL/alc.h> | |
12 #include <AL/al.h> | |
13 | |
14 #include "config.h" | |
15 #include "mp_msg.h" | |
16 #include "help_mp.h" | |
17 | |
18 #include "audio_out.h" | |
19 #include "audio_out_internal.h" | |
20 #include "libaf/af_format.h" | |
21 #include "osdep/timer.h" | |
22 #include "subopt-helper.h" | |
23 | |
24 static ao_info_t info = | |
25 { | |
26 "OpenAL audio output", | |
27 "openal", | |
28 "Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>", | |
29 "" | |
30 }; | |
31 | |
32 LIBAO_EXTERN(openal) | |
33 | |
34 #define MAX_CHANS 6 | |
35 #define NUM_BUF 128 | |
36 #define CHUNK_SIZE 512 | |
37 static ALuint buffers[MAX_CHANS][NUM_BUF]; | |
38 static ALuint sources[MAX_CHANS]; | |
39 | |
40 static int cur_buf[MAX_CHANS]; | |
41 static int unqueue_buf[MAX_CHANS]; | |
42 static int16_t *tmpbuf; | |
43 | |
44 | |
45 static int control(int cmd, void *arg) { | |
46 return CONTROL_UNKNOWN; | |
47 } | |
48 | |
49 /** | |
50 * \brief print suboption usage help | |
51 */ | |
52 static void print_help(void) { | |
53 mp_msg(MSGT_AO, MSGL_FATAL, | |
54 "\n-ao openal commandline help:\n" | |
55 "Example: mplayer -ao openal\n" | |
56 "\nOptions:\n" | |
57 ); | |
58 } | |
59 | |
60 static int init(int rate, int channels, int format, int flags) { | |
61 ALCdevice *dev = NULL; | |
62 ALCcontext *ctx = NULL; | |
63 ALint bufrate; | |
64 int i; | |
65 opt_t subopts[] = { | |
66 {NULL} | |
67 }; | |
68 if (subopt_parse(ao_subdevice, subopts) != 0) { | |
69 print_help(); | |
70 return 0; | |
71 } | |
72 if (channels > MAX_CHANS) { | |
73 mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Invalid number of channels: %i\n", channels); | |
74 goto err_out; | |
75 } | |
76 dev = alcOpenDevice(NULL); | |
77 if (!dev) { | |
78 mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] could not open device\n"); | |
79 goto err_out; | |
80 } | |
81 ctx = alcCreateContext(dev, NULL); | |
82 alcMakeContextCurrent(ctx); | |
83 for (i = 0; i < channels; i++) { | |
84 cur_buf[i] = 0; | |
85 unqueue_buf[i] = 0; | |
86 alGenBuffers(NUM_BUF, buffers[i]); | |
87 } | |
88 alGenSources(channels, sources); | |
89 alSource3f(sources[0], AL_POSITION, 0, 0, 10); | |
90 ao_data.channels = channels; | |
91 alGetBufferi(buffers[0][0], AL_FREQUENCY, &bufrate); | |
92 ao_data.samplerate = rate = bufrate; | |
93 ao_data.format = AF_FORMAT_S16_NE; | |
94 ao_data.bps = channels * rate * 2; | |
95 ao_data.buffersize = CHUNK_SIZE * NUM_BUF; | |
96 ao_data.outburst = channels * CHUNK_SIZE; | |
97 tmpbuf = (int16_t *)malloc(CHUNK_SIZE); | |
98 return 1; | |
99 | |
100 err_out: | |
101 return 0; | |
102 } | |
103 | |
104 // close audio device | |
105 static void uninit(int immed) { | |
106 ALCcontext *ctx = alcGetCurrentContext(); | |
107 ALCdevice *dev = alcGetContextsDevice(ctx); | |
108 free(tmpbuf); | |
109 if (!immed) { | |
110 ALint state; | |
111 alGetSourcei(sources[0], AL_SOURCE_STATE, &state); | |
112 while (state == AL_PLAYING) { | |
113 usec_sleep(10000); | |
114 alGetSourcei(sources[0], AL_SOURCE_STATE, &state); | |
115 } | |
116 } | |
117 reset(); | |
118 alcMakeContextCurrent(NULL); | |
119 alcDestroyContext(ctx); | |
120 alcCloseDevice(dev); | |
121 } | |
122 | |
123 static void unqueue_buffers(void) { | |
124 ALint p; | |
125 int s, i; | |
126 for (s = 0; s < ao_data.channels; s++) { | |
127 alGetSourcei(sources[s], AL_BUFFERS_PROCESSED, &p); | |
128 for (i = 0; i < p; i++) { | |
129 alSourceUnqueueBuffers(sources[s], 1, &buffers[s][unqueue_buf[s]]); | |
130 unqueue_buf[s] = (unqueue_buf[s] + 1) % NUM_BUF; | |
131 } | |
132 } | |
133 } | |
134 | |
135 /** | |
136 * \brief stop playing and empty buffers (for seeking/pause) | |
137 */ | |
138 static void reset(void) { | |
139 alSourceRewindv(ao_data.channels, sources); | |
140 unqueue_buffers(); | |
141 } | |
142 | |
143 /** | |
144 * \brief stop playing, keep buffers (for pause) | |
145 */ | |
146 static void audio_pause(void) { | |
147 alSourcePausev(ao_data.channels, sources); | |
148 } | |
149 | |
150 /** | |
151 * \brief resume playing, after audio_pause() | |
152 */ | |
153 static void audio_resume(void) { | |
154 alSourcePlayv(ao_data.channels, sources); | |
155 } | |
156 | |
157 static int get_space(void) { | |
158 ALint queued; | |
159 unqueue_buffers(); | |
160 alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued); | |
161 return (NUM_BUF - queued) * CHUNK_SIZE * ao_data.channels; | |
162 } | |
163 | |
164 /** | |
165 * \brief write data into buffer and reset underrun flag | |
166 */ | |
167 static int play(void *data, int len, int flags) { | |
168 ALint state; | |
169 int i, j, k; | |
170 int ch; | |
171 int16_t *d = data; | |
172 len /= ao_data.outburst; | |
173 for (i = 0; i < len; i++) { | |
174 for (ch = 0; ch < ao_data.channels; ch++) { | |
175 for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao_data.channels) | |
176 tmpbuf[j] = d[k]; | |
177 alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, tmpbuf, | |
178 CHUNK_SIZE, ao_data.samplerate); | |
179 alSourceQueueBuffers(sources[ch], 1, &buffers[ch][cur_buf[ch]]); | |
180 cur_buf[ch] = (cur_buf[ch] + 1) % NUM_BUF; | |
181 } | |
182 d += ao_data.channels * CHUNK_SIZE / 2; | |
183 } | |
184 alGetSourcei(sources[0], AL_SOURCE_STATE, &state); | |
185 if (state != AL_PLAYING) // checked here in case of an underrun | |
186 alSourcePlayv(ao_data.channels, sources); | |
187 return len * ao_data.outburst; | |
188 } | |
189 | |
190 static float get_delay(void) { | |
191 ALint queued; | |
192 unqueue_buffers(); | |
193 alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued); | |
194 return queued * CHUNK_SIZE / 2 / (float)ao_data.samplerate; | |
195 } | |
196 |