Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8359 was 8350, checked in by bensch, 18 years ago

fixed out some warnings

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