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 global_ao
->no_persistent_volume
= true;
112 if (subopt_parse(ao_subdevice
, subopts
) != 0) {
116 if (channels
> MAX_CHANS
) {
117 mp_msg(MSGT_AO
, MSGL_FATAL
, "[OpenAL] Invalid number of channels: %i\n", channels
);
120 dev
= alcOpenDevice(NULL
);
122 mp_msg(MSGT_AO
, MSGL_FATAL
, "[OpenAL] could not open device\n");
125 ctx
= alcCreateContext(dev
, attribs
);
126 alcMakeContextCurrent(ctx
);
127 alListenerfv(AL_POSITION
, position
);
128 alListenerfv(AL_ORIENTATION
, direction
);
129 alGenSources(channels
, sources
);
130 for (i
= 0; i
< channels
; i
++) {
133 alGenBuffers(NUM_BUF
, buffers
[i
]);
134 alSourcefv(sources
[i
], AL_POSITION
, sppos
[i
]);
135 alSource3f(sources
[i
], AL_VELOCITY
, 0, 0, 0);
138 alSource3f(sources
[0], AL_POSITION
, 0, 0, 1);
139 ao_data
.channels
= channels
;
140 alcGetIntegerv(dev
, ALC_FREQUENCY
, 1, &freq
);
141 if (alcGetError(dev
) == ALC_NO_ERROR
&& freq
)
143 ao_data
.samplerate
= rate
;
144 ao_data
.format
= AF_FORMAT_S16_NE
;
145 ao_data
.bps
= channels
* rate
* 2;
146 ao_data
.buffersize
= CHUNK_SIZE
* NUM_BUF
;
147 ao_data
.outburst
= channels
* CHUNK_SIZE
;
148 tmpbuf
= malloc(CHUNK_SIZE
);
155 // close audio device
156 static void uninit(int immed
) {
157 ALCcontext
*ctx
= alcGetCurrentContext();
158 ALCdevice
*dev
= alcGetContextsDevice(ctx
);
162 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
163 while (state
== AL_PLAYING
) {
165 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
169 alcMakeContextCurrent(NULL
);
170 alcDestroyContext(ctx
);
174 static void unqueue_buffers(void) {
177 for (s
= 0; s
< ao_data
.channels
; s
++) {
178 int till_wrap
= NUM_BUF
- unqueue_buf
[s
];
179 alGetSourcei(sources
[s
], AL_BUFFERS_PROCESSED
, &p
);
180 if (p
>= till_wrap
) {
181 alSourceUnqueueBuffers(sources
[s
], till_wrap
, &buffers
[s
][unqueue_buf
[s
]]);
186 alSourceUnqueueBuffers(sources
[s
], p
, &buffers
[s
][unqueue_buf
[s
]]);
193 * \brief stop playing and empty buffers (for seeking/pause)
195 static void reset(void) {
196 alSourceStopv(ao_data
.channels
, sources
);
201 * \brief stop playing, keep buffers (for pause)
203 static void audio_pause(void) {
204 alSourcePausev(ao_data
.channels
, sources
);
208 * \brief resume playing, after audio_pause()
210 static void audio_resume(void) {
211 alSourcePlayv(ao_data
.channels
, sources
);
214 static int get_space(void) {
217 alGetSourcei(sources
[0], AL_BUFFERS_QUEUED
, &queued
);
218 queued
= NUM_BUF
- queued
- 3;
219 if (queued
< 0) return 0;
220 return queued
* CHUNK_SIZE
* ao_data
.channels
;
224 * \brief write data into buffer and reset underrun flag
226 static int play(void *data
, int len
, int flags
) {
231 len
/= ao_data
.channels
* CHUNK_SIZE
;
232 for (i
= 0; i
< len
; i
++) {
233 for (ch
= 0; ch
< ao_data
.channels
; ch
++) {
234 for (j
= 0, k
= ch
; j
< CHUNK_SIZE
/ 2; j
++, k
+= ao_data
.channels
)
236 alBufferData(buffers
[ch
][cur_buf
[ch
]], AL_FORMAT_MONO16
, tmpbuf
,
237 CHUNK_SIZE
, ao_data
.samplerate
);
238 alSourceQueueBuffers(sources
[ch
], 1, &buffers
[ch
][cur_buf
[ch
]]);
239 cur_buf
[ch
] = (cur_buf
[ch
] + 1) % NUM_BUF
;
241 d
+= ao_data
.channels
* CHUNK_SIZE
/ 2;
243 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
244 if (state
!= AL_PLAYING
) // checked here in case of an underrun
245 alSourcePlayv(ao_data
.channels
, sources
);
246 return len
* ao_data
.channels
* CHUNK_SIZE
;
249 static float get_delay(void) {
252 alGetSourcei(sources
[0], AL_BUFFERS_QUEUED
, &queued
);
253 return queued
* CHUNK_SIZE
/ 2 / (float)ao_data
.samplerate
;