Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8295 was 8293, checked in by bensch, 18 years ago

orxonox/trunk: merged the osX-branche back here
merged with command:
svn merge https://svn.orxonox.net/orxonox/branches/osx . -r7763:HEAD

conflicts resolved, and everything is working as expected (or at least i hope so :) )

File size: 13.2 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
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
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  {
88    OrxThread::MutexLock musicLock(&this->musicMutex);
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  {
165    /*if (!(this->state & OggPlayer::FileOpened))
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);
175    return true;*/
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    {
220      OrxThread::MutexLock musicLock(&this->musicMutex);
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    {
270      {
271        OrxThread::MutexLock musicLock(&ogg->musicMutex);
272        ogg->update();
273      }
274      SDL_Delay(10);
275    }
276    PRINTF(4)("End the AudioThread\n");
277  }
278
279
280  /**
281   * @brief plays back the sound
282   * @return true if running, false otherwise
283   */
284  bool OggPlayer::playback()
285  {
286    if (!(this->state & OggPlayer::FileOpened))
287      return false;
288
289    if(this->state & OggPlayer::Playing)
290      return true;
291    this->state |= OggPlayer::Playing;
292
293    OrxThread::MutexLock musicLock(&this->musicMutex);
294    if(!this->stream(this->buffers[0]) || !this->stream(this->buffers[1]))
295    {
296      this->state &= ~OggPlayer::Playing;
297      return false;
298    }
299
300    alSourceQueueBuffers(this->source, 2, this->buffers);
301    if (DEBUG_LEVEL >= 3)
302      SoundEngine::checkError("OggPlayer::playback()::alSourceQueueBuffers", __LINE__);
303
304    alSourcePlay(this->source);
305    if (DEBUG_LEVEL >= 3)
306      SoundEngine::checkError("OggPlayer::playback()::alSourcePlay", __LINE__);
307    return true;
308  }
309
310
311  /**
312   * @brief waits for the AudioThread to be finished.
313   */
314  void OggPlayer::suspend()
315  {
316    if (this->musicThreadID != NULL)
317    {
318      assert (!(this->state & Playing));
319      SDL_WaitThread(this->musicThreadID, NULL);
320      this->musicThreadID = NULL;
321    }
322    if (this->state & OggPlayer::SourceAllocated)
323    {
324      alSourceStop(this->source);
325      alSourcei(this->source, AL_BUFFER, 0);
326    }
327  }
328
329  /**
330   * @brief updates the stream, this has to be done every few parts of a second, for sound-consistency
331   * @returns true, if the Sound is playing flawlessly
332   */
333  bool OggPlayer::update()
334  {
335    int processed = 0;
336    bool active = true;
337
338    alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
339
340    if (DEBUG_LEVEL >= 3)
341      SoundEngine::checkError("OggPlayer::update()::alGetSourceI", __LINE__);
342
343    while(processed--)
344    {
345      ALuint buffer;
346
347      alSourceUnqueueBuffers(source, 1, &buffer);
348      if (DEBUG_LEVEL >= 3)
349        SoundEngine::checkError("OggPlayer::update()::unqueue", __LINE__);
350
351      active = stream(buffer);
352
353      alSourceQueueBuffers(source, 1, &buffer);
354      if (DEBUG_LEVEL >= 3)
355        SoundEngine::checkError("OggPlayer::update()::queue", __LINE__);
356    }
357
358    int play;
359    alGetSourcei(source, AL_SOURCE_STATE, &play);
360
361    if (play != AL_PLAYING)
362    {
363      alSourcePlay(source);
364      PRINTF(2)("Filling Audio Gap\n");
365    }
366
367
368    return active;
369  }
370
371  /**
372   * @brief gets a new Stream from buffer
373   * @param buffer the buffer to get the stream from
374   * @return true, if everything worked as planed
375   */
376  bool OggPlayer::stream(ALuint buffer)
377  {
378    if (unlikely(!(this->state & Playing)))
379      return false;
380    char pcm[OGG_PLAYER_BUFFER_SIZE];
381    int  size = 0;
382    int  section;
383    int  result;
384
385    while(size < OGG_PLAYER_BUFFER_SIZE)
386    {
387      result = ov_read(&this->oggStream, pcm + size, OGG_PLAYER_BUFFER_SIZE - size, 0, 2, 1, &section);
388
389      if(result > 0)
390        size += result;
391      else if(result < 0)
392        throw getVorbisError(result);
393      else /* eof */
394        ov_time_seek(&this->oggStream, 0.0);
395    }
396
397    if(size == 0)
398      return false;
399/*#ifdef SDL_BIG_ENDIAN
400                        int cnt = wavLength/2;
401                        Uint16* wavBufferAsShorts = ( Uint16* )wavBuffer;
402                        for ( int i = 0; i < cnt; ++i, ++wavBufferAsShorts )
403                                *wavBufferAsShorts = SDL_Swap16( *wavBufferAsShorts );
404#endif*/
405    alBufferData(buffer, format, pcm, size, vorbisInfo->rate);
406    if (DEBUG_LEVEL >= 3)
407      SoundEngine::checkError("OggPlayer::stream()::BUFFER", __LINE__);
408
409    return true;
410  }
411
412
413  /**
414   * @brief releases a stream
415   */
416  void OggPlayer::release()
417  {
418    if (this->state & OggPlayer::SourceAllocated)
419    {
420      assert(alIsSource(this->source));
421      if (this->state & OggPlayer::Playing);
422      {
423        // Kill the Music Thread.
424        this->state &= ~OggPlayer::Playing;
425        this->suspend();
426
427        SoundEngine::checkError("OggPlayer::release()::alSourceStop", __LINE__);
428      }
429      empty();
430      alSourcei(this->source, AL_BUFFER, 0);
431      SoundEngine::getInstance()->pushALSource(this->source);
432      this->source = 0;
433      this->state &= ~SourceAllocated;
434    }
435    if (this->state & OggPlayer::BuffersAllocated)
436    {
437      assert (this->buffers[0] != 0 && this->buffers[1] != 0);
438      alDeleteBuffers(2, buffers);
439      SoundEngine::checkError("OggPlayer::release()::alDeleteBuffers", __LINE__);
440      this->buffers[0] = 0;
441      this->buffers[1] = 0;
442      this->state &= ~OggPlayer::BuffersAllocated;
443    }
444
445    if (this->state & OggPlayer::FileOpened)
446    {
447      ov_clear(&oggStream);
448      this->state &= ~OggPlayer::FileOpened;
449    }
450
451  }
452
453
454  /**
455   * @brief empties the buffers
456   */
457  void OggPlayer::empty()
458  {
459    int queued;
460
461    alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
462
463    while(queued--)
464    {
465      ALuint buffer;
466
467      alSourceUnqueueBuffers(source, 1, &buffer);
468      SoundEngine::checkError("OggPlayer::empty()::unqueue Buffers", __LINE__);
469    }
470  }
471
472
473  /////////////////////
474  // DEBUG_LEVEL FUNCTIONS //
475  /////////////////////
476  /**
477   * displays some info about the ogg-file
478   */
479  void OggPlayer::debug() const
480  {
481    std::cout
482    << "version         " << vorbisInfo->version         << "\n"
483    << "channels        " << vorbisInfo->channels        << "\n"
484    << "rate (hz)       " << vorbisInfo->rate            << "\n"
485    << "bitrate upper   " << vorbisInfo->bitrate_upper   << "\n"
486    << "bitrate nominal " << vorbisInfo->bitrate_nominal << "\n"
487    << "bitrate lower   " << vorbisInfo->bitrate_lower   << "\n"
488    << "bitrate window  " << vorbisInfo->bitrate_window  << "\n"
489    << "\n"
490    << "vendor " << vorbisComment->vendor << "\n";
491
492    for(int i = 0; i < vorbisComment->comments; i++)
493      std::cout << "   " << vorbisComment->user_comments[i] << "\n";
494
495    std::cout << std::endl;
496  }
497
498
499  void OggPlayer::printState() const
500  {
501    PRINTF(0)("OggPlayer is in the following States: ");
502    if (this->state & OggPlayer::FileOpened)
503      PRINT(0)("FileOpened ");
504    if (this->state & OggPlayer::SourceAllocated)
505      PRINT(0)("SourceAllocated ");
506    if (this->state & OggPlayer::BuffersAllocated)
507      PRINT(0)("BuffersAllocated ");
508    if (this->state & OggPlayer::Stopped)
509      PRINT(0)("Stopped ");
510    if (this->state & OggPlayer::Playing)
511      PRINT(0)("Playing ");
512    if (this->state & OggPlayer::Paused)
513      PRINT(0)("Paused ");
514    if (this->state & OggPlayer::Error)
515      PRINT(0)("Error ");
516    PRINT(0)("\n");
517  }
518
519  /**
520   * returns errors
521   * @param code the error-code
522   * @return the error as a String
523   */
524  const char* OggPlayer::getVorbisError(int code)
525  {
526    switch(code)
527    {
528      case OV_EREAD:
529        return ("Read from media.");
530      case OV_ENOTVORBIS:
531        return ("Not Vorbis data.");
532      case OV_EVERSION:
533        return ("Vorbis version mismatch.");
534      case OV_EBADHEADER:
535        return ("Invalid Vorbis header.");
536      case OV_EFAULT:
537        return ("Internal logic fault (bug or heap/stack corruption.");
538      default:
539        return ("Unknown Ogg error.");
540    }
541  }
542
543}
Note: See TracBrowser for help on using the repository browser.