1
// Copyright (c) 2007, Niels Martin Hansen
2
// All rights reserved.
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are met:
7
// * Redistributions of source code must retain the above copyright notice,
8
// this list of conditions and the following disclaimer.
9
// * Redistributions in binary form must reproduce the above copyright notice,
10
// this list of conditions and the following disclaimer in the documentation
11
// and/or other materials provided with the distribution.
12
// * Neither the name of the Aegisub Group nor the names of its contributors
13
// may be used to endorse or promote products derived from this software
14
// without specific prior written permission.
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
// POSSIBILITY OF SUCH DAMAGE.
28
// -----------------------------------------------------------------------------
32
// Website: http://aegisub.cellosoft.com
33
// Contact: mailto:jiifurusu@gmail.com
44
#include <wx/wxprec.h>
45
#include "audio_player_manager.h"
46
#include "audio_provider_manager.h"
49
#include "frame_main.h"
50
#include "audio_player_openal.h"
56
#elif defined(__APPLE__)
57
#include <OpenAL/AL.h>
58
#include <OpenAL/ALC.h>
65
// Auto-link to OpenAL lib for MSVC
67
#pragma comment(lib, "openal32.lib")
73
OpenALPlayer::OpenALPlayer()
78
start_frame = cur_frame = end_frame = bpf = 0;
85
OpenALPlayer::~OpenALPlayer()
93
void OpenALPlayer::OpenStream()
98
provider = GetProvider();
99
bpf = provider->GetChannels() * provider->GetBytesPerSample();
102
device = alcOpenDevice(0);
104
throw _T("Failed opening default OpenAL device");
108
context = alcCreateContext(device, 0);
110
alcCloseDevice(device);
111
throw _T("Failed creating OpenAL context");
113
if (!alcMakeContextCurrent(context)) {
114
alcDestroyContext(context);
115
alcCloseDevice(device);
116
throw _T("Failed selecting OpenAL context");
122
alGenBuffers(num_buffers, buffers);
123
if (alGetError() != AL_NO_ERROR) {
124
alcDestroyContext(context);
125
alcCloseDevice(device);
126
throw _T("Error generating OpenAL buffers");
130
alGenSources(1, &source);
131
if (alGetError() != AL_NO_ERROR) {
132
alDeleteBuffers(num_buffers, buffers);
133
alcDestroyContext(context);
134
alcCloseDevice(device);
135
throw _T("Error generating OpenAL source");
138
// Determine buffer length
139
samplerate = provider->GetSampleRate();
140
buffer_length = samplerate / num_buffers / 2; // buffers for half a second of audio
149
void OpenALPlayer::CloseStream()
155
alDeleteSources(1, &source);
156
alDeleteBuffers(num_buffers, buffers);
157
alcDestroyContext(context);
158
alcCloseDevice(device);
167
void OpenALPlayer::Play(int64_t start,int64_t count)
172
alSourceStop(source);
173
alSourcei(source, AL_BUFFER, 0);
179
end_frame = start + count;
183
buffers_free = num_buffers;
186
buf_first_queued = 0;
187
FillBuffers(num_buffers);
190
alSourcePlay(source);
192
playback_segment_timer.Start();
195
if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
201
void OpenALPlayer::Stop(bool timerToo)
204
if (!playing) return;
213
// Then drop the playback
214
alSourceStop(source);
215
alSourcei(source, AL_BUFFER, 0);
217
if (timerToo && displayTimer) {
218
displayTimer->Stop();
223
void OpenALPlayer::FillBuffers(ALsizei count)
225
if (count > buffers_free) count = buffers_free;
226
if (count < 1) count = 1;
228
// Get memory to hold sound buffers
229
void *data = malloc(buffer_length * bpf);
231
// Do the actual filling/queueing
232
ALuint bufid = buf_first_free;
234
ALsizei fill_len = buffer_length;
235
if (fill_len > (ALsizei)(end_frame - cur_frame)) fill_len = (ALsizei)(end_frame - cur_frame);
236
if (fill_len < 0) fill_len = 0;
239
// Get fill_len frames of audio
240
provider->GetAudioWithVolume(data, cur_frame, fill_len, volume);
241
if (fill_len < buffer_length)
242
// And zerofill the rest
243
memset((char*)data+(fill_len*bpf), 0, (buffer_length-fill_len)*bpf);
245
cur_frame += fill_len;
247
alBufferData(buffers[bufid], AL_FORMAT_MONO16, data, buffer_length*bpf, samplerate);
248
alSourceQueueBuffers(source, 1, &buffers[bufid]); // FIXME: collect buffer handles and queue all at once instead of one at a time?
249
if (++bufid >= num_buffers) bufid = 0;
250
count--; buffers_free--;
252
buf_first_free = bufid;
254
// Free buffer memory again
259
void OpenALPlayer::Notify()
262
alGetSourcei(source, AL_BUFFERS_PROCESSED, &newplayed);
266
ALuint *bufs = new ALuint[newplayed];
268
while (i < newplayed) {
269
bufs[i++] = buffers[buf_first_queued];
270
if (++buf_first_queued >= num_buffers) buf_first_queued = 0;
272
alSourceUnqueueBuffers(source, newplayed, bufs);
274
buffers_free += newplayed;
277
buffers_played += newplayed;
278
playback_segment_timer.Start();
281
FillBuffers(newplayed);
284
// Check that all of the selected audio plus one full set of buffers has been queued
285
if ((buffers_played - num_buffers) * buffer_length > (ALsizei)(end_frame - start_frame)) {
292
bool OpenALPlayer::IsPlaying()
300
void OpenALPlayer::SetEndPosition(int64_t pos)
306
////////////////////////
307
// Set current position
308
void OpenALPlayer::SetCurrentPosition(int64_t pos)
314
int64_t OpenALPlayer::GetStartPosition()
320
int64_t OpenALPlayer::GetEndPosition()
326
////////////////////////
327
// Get current position
328
int64_t OpenALPlayer::GetCurrentPosition()
330
// FIXME: this should be based on not duration played but actual sample being heard
331
// (during video playback, cur_frame might get changed to resync)
332
long extra = playback_segment_timer.Time();
333
return buffers_played * buffer_length + start_frame + extra * samplerate / 1000;
337
#endif // WITH_OPENAL