Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/osx/src/lib/sound/ogg_player.cc @ 7997

Last change on this file since 7997 was 7737, checked in by bensch, 19 years ago

Using MultiLine-Text in the Shell. This is much faster, and Nicer :)

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