Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/new_class_id/src/lib/sound/ogg_player.cc @ 9786

Last change on this file since 9786 was 9715, checked in by bensch, 18 years ago

renamed newclassid to classid and newobjectlist to objectlist

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