~ubuntu-branches/ubuntu/trusty/aegisub/trusty

« back to all changes in this revision

Viewing changes to src/audio_player_openal.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel
  • Date: 2012-03-16 22:58:00 UTC
  • Revision ID: package-import@ubuntu.com-20120316225800-yfb8h9e5n04rk46a
Tags: upstream-2.1.9
ImportĀ upstreamĀ versionĀ 2.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2007, Niels Martin Hansen
 
2
// All rights reserved.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are met:
 
6
//
 
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.
 
15
//
 
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.
 
27
//
 
28
// -----------------------------------------------------------------------------
 
29
//
 
30
// AEGISUB
 
31
//
 
32
// Website: http://aegisub.cellosoft.com
 
33
// Contact: mailto:jiifurusu@gmail.com
 
34
//
 
35
 
 
36
 
 
37
#include "config.h"
 
38
 
 
39
#ifdef WITH_OPENAL
 
40
 
 
41
 
 
42
///////////
 
43
// Headers
 
44
#include <wx/wxprec.h>
 
45
#include "audio_player_manager.h"
 
46
#include "audio_provider_manager.h"
 
47
#include "utils.h"
 
48
#include "main.h"
 
49
#include "frame_main.h"
 
50
#include "audio_player_openal.h"
 
51
#include "options.h"
 
52
 
 
53
#ifdef __WINDOWS__
 
54
#include <al.h>
 
55
#include <alc.h>
 
56
#elif defined(__APPLE__)
 
57
#include <OpenAL/AL.h>
 
58
#include <OpenAL/ALC.h>
 
59
#else
 
60
#include <AL/al.h>
 
61
#include <AL/alc.h>
 
62
#endif
 
63
 
 
64
 
 
65
// Auto-link to OpenAL lib for MSVC
 
66
#ifdef _MSC_VER
 
67
#pragma comment(lib, "openal32.lib")
 
68
#endif
 
69
 
 
70
 
 
71
///////////////
 
72
// Constructor
 
73
OpenALPlayer::OpenALPlayer()
 
74
{
 
75
        volume = 1.0f;
 
76
        open = false;
 
77
        playing = false;
 
78
        start_frame = cur_frame = end_frame = bpf = 0;
 
79
        provider = 0;
 
80
}
 
81
 
 
82
 
 
83
//////////////
 
84
// Destructor
 
85
OpenALPlayer::~OpenALPlayer()
 
86
{
 
87
        CloseStream();
 
88
}
 
89
 
 
90
 
 
91
///////////////
 
92
// Open stream
 
93
void OpenALPlayer::OpenStream()
 
94
{
 
95
        CloseStream();
 
96
 
 
97
        // Get provider
 
98
        provider = GetProvider();
 
99
        bpf = provider->GetChannels() * provider->GetBytesPerSample();
 
100
 
 
101
        // Open device
 
102
        device = alcOpenDevice(0);
 
103
        if (!device) {
 
104
                throw _T("Failed opening default OpenAL device");
 
105
        }
 
106
 
 
107
        // Create context
 
108
        context = alcCreateContext(device, 0);
 
109
        if (!context) {
 
110
                alcCloseDevice(device);
 
111
                throw _T("Failed creating OpenAL context");
 
112
        }
 
113
        if (!alcMakeContextCurrent(context)) {
 
114
                alcDestroyContext(context);
 
115
                alcCloseDevice(device);
 
116
                throw _T("Failed selecting OpenAL context");
 
117
        }
 
118
        // Clear error code
 
119
        alGetError();
 
120
 
 
121
        // Generate buffers
 
122
        alGenBuffers(num_buffers, buffers);
 
123
        if (alGetError() != AL_NO_ERROR) {
 
124
                alcDestroyContext(context);
 
125
                alcCloseDevice(device);
 
126
                throw _T("Error generating OpenAL buffers");
 
127
        }
 
128
 
 
129
        // Generate source
 
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");
 
136
        }
 
137
 
 
138
        // Determine buffer length
 
139
        samplerate = provider->GetSampleRate();
 
140
        buffer_length = samplerate / num_buffers / 2; // buffers for half a second of audio
 
141
 
 
142
        // Now ready
 
143
        open = true;
 
144
}
 
145
 
 
146
 
 
147
////////////////
 
148
// Close stream
 
149
void OpenALPlayer::CloseStream()
 
150
{
 
151
        if (!open) return;
 
152
 
 
153
        Stop();
 
154
 
 
155
        alDeleteSources(1, &source);
 
156
        alDeleteBuffers(num_buffers, buffers);
 
157
        alcDestroyContext(context);
 
158
        alcCloseDevice(device);
 
159
 
 
160
        // No longer working
 
161
        open = false;
 
162
}
 
163
 
 
164
 
 
165
////////
 
166
// Play
 
167
void OpenALPlayer::Play(int64_t start,int64_t count)
 
168
{
 
169
        if (playing) {
 
170
                // Quick reset
 
171
                playing = false;
 
172
                alSourceStop(source);
 
173
                alSourcei(source, AL_BUFFER, 0);
 
174
        }
 
175
 
 
176
        // Set params
 
177
        start_frame = start;
 
178
        cur_frame = start;
 
179
        end_frame = start + count;
 
180
        playing = true;
 
181
 
 
182
        // Prepare buffers
 
183
        buffers_free = num_buffers;
 
184
        buffers_played = 0;
 
185
        buf_first_free = 0;
 
186
        buf_first_queued = 0;
 
187
        FillBuffers(num_buffers);
 
188
 
 
189
        // And go!
 
190
        alSourcePlay(source);
 
191
        wxTimer::Start(100);
 
192
        playback_segment_timer.Start();
 
193
 
 
194
        // Update timer
 
195
        if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
 
196
}
 
197
 
 
198
 
 
199
////////
 
200
// Stop
 
201
void OpenALPlayer::Stop(bool timerToo)
 
202
{
 
203
        if (!open) return;
 
204
        if (!playing) return;
 
205
 
 
206
        // Reset data
 
207
        wxTimer::Stop();
 
208
        playing = false;
 
209
        start_frame = 0;
 
210
        cur_frame = 0;
 
211
        end_frame = 0;
 
212
 
 
213
        // Then drop the playback
 
214
        alSourceStop(source);
 
215
        alSourcei(source, AL_BUFFER, 0);
 
216
 
 
217
        if (timerToo && displayTimer) {
 
218
                displayTimer->Stop();
 
219
        }
 
220
}
 
221
 
 
222
 
 
223
void OpenALPlayer::FillBuffers(ALsizei count)
 
224
{
 
225
        if (count > buffers_free) count = buffers_free;
 
226
        if (count < 1) count = 1;
 
227
 
 
228
        // Get memory to hold sound buffers
 
229
        void *data = malloc(buffer_length * bpf);
 
230
 
 
231
        // Do the actual filling/queueing
 
232
        ALuint bufid = buf_first_free;
 
233
        while (count > 0) {
 
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;
 
237
 
 
238
                if (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);
 
244
 
 
245
                cur_frame += fill_len;
 
246
 
 
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--;
 
251
        }
 
252
        buf_first_free = bufid;
 
253
 
 
254
        // Free buffer memory again
 
255
        free(data);
 
256
}
 
257
 
 
258
 
 
259
void OpenALPlayer::Notify()
 
260
{
 
261
        ALsizei newplayed;
 
262
        alGetSourcei(source, AL_BUFFERS_PROCESSED, &newplayed);
 
263
 
 
264
        if (newplayed > 0) {
 
265
                // Reclaim buffers
 
266
                ALuint *bufs = new ALuint[newplayed];
 
267
                ALsizei i = 0;
 
268
                while (i < newplayed) {
 
269
                        bufs[i++] = buffers[buf_first_queued];
 
270
                        if (++buf_first_queued >= num_buffers) buf_first_queued = 0;
 
271
                }
 
272
                alSourceUnqueueBuffers(source, newplayed, bufs);
 
273
                delete[] bufs;
 
274
                buffers_free += newplayed;
 
275
 
 
276
                // Update
 
277
                buffers_played += newplayed;
 
278
                playback_segment_timer.Start();
 
279
 
 
280
                // Fill more buffers
 
281
                FillBuffers(newplayed);
 
282
        }
 
283
 
 
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)) {
 
286
                // Then stop
 
287
                Stop(true);
 
288
        }
 
289
}
 
290
 
 
291
 
 
292
bool OpenALPlayer::IsPlaying()
 
293
{
 
294
        return playing;
 
295
}
 
296
 
 
297
 
 
298
///////////
 
299
// Set end
 
300
void OpenALPlayer::SetEndPosition(int64_t pos)
 
301
{
 
302
        end_frame = pos;
 
303
}
 
304
 
 
305
 
 
306
////////////////////////
 
307
// Set current position
 
308
void OpenALPlayer::SetCurrentPosition(int64_t pos)
 
309
{
 
310
        cur_frame = pos;
 
311
}
 
312
 
 
313
 
 
314
int64_t OpenALPlayer::GetStartPosition()
 
315
{
 
316
        return start_frame;
 
317
}
 
318
 
 
319
 
 
320
int64_t OpenALPlayer::GetEndPosition()
 
321
{
 
322
        return end_frame;
 
323
}
 
324
 
 
325
 
 
326
////////////////////////
 
327
// Get current position
 
328
int64_t OpenALPlayer::GetCurrentPosition()
 
329
{
 
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;
 
334
}
 
335
 
 
336
 
 
337
#endif // WITH_OPENAL
 
338