2 * OpenAL audio output driver for MPlayer
4 * Copyleft 2006 by Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.de)
6 * This file is part of MPlayer.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * along with MPlayer; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <OpenAL/alc.h>
30 #include <OpenAL/al.h>
38 #include "audio_out.h"
39 #include "audio_out_internal.h"
40 #include "libaf/af_format.h"
41 #include "osdep/timer.h"
42 #include "subopt-helper.h"
44 static const ao_info_t info
=
46 "OpenAL audio output",
48 "Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>",
56 #define CHUNK_SIZE 512
57 static ALuint buffers
[MAX_CHANS
][NUM_BUF
];
58 static ALuint sources
[MAX_CHANS
];
60 static int cur_buf
[MAX_CHANS
];
61 static int unqueue_buf
[MAX_CHANS
];
62 static int16_t *tmpbuf
;
65 static int control(int cmd
, void *arg
) {
67 case AOCONTROL_GET_VOLUME
:
68 case AOCONTROL_SET_VOLUME
: {
70 ao_control_vol_t
*vol
= (ao_control_vol_t
*)arg
;
71 if (cmd
== AOCONTROL_SET_VOLUME
) {
72 volume
= (vol
->left
+ vol
->right
) / 200.0;
73 alListenerf(AL_GAIN
, volume
);
75 alGetListenerf(AL_GAIN
, &volume
);
76 vol
->left
= vol
->right
= volume
* 100;
80 return CONTROL_UNKNOWN
;
84 * \brief print suboption usage help
86 static void print_help(void) {
87 mp_msg(MSGT_AO
, MSGL_FATAL
,
88 "\n-ao openal commandline help:\n"
89 "Example: mplayer -ao openal\n"
94 static int init(int rate
, int channels
, int format
, int flags
) {
95 float position
[3] = {0, 0, 0};
96 float direction
[6] = {0, 0, 1, 0, -1, 0};
97 float sppos
[MAX_CHANS
][3] = {
98 {-1, 0, 0.5}, {1, 0, 0.5},
99 {-1, 0, -1}, {1, 0, -1},
100 {0, 0, 1}, {0, 0, 0.1},
101 {-1, 0, 0}, {1, 0, 0},
103 ALCdevice
*dev
= NULL
;
104 ALCcontext
*ctx
= NULL
;
106 ALCint attribs
[] = {ALC_FREQUENCY
, rate
, 0, 0};
108 const opt_t subopts
[] = {
111 if (subopt_parse(ao_subdevice
, subopts
) != 0) {
115 if (channels
> MAX_CHANS
) {
116 mp_msg(MSGT_AO
, MSGL_FATAL
, "[OpenAL] Invalid number of channels: %i\n", channels
);
119 dev
= alcOpenDevice(NULL
);
121 mp_msg(MSGT_AO
, MSGL_FATAL
, "[OpenAL] could not open device\n");
124 ctx
= alcCreateContext(dev
, attribs
);
125 alcMakeContextCurrent(ctx
);
126 alListenerfv(AL_POSITION
, position
);
127 alListenerfv(AL_ORIENTATION
, direction
);
128 alGenSources(channels
, sources
);
129 for (i
= 0; i
< channels
; i
++) {
132 alGenBuffers(NUM_BUF
, buffers
[i
]);
133 alSourcefv(sources
[i
], AL_POSITION
, sppos
[i
]);
134 alSource3f(sources
[i
], AL_VELOCITY
, 0, 0, 0);
137 alSource3f(sources
[0], AL_POSITION
, 0, 0, 1);
138 ao_data
.channels
= channels
;
139 alcGetIntegerv(dev
, ALC_FREQUENCY
, 1, &freq
);
140 if (alcGetError(dev
) == ALC_NO_ERROR
&& freq
)
142 ao_data
.samplerate
= rate
;
143 ao_data
.format
= AF_FORMAT_S16_NE
;
144 ao_data
.bps
= channels
* rate
* 2;
145 ao_data
.buffersize
= CHUNK_SIZE
* NUM_BUF
;
146 ao_data
.outburst
= channels
* CHUNK_SIZE
;
147 tmpbuf
= malloc(CHUNK_SIZE
);
154 // close audio device
155 static void uninit(int immed
) {
156 ALCcontext
*ctx
= alcGetCurrentContext();
157 ALCdevice
*dev
= alcGetContextsDevice(ctx
);
161 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
162 while (state
== AL_PLAYING
) {
164 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
168 alcMakeContextCurrent(NULL
);
169 alcDestroyContext(ctx
);
173 static void unqueue_buffers(void) {
176 for (s
= 0; s
< ao_data
.channels
; s
++) {
177 int till_wrap
= NUM_BUF
- unqueue_buf
[s
];
178 alGetSourcei(sources
[s
], AL_BUFFERS_PROCESSED
, &p
);
179 if (p
>= till_wrap
) {
180 alSourceUnqueueBuffers(sources
[s
], till_wrap
, &buffers
[s
][unqueue_buf
[s
]]);
185 alSourceUnqueueBuffers(sources
[s
], p
, &buffers
[s
][unqueue_buf
[s
]]);
192 * \brief stop playing and empty buffers (for seeking/pause)
194 static void reset(void) {
195 alSourceStopv(ao_data
.channels
, sources
);
200 * \brief stop playing, keep buffers (for pause)
202 static void audio_pause(void) {
203 alSourcePausev(ao_data
.channels
, sources
);
207 * \brief resume playing, after audio_pause()
209 static void audio_resume(void) {
210 alSourcePlayv(ao_data
.channels
, sources
);
213 static int get_space(void) {
216 alGetSourcei(sources
[0], AL_BUFFERS_QUEUED
, &queued
);
217 queued
= NUM_BUF
- queued
- 3;
218 if (queued
< 0) return 0;
219 return queued
* CHUNK_SIZE
* ao_data
.channels
;
223 * \brief write data into buffer and reset underrun flag
225 static int play(void *data
, int len
, int flags
) {
230 len
/= ao_data
.channels
* CHUNK_SIZE
;
231 for (i
= 0; i
< len
; i
++) {
232 for (ch
= 0; ch
< ao_data
.channels
; ch
++) {
233 for (j
= 0, k
= ch
; j
< CHUNK_SIZE
/ 2; j
++, k
+= ao_data
.channels
)
235 alBufferData(buffers
[ch
][cur_buf
[ch
]], AL_FORMAT_MONO16
, tmpbuf
,
236 CHUNK_SIZE
, ao_data
.samplerate
);
237 alSourceQueueBuffers(sources
[ch
], 1, &buffers
[ch
][cur_buf
[ch
]]);
238 cur_buf
[ch
] = (cur_buf
[ch
] + 1) % NUM_BUF
;
240 d
+= ao_data
.channels
* CHUNK_SIZE
/ 2;
242 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
243 if (state
!= AL_PLAYING
) // checked here in case of an underrun
244 alSourcePlayv(ao_data
.channels
, sources
);
245 return len
* ao_data
.channels
* CHUNK_SIZE
;
248 static float get_delay(void) {
251 alGetSourcei(sources
[0], AL_BUFFERS_QUEUED
, &queued
);
252 return queued
* CHUNK_SIZE
/ 2 / (float)ao_data
.samplerate
;