Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7318 was 7314, checked in by bensch, 19 years ago

orxonox/trunk: ShellBuffer is now ThreadSafe for printing to it
I must say: SDL_Thread's are way easier than simple pThreads

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