Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7704 was 7460, checked in by bensch, 19 years ago

orxonox/trunk: Namespaces for sound

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