2 * ao_openal.c - OpenAL audio output driver for MPlayer
4 * This driver is under the same license as MPlayer.
5 * (http://www.mplayerhq.hu)
7 * Copyleft 2006 by Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.de)
16 #include <OpenAL/alc.h>
17 #include <OpenAL/al.h>
26 #include "audio_out.h"
27 #include "audio_out_internal.h"
28 #include "libaf/af_format.h"
29 #include "osdep/timer.h"
30 #include "subopt-helper.h"
32 static ao_info_t info
=
34 "OpenAL audio output",
36 "Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>",
44 #define CHUNK_SIZE 512
45 static ALuint buffers
[MAX_CHANS
][NUM_BUF
];
46 static ALuint sources
[MAX_CHANS
];
48 static int cur_buf
[MAX_CHANS
];
49 static int unqueue_buf
[MAX_CHANS
];
50 static int16_t *tmpbuf
;
53 static int control(int cmd
, void *arg
) {
55 case AOCONTROL_GET_VOLUME
:
56 case AOCONTROL_SET_VOLUME
: {
58 ao_control_vol_t
*vol
= (ao_control_vol_t
*)arg
;
59 if (cmd
== AOCONTROL_SET_VOLUME
) {
60 volume
= (vol
->left
+ vol
->right
) / 200.0;
61 alListenerf(AL_GAIN
, volume
);
63 alGetListenerf(AL_GAIN
, &volume
);
64 vol
->left
= vol
->right
= volume
* 100;
68 return CONTROL_UNKNOWN
;
72 * \brief print suboption usage help
74 static void print_help(void) {
75 mp_msg(MSGT_AO
, MSGL_FATAL
,
76 "\n-ao openal commandline help:\n"
77 "Example: mplayer -ao openal\n"
82 static int init(int rate
, int channels
, int format
, int flags
) {
83 float position
[3] = {0, 0, 0};
84 float direction
[6] = {0, 0, 1, 0, -1, 0};
86 {-1, 0, 0.5}, {1, 0, 0.5},
87 {-1, 0, -1}, {1, 0, -1},
88 {0, 0, 1}, {0, 0, 0.1},
90 ALCdevice
*dev
= NULL
;
91 ALCcontext
*ctx
= NULL
;
93 ALCint attribs
[] = {ALC_FREQUENCY
, rate
, 0, 0};
98 if (subopt_parse(ao_subdevice
, subopts
) != 0) {
102 if (channels
> MAX_CHANS
) {
103 mp_msg(MSGT_AO
, MSGL_FATAL
, "[OpenAL] Invalid number of channels: %i\n", channels
);
106 dev
= alcOpenDevice(NULL
);
108 mp_msg(MSGT_AO
, MSGL_FATAL
, "[OpenAL] could not open device\n");
111 ctx
= alcCreateContext(dev
, attribs
);
112 alcMakeContextCurrent(ctx
);
113 alListenerfv(AL_POSITION
, position
);
114 alListenerfv(AL_ORIENTATION
, direction
);
115 alGenSources(channels
, sources
);
116 for (i
= 0; i
< channels
; i
++) {
119 alGenBuffers(NUM_BUF
, buffers
[i
]);
120 alSourcefv(sources
[i
], AL_POSITION
, sppos
[i
]);
121 alSource3f(sources
[i
], AL_VELOCITY
, 0, 0, 0);
124 alSource3f(sources
[0], AL_POSITION
, 0, 0, 1);
125 ao_data
.channels
= channels
;
126 alcGetIntegerv(dev
, ALC_FREQUENCY
, 1, &freq
);
127 if (alcGetError(dev
) == ALC_NO_ERROR
&& freq
)
129 ao_data
.samplerate
= rate
;
130 ao_data
.format
= AF_FORMAT_S16_NE
;
131 ao_data
.bps
= channels
* rate
* 2;
132 ao_data
.buffersize
= CHUNK_SIZE
* NUM_BUF
;
133 ao_data
.outburst
= channels
* CHUNK_SIZE
;
134 tmpbuf
= malloc(CHUNK_SIZE
);
141 // close audio device
142 static void uninit(int immed
) {
143 ALCcontext
*ctx
= alcGetCurrentContext();
144 ALCdevice
*dev
= alcGetContextsDevice(ctx
);
148 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
149 while (state
== AL_PLAYING
) {
151 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
155 alcMakeContextCurrent(NULL
);
156 alcDestroyContext(ctx
);
160 static void unqueue_buffers(void) {
163 for (s
= 0; s
< ao_data
.channels
; s
++) {
164 alGetSourcei(sources
[s
], AL_BUFFERS_PROCESSED
, &p
);
165 for (i
= 0; i
< p
; i
++) {
166 alSourceUnqueueBuffers(sources
[s
], 1, &buffers
[s
][unqueue_buf
[s
]]);
167 unqueue_buf
[s
] = (unqueue_buf
[s
] + 1) % NUM_BUF
;
173 * \brief stop playing and empty buffers (for seeking/pause)
175 static void reset(void) {
176 alSourceRewindv(ao_data
.channels
, sources
);
181 * \brief stop playing, keep buffers (for pause)
183 static void audio_pause(void) {
184 alSourcePausev(ao_data
.channels
, sources
);
188 * \brief resume playing, after audio_pause()
190 static void audio_resume(void) {
191 alSourcePlayv(ao_data
.channels
, sources
);
194 static int get_space(void) {
197 alGetSourcei(sources
[0], AL_BUFFERS_QUEUED
, &queued
);
198 return (NUM_BUF
- queued
) * CHUNK_SIZE
* ao_data
.channels
;
202 * \brief write data into buffer and reset underrun flag
204 static int play(void *data
, int len
, int flags
) {
209 len
/= ao_data
.outburst
;
210 for (i
= 0; i
< len
; i
++) {
211 for (ch
= 0; ch
< ao_data
.channels
; ch
++) {
212 for (j
= 0, k
= ch
; j
< CHUNK_SIZE
/ 2; j
++, k
+= ao_data
.channels
)
214 alBufferData(buffers
[ch
][cur_buf
[ch
]], AL_FORMAT_MONO16
, tmpbuf
,
215 CHUNK_SIZE
, ao_data
.samplerate
);
216 alSourceQueueBuffers(sources
[ch
], 1, &buffers
[ch
][cur_buf
[ch
]]);
217 cur_buf
[ch
] = (cur_buf
[ch
] + 1) % NUM_BUF
;
219 d
+= ao_data
.channels
* CHUNK_SIZE
/ 2;
221 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
222 if (state
!= AL_PLAYING
) // checked here in case of an underrun
223 alSourcePlayv(ao_data
.channels
, sources
);
224 return len
* ao_data
.outburst
;
227 static float get_delay(void) {
230 alGetSourcei(sources
[0], AL_BUFFERS_QUEUED
, &queued
);
231 return queued
* CHUNK_SIZE
/ 2 / (float)ao_data
.samplerate
;