Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/bsp_model/src/lib/sound/ogg_player.cc @ 8538

Last change on this file since 8538 was 8362, checked in by bensch, 18 years ago

orxonox/trunk: removed stupid included in base_object.h
this should lead to faster compile-times

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