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)
20 #include "audio_out.h"
21 #include "audio_out_internal.h"
22 #include "libaf/af_format.h"
23 #include "osdep/timer.h"
24 #include "subopt-helper.h"
26 static ao_info_t info
=
28 "OpenAL audio output",
30 "Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>",
38 #define CHUNK_SIZE 512
39 static ALuint buffers
[MAX_CHANS
][NUM_BUF
];
40 static ALuint sources
[MAX_CHANS
];
42 static int cur_buf
[MAX_CHANS
];
43 static int unqueue_buf
[MAX_CHANS
];
44 static int16_t *tmpbuf
;
47 static int control(int cmd
, void *arg
) {
48 return CONTROL_UNKNOWN
;
52 * \brief print suboption usage help
54 static void print_help(void) {
55 mp_msg(MSGT_AO
, MSGL_FATAL
,
56 "\n-ao openal commandline help:\n"
57 "Example: mplayer -ao openal\n"
62 static int init(int rate
, int channels
, int format
, int flags
) {
63 ALCdevice
*dev
= NULL
;
64 ALCcontext
*ctx
= NULL
;
70 if (subopt_parse(ao_subdevice
, subopts
) != 0) {
74 if (channels
> MAX_CHANS
) {
75 mp_msg(MSGT_AO
, MSGL_FATAL
, "[OpenAL] Invalid number of channels: %i\n", channels
);
78 dev
= alcOpenDevice(NULL
);
80 mp_msg(MSGT_AO
, MSGL_FATAL
, "[OpenAL] could not open device\n");
83 ctx
= alcCreateContext(dev
, NULL
);
84 alcMakeContextCurrent(ctx
);
85 for (i
= 0; i
< channels
; i
++) {
88 alGenBuffers(NUM_BUF
, buffers
[i
]);
90 alGenSources(channels
, sources
);
91 alSource3f(sources
[0], AL_POSITION
, 0, 0, 10);
92 ao_data
.channels
= channels
;
93 alGetBufferi(buffers
[0][0], AL_FREQUENCY
, &bufrate
);
94 ao_data
.samplerate
= rate
= bufrate
;
95 ao_data
.format
= AF_FORMAT_S16_NE
;
96 ao_data
.bps
= channels
* rate
* 2;
97 ao_data
.buffersize
= CHUNK_SIZE
* NUM_BUF
;
98 ao_data
.outburst
= channels
* CHUNK_SIZE
;
99 tmpbuf
= malloc(CHUNK_SIZE
);
106 // close audio device
107 static void uninit(int immed
) {
108 ALCcontext
*ctx
= alcGetCurrentContext();
109 ALCdevice
*dev
= alcGetContextsDevice(ctx
);
113 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
114 while (state
== AL_PLAYING
) {
116 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
120 alcMakeContextCurrent(NULL
);
121 alcDestroyContext(ctx
);
125 static void unqueue_buffers(void) {
128 for (s
= 0; s
< ao_data
.channels
; s
++) {
129 alGetSourcei(sources
[s
], AL_BUFFERS_PROCESSED
, &p
);
130 for (i
= 0; i
< p
; i
++) {
131 alSourceUnqueueBuffers(sources
[s
], 1, &buffers
[s
][unqueue_buf
[s
]]);
132 unqueue_buf
[s
] = (unqueue_buf
[s
] + 1) % NUM_BUF
;
138 * \brief stop playing and empty buffers (for seeking/pause)
140 static void reset(void) {
141 alSourceRewindv(ao_data
.channels
, sources
);
146 * \brief stop playing, keep buffers (for pause)
148 static void audio_pause(void) {
149 alSourcePausev(ao_data
.channels
, sources
);
153 * \brief resume playing, after audio_pause()
155 static void audio_resume(void) {
156 alSourcePlayv(ao_data
.channels
, sources
);
159 static int get_space(void) {
162 alGetSourcei(sources
[0], AL_BUFFERS_QUEUED
, &queued
);
163 return (NUM_BUF
- queued
) * CHUNK_SIZE
* ao_data
.channels
;
167 * \brief write data into buffer and reset underrun flag
169 static int play(void *data
, int len
, int flags
) {
174 len
/= ao_data
.outburst
;
175 for (i
= 0; i
< len
; i
++) {
176 for (ch
= 0; ch
< ao_data
.channels
; ch
++) {
177 for (j
= 0, k
= ch
; j
< CHUNK_SIZE
/ 2; j
++, k
+= ao_data
.channels
)
179 alBufferData(buffers
[ch
][cur_buf
[ch
]], AL_FORMAT_MONO16
, tmpbuf
,
180 CHUNK_SIZE
, ao_data
.samplerate
);
181 alSourceQueueBuffers(sources
[ch
], 1, &buffers
[ch
][cur_buf
[ch
]]);
182 cur_buf
[ch
] = (cur_buf
[ch
] + 1) % NUM_BUF
;
184 d
+= ao_data
.channels
* CHUNK_SIZE
/ 2;
186 alGetSourcei(sources
[0], AL_SOURCE_STATE
, &state
);
187 if (state
!= AL_PLAYING
) // checked here in case of an underrun
188 alSourcePlayv(ao_data
.channels
, sources
);
189 return len
* ao_data
.outburst
;
192 static float get_delay(void) {
195 alGetSourcei(sources
[0], AL_BUFFERS_QUEUED
, &queued
);
196 return queued
* CHUNK_SIZE
/ 2 / (float)ao_data
.samplerate
;