Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/sound/ogg_player.cc @ 7313

Last change on this file since 7313 was 7313, checked in by bensch, 18 years ago

orxonox/trunk: hunting ghosts… the Thread seems to work, and be safe…. now i think about many other cases, where the thread could corrupt… fixing.

File size: 12.2 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11### File Specific:
12   main-programmer: Benjamin Grauer
13   co-programmer: ...
14
15
16   -------------------------------------------------------------------
17   The source of this file comes stright from http://www.devmaster.net
18   Thanks a lot for the nice work, and the easy portability to our Project.
19*/
20
21#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_SOUND
22
23#include <iostream>
24
25#include "ogg_player.h"
26
27#include "sound_engine.h"
28
29#include "debug.h"
30
31/**
32 * initializes an Ogg-player from a file
33 * @param fileName the file to load
34 */
35OggPlayer::OggPlayer(const std::string& fileName)
36{
37  this->setClassID(CL_SOUND_OGG_PLAYER, "OggPlayer");
38
39  this->state = OggPlayer::None;
40
41  this->source = 0;
42  this->buffers[0] = 0;
43  this->buffers[1] = 0;
44  this->musicThreadID = NULL;
45  this->musicMutex = SDL_CreateMutex();
46
47  if (!fileName.empty())
48  {
49    if (this->open(fileName))
50      this->setName(fileName);
51  }
52
53}
54
55/**
56 * @brief deletes the OggPlayer
57 */
58OggPlayer::~OggPlayer()
59{
60  this->release();
61  SDL_DestroyMutex(this->musicMutex);
62}
63
64///////////////
65// INTERFACE //
66///////////////
67/**
68 * @brief opens a file for playback
69 * @param fileName the file to open
70 */
71bool OggPlayer::open(const std::string& fileName)
72{
73  MutexLock(this->musicMutex);
74  // release old Ogg-File (if loaded)
75  if (this->state & OggPlayer::FileOpened)
76    this->release();
77
78  // allocating Buffers
79  if (this->buffers[0] == 0)
80    alGenBuffers(2, this->buffers);
81  SoundEngine::checkError("Allocating Buffers", __LINE__);
82  if (this->buffers[0] != 0 && this->buffers[1] != 0)
83    state |= OggPlayer::BuffersAllocated;
84  else
85  {
86    PRINTF(2)("Unable to allocate al-Buffers\n");
87    this->release();
88    return false;
89  }
90  // allocating source
91  if (this->source == 0)
92    SoundEngine::getInstance()->popALSource(this->source);
93  if (this->source != 0)
94    state |= OggPlayer::SourceAllocated;
95  else
96  {
97    PRINTF(2)("No more Sources Availiable (maybe you should consider raising the source-count.)\n");
98    this->release();
99    return false;
100  }
101
102  // opening the FILE;
103  int result;
104  if(!(oggFile = fopen(fileName.c_str(), "rb")))
105  {
106    PRINTF(2)("Could not open Ogg file.");
107    this->release();
108    return false;
109  }
110  // reading the Stream.
111  if((result = ov_open(oggFile, &oggStream, NULL, 0)) < 0)
112  {
113    PRINTF(2)("Could not open Ogg stream. %s", getVorbisError(result));
114    fclose(oggFile);
115    this->release();
116    return false;
117  }
118  this->state |= OggPlayer::FileOpened;
119
120  // acquiring the vorbis-properties.
121  vorbisInfo = ov_info(&oggStream, -1);
122  vorbisComment = ov_comment(&oggStream, -1);
123
124  if(vorbisInfo->channels == 1)
125    format = AL_FORMAT_MONO16;
126  else
127    format = AL_FORMAT_STEREO16;
128
129  // setting the Source Properties.
130  alSource3f(source, AL_POSITION,        0.0, 0.0, 0.0);
131  alSource3f(source, AL_VELOCITY,        0.0, 0.0, 0.0);
132  alSource3f(source, AL_DIRECTION,       0.0, 0.0, 0.0);
133  alSourcef (source, AL_ROLLOFF_FACTOR,  0.0          );
134  alSourcei (source, AL_SOURCE_RELATIVE, AL_TRUE      );
135  alSourcef (source, AL_GAIN,            SoundEngine::getInstance()->getMusicVolume());
136  SoundEngine::checkError("OggPlayer::open()::SetSourceProperties", __LINE__);
137
138  // Set to State Stopped
139  this->state |= OggPlayer::Stopped;
140  return true;
141}
142
143
144/**
145 * @brief start Playing Music.
146 * @returns true on success false otherwise.
147 */
148bool OggPlayer::play()
149{
150  if (!(this->state & OggPlayer::FileOpened))
151    return false;
152
153  this->state &= ~(OggPlayer::Stopped | OggPlayer::Paused);
154
155  if (!this->playback())
156    return false;
157
158  if (this->musicThreadID == NULL)
159    return ((this->musicThreadID = SDL_CreateThread(OggPlayer::musicThread, (void*)this)) != NULL);
160  return true;
161}
162
163/**
164 * @brief stop the OggPlayer from Playing.
165 */
166void OggPlayer::stop()
167{
168  this->state &= ~(OggPlayer::Playing | OggPlayer::Paused);
169  this->state |= OggPlayer::Stopped;
170
171  this->suspend();
172  this->rewind();
173}
174
175/**
176 * @brief Pause the Playing.
177 */
178void OggPlayer::pause()
179{
180  this->state &= ~OggPlayer::Playing;
181
182  if (!(this->state & OggPlayer::Stopped))
183    this->state |= OggPlayer::Paused;
184
185  this->suspend();
186}
187
188/**
189 * @brief rewind to the beginning, and play (if already playing)
190 */
191void OggPlayer::rewind()
192{
193  this->jumpTo(0.0f);
194}
195
196/**
197 * @brief jump to Second timeCode in the MusicFile
198 * @param timeCode where to jump to.
199 */
200void OggPlayer::jumpTo(float timeCode)
201{
202
203  if (this->state & OggPlayer::FileOpened)
204  {
205    SDL_mutexP(this->musicMutex);
206    ov_time_seek(&this->oggStream, timeCode);
207    SDL_mutexV(this->musicMutex);
208  }
209}
210
211/**
212 * @returns the Length of the Music in Seconds
213 */
214float OggPlayer::length()
215{
216  if (this->state & OggPlayer::FileOpened)
217    return ov_time_total(&this->oggStream, -1);
218  else
219    return 0.0f;
220}
221
222
223/**
224 * @returns true if the file is playing
225 */
226bool OggPlayer::isPlaying()
227{
228  if (!(this->state & OggPlayer::FileOpened))
229    return false;
230  ALenum state;
231
232  alGetSourcei(this->source, AL_SOURCE_STATE, &state);
233
234  return (state == AL_PLAYING);
235}
236
237
238
239////////////////////////
240// INTERNAL FUNCTIONS //
241////////////////////////
242/**
243 * @brief creates a Thread for Playing back the music even if the rest of the Engine is slow
244 * @param oggPlayer the OggPlayer to play back
245 * @returns -1 on error.
246 */
247int OggPlayer::musicThread(void* oggPlayer)
248{
249  if (oggPlayer == NULL)
250    return -1;
251  OggPlayer* ogg = (OggPlayer*)oggPlayer;
252
253  PRINTF(4)("STARTIG AUDIO THREAD\n");
254  while (ogg->state & OggPlayer::Playing)
255  {
256    SDL_mutexP(ogg->musicMutex);
257    ogg->update();
258    SDL_mutexV(ogg->musicMutex);
259    SDL_Delay(1);
260  }
261  PRINTF(4)("End the AudioThread\n");
262}
263
264
265/**
266 * @brief plays back the sound
267 * @return true if running, false otherwise
268 */
269bool OggPlayer::playback()
270{
271  if (!(this->state & OggPlayer::FileOpened))
272    return false;
273
274  if(this->state & OggPlayer::Playing)
275    return true;
276  this->state |= OggPlayer::Playing;
277
278  SDL_mutexP(this->musicMutex);
279  if(!this->stream(this->buffers[0]) || !this->stream(this->buffers[1]))
280  {
281    SDL_mutexV(this->musicMutex);
282    this->state &= ~OggPlayer::Playing;
283    return false;
284  }
285
286  alSourceQueueBuffers(this->source, 2, this->buffers);
287  if (DEBUG >= 3)
288    SoundEngine::checkError("OggPlayer::playback()::alSourceQueueBuffers", __LINE__);
289
290  alSourcePlay(this->source);
291  if (DEBUG >= 3)
292    SoundEngine::checkError("OggPlayer::playback()::alSourcePlay", __LINE__);
293  SDL_mutexV(this->musicMutex);
294  return true;
295}
296
297
298/**
299 * @brief waits for the AudioThread to be finished.
300 */
301void OggPlayer::suspend()
302{
303  if (this->musicThreadID != NULL)
304  {
305    assert (!(this->state & Playing));
306    SDL_WaitThread(this->musicThreadID, NULL);
307    this->musicThreadID = NULL;
308  }
309  if (this->state & OggPlayer::SourceAllocated)
310  {
311    alSourceStop(this->source);
312    alSourcei(this->source, AL_BUFFER, 0);
313  }
314}
315
316/**
317 * @brief updates the stream, this has to be done every few parts of a second, for sound-consistency
318 * @returns true, if the Sound is playing flawlessly
319 */
320bool OggPlayer::update()
321{
322  int processed = 0;
323  bool active = true;
324
325  alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
326  if (DEBUG >= 3)
327    SoundEngine::checkError("OggPlayer::update()::alGetSourceI", __LINE__);
328
329  while(processed--)
330  {
331    ALuint buffer;
332
333    alSourceUnqueueBuffers(source, 1, &buffer);
334    if (DEBUG >= 3)
335      SoundEngine::checkError("OggPlayer::update()::unqueue", __LINE__);
336
337    active = stream(buffer);
338
339    alSourceQueueBuffers(source, 1, &buffer);
340    if (DEBUG >= 3)
341      SoundEngine::checkError("OggPlayer::update()::queue", __LINE__);
342  }
343
344  return active;
345}
346
347/**
348 * @brief gets a new Stream from buffer
349 * @param buffer the buffer to get the stream from
350 * @return true, if everything worked as planed
351 */
352bool OggPlayer::stream(ALuint buffer)
353{
354  if (unlikely(!(this->state & Playing)))
355    return false;
356  char pcm[OGG_PLAYER_BUFFER_SIZE];
357  int  size = 0;
358  int  section;
359  int  result;
360
361  while(size < OGG_PLAYER_BUFFER_SIZE)
362  {
363    result = ov_read(&this->oggStream, pcm + size, OGG_PLAYER_BUFFER_SIZE - size, 0, 2, 1, &section);
364
365    if(result > 0)
366      size += result;
367    else if(result < 0)
368      throw getVorbisError(result);
369    else /* eof */
370      ov_time_seek(&this->oggStream, 0.0);
371  }
372
373  if(size == 0)
374    return false;
375
376  alBufferData(buffer, format, pcm, size, vorbisInfo->rate);
377  if (DEBUG >= 3)
378    SoundEngine::checkError("OggPlayer::stream()::BUFFER", __LINE__);
379
380  return true;
381}
382
383
384/**
385 * @brief releases a stream
386 */
387void OggPlayer::release()
388{
389  if (this->state & OggPlayer::SourceAllocated)
390  {
391    assert(alIsSource(this->source));
392    if (this->state & OggPlayer::Playing);
393    {
394      // Kill the Music Thread.
395      this->state &= ~OggPlayer::Playing;
396      this->suspend();
397
398      SoundEngine::checkError("OggPlayer::release()::alSourceStop", __LINE__);
399    }
400    empty();
401    alSourcei(this->source, AL_BUFFER, 0);
402    SoundEngine::getInstance()->pushALSource(this->source);
403    this->source = 0;
404    this->state &= ~SourceAllocated;
405  }
406  if (this->state & OggPlayer::BuffersAllocated)
407  {
408    assert (this->buffers[0] != 0 && this->buffers[1] != 0);
409    alDeleteBuffers(2, buffers);
410    SoundEngine::checkError("OggPlayer::release()::alDeleteBuffers", __LINE__);
411    this->buffers[0] = 0;
412    this->buffers[1] = 0;
413    this->state &= ~OggPlayer::BuffersAllocated;
414  }
415
416  if (this->state & OggPlayer::FileOpened)
417  {
418    ov_clear(&oggStream);
419    this->state &= ~OggPlayer::FileOpened;
420  }
421
422}
423
424
425/**
426 * @brief empties the buffers
427 */
428void OggPlayer::empty()
429{
430  int queued;
431
432  alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
433
434  while(queued--)
435  {
436    ALuint buffer;
437
438    alSourceUnqueueBuffers(source, 1, &buffer);
439    SoundEngine::checkError("OggPlayer::empty()::unqueue Buffers", __LINE__);
440  }
441}
442
443
444/////////////////////
445// DEBUG FUNCTIONS //
446/////////////////////
447/**
448 * displays some info about the ogg-file
449 */
450void OggPlayer::debug() const
451{
452  cout
453  << "version         " << vorbisInfo->version         << "\n"
454  << "channels        " << vorbisInfo->channels        << "\n"
455  << "rate (hz)       " << vorbisInfo->rate            << "\n"
456  << "bitrate upper   " << vorbisInfo->bitrate_upper   << "\n"
457  << "bitrate nominal " << vorbisInfo->bitrate_nominal << "\n"
458  << "bitrate lower   " << vorbisInfo->bitrate_lower   << "\n"
459  << "bitrate window  " << vorbisInfo->bitrate_window  << "\n"
460  << "\n"
461  << "vendor " << vorbisComment->vendor << "\n";
462
463  for(int i = 0; i < vorbisComment->comments; i++)
464    cout << "   " << vorbisComment->user_comments[i] << "\n";
465
466  cout << endl;
467}
468
469
470void OggPlayer::printState() const
471{
472  PRINTF(0)("OggPlayer is in the following States: ");
473  if (this->state & OggPlayer::FileOpened)
474    PRINT(0)("FileOpened ");
475  if (this->state & OggPlayer::SourceAllocated)
476    PRINT(0)("SourceAllocated ");
477  if (this->state & OggPlayer::BuffersAllocated)
478    PRINT(0)("BuffersAllocated ");
479  if (this->state & OggPlayer::Stopped)
480    PRINT(0)("Stopped ");
481  if (this->state & OggPlayer::Playing)
482    PRINT(0)("Playing ");
483  if (this->state & OggPlayer::Paused)
484    PRINT(0)("Paused ");
485  if (this->state & OggPlayer::Error)
486    PRINT(0)("Error ");
487  PRINT(0)("\n");
488}
489
490/**
491 * returns errors
492 * @param code the error-code
493 * @return the error as a String
494 */
495const char* OggPlayer::getVorbisError(int code)
496{
497  switch(code)
498  {
499    case OV_EREAD:
500      return ("Read from media.");
501    case OV_ENOTVORBIS:
502      return ("Not Vorbis data.");
503    case OV_EVERSION:
504      return ("Vorbis version mismatch.");
505    case OV_EBADHEADER:
506      return ("Invalid Vorbis header.");
507    case OV_EFAULT:
508      return ("Internal logic fault (bug or heap/stack corruption.");
509    default:
510      return ("Unknown Ogg error.");
511  }
512}
513
Note: See TracBrowser for help on using the repository browser.