Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: Namespaces for sound

File size: 13.3 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    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.
19
20   -------------------------------------------------------------------
21   A Simple Version of this can be found at http://www.devmaster.net
22   Thanks a lot for the nice work, and the easy portability to our Project.
23*/
24#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_SOUND
25
26#include <iostream>
27
28#include "ogg_player.h"
29
30#include "sound_engine.h"
31
32#include "debug.h"
33
34namespace OrxSound
35{
36  /**
37   * initializes an Ogg-player from a file
38   * @param fileName the file to load
39   */
40  OggPlayer::OggPlayer(const std::string& fileName)
41  {
42    this->setClassID(CL_SOUND_OGG_PLAYER, "OggPlayer");
43
44    this->state = OggPlayer::None;
45
46    this->source = 0;
47    this->buffers[0] = 0;
48    this->buffers[1] = 0;
49    this->musicThreadID = NULL;
50    this->musicMutex = SDL_CreateMutex();
51
52    if (!fileName.empty())
53    {
54      if (this->open(fileName))
55        this->setName(fileName);
56    }
57
58  }
59
60  /**
61   * @brief deletes the OggPlayer
62   */
63  OggPlayer::~OggPlayer()
64  {
65    this->release();
66    SDL_DestroyMutex(this->musicMutex);
67  }
68
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)
77  {
78    MutexLock(this->musicMutex);
79    // release old Ogg-File (if loaded)
80    if (this->state & OggPlayer::FileOpened)
81      this->release();
82
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    }
106
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;
124
125    // acquiring the vorbis-properties.
126    vorbisInfo = ov_info(&oggStream, -1);
127    vorbisComment = ov_comment(&oggStream, -1);
128
129    if(vorbisInfo->channels == 1)
130      format = AL_FORMAT_MONO16;
131    else
132      format = AL_FORMAT_STEREO16;
133
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__);
142
143    // Set to State Stopped
144    this->state |= OggPlayer::Stopped;
145    return true;
146  }
147
148
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;
157
158    this->state &= ~(OggPlayer::Stopped | OggPlayer::Paused);
159
160    if (!this->playback())
161      return false;
162
163    if (this->musicThreadID == NULL)
164      return ((this->musicThreadID = SDL_CreateThread(OggPlayer::musicThread, (void*)this)) != NULL);
165    return true;
166  }
167
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;
175
176    this->suspend();
177    this->rewind();
178  }
179
180  /**
181   * @brief Pause the Playing.
182   */
183  void OggPlayer::pause()
184  {
185    this->state &= ~OggPlayer::Playing;
186
187    if (!(this->state & OggPlayer::Stopped))
188      this->state |= OggPlayer::Paused;
189
190    this->suspend();
191  }
192
193  /**
194   * @brief rewind to the beginning, and play (if already playing)
195   */
196  void OggPlayer::rewind()
197  {
198    this->jumpTo(0.0f);
199  }
200
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  {
207
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  }
215
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  }
226
227
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;
236
237    alGetSourcei(this->source, AL_SOURCE_STATE, &state);
238
239    return (state == AL_PLAYING);
240  }
241
242
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)
253  {
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");
267  }
268
269
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;
278
279    if(this->state & OggPlayer::Playing)
280      return true;
281    this->state |= OggPlayer::Playing;
282
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__);
298    SDL_mutexV(this->musicMutex);
299    return true;
300  }
301
302
303  /**
304   * @brief waits for the AudioThread to be finished.
305   */
306  void OggPlayer::suspend()
307  {
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    }
319  }
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()
326  {
327    int processed = 0;
328    bool active = true;
329
330    alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
331    if (DEBUG >= 3)
332      SoundEngine::checkError("OggPlayer::update()::alGetSourceI", __LINE__);
333
334    while(processed--)
335    {
336      ALuint buffer;
337
338      alSourceUnqueueBuffers(source, 1, &buffer);
339      if (DEBUG >= 3)
340        SoundEngine::checkError("OggPlayer::update()::unqueue", __LINE__);
341
342      active = stream(buffer);
343
344      alSourceQueueBuffers(source, 1, &buffer);
345      if (DEBUG >= 3)
346        SoundEngine::checkError("OggPlayer::update()::queue", __LINE__);
347    }
348
349    return active;
350  }
351
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;
365
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);
369
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    }
377
378    if(size == 0)
379      return false;
380
381    alBufferData(buffer, format, pcm, size, vorbisInfo->rate);
382    if (DEBUG >= 3)
383      SoundEngine::checkError("OggPlayer::stream()::BUFFER", __LINE__);
384
385    return true;
386  }
387
388
389  /**
390   * @brief releases a stream
391   */
392  void OggPlayer::release()
393  {
394    if (this->state & OggPlayer::SourceAllocated)
395    {
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();
402
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;
410    }
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    }
420
421    if (this->state & OggPlayer::FileOpened)
422    {
423      ov_clear(&oggStream);
424      this->state &= ~OggPlayer::FileOpened;
425    }
426
427  }
428
429
430  /**
431   * @brief empties the buffers
432   */
433  void OggPlayer::empty()
434  {
435    int queued;
436
437    alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
438
439    while(queued--)
440    {
441      ALuint buffer;
442
443      alSourceUnqueueBuffers(source, 1, &buffer);
444      SoundEngine::checkError("OggPlayer::empty()::unqueue Buffers", __LINE__);
445    }
446  }
447
448
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";
467
468    for(int i = 0; i < vorbisComment->comments; i++)
469      std::cout << "   " << vorbisComment->user_comments[i] << "\n";
470
471    std::cout << std::endl;
472  }
473
474
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  }
494
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)
501  {
502    switch(code)
503    {
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.");
516    }
517  }
518
519}
Note: See TracBrowser for help on using the repository browser.