Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7308 was 7308, checked in by bensch, 20 years ago

orxonox/trunk: added a mutex, that should prevent the SoundEngine from failing, when jumping around in the file

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