Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8162 was 8122, checked in by ponder, 19 years ago

Md2loader seems to work

File size: 13.1 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(1);
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    if (DEBUG_LEVEL >= 3)
340      SoundEngine::checkError("OggPlayer::update()::alGetSourceI", __LINE__);
341
342    while(processed--)
343    {
344      ALuint buffer;
345
346      alSourceUnqueueBuffers(source, 1, &buffer);
347      if (DEBUG_LEVEL >= 3)
348        SoundEngine::checkError("OggPlayer::update()::unqueue", __LINE__);
349
350      active = stream(buffer);
351
352      alSourceQueueBuffers(source, 1, &buffer);
353      if (DEBUG_LEVEL >= 3)
354        SoundEngine::checkError("OggPlayer::update()::queue", __LINE__);
355    }
356
357    return active;
358  }
359
360  /**
361   * @brief gets a new Stream from buffer
362   * @param buffer the buffer to get the stream from
363   * @return true, if everything worked as planed
364   */
365  bool OggPlayer::stream(ALuint buffer)
366  {
367    if (unlikely(!(this->state & Playing)))
368      return false;
369    char pcm[OGG_PLAYER_BUFFER_SIZE];
370    int  size = 0;
371    int  section;
372    int  result;
373
374    while(size < OGG_PLAYER_BUFFER_SIZE)
375    {
376      result = ov_read(&this->oggStream, pcm + size, OGG_PLAYER_BUFFER_SIZE - size, 0, 2, 1, &section);
377
378      if(result > 0)
379        size += result;
380      else if(result < 0)
381        throw getVorbisError(result);
382      else /* eof */
383        ov_time_seek(&this->oggStream, 0.0);
384    }
385
386    if(size == 0)
387      return false;
388/*#ifdef SDL_BIG_ENDIAN
389                        int cnt = wavLength/2;
390                        Uint16* wavBufferAsShorts = ( Uint16* )wavBuffer;
391                        for ( int i = 0; i < cnt; ++i, ++wavBufferAsShorts )
392                                *wavBufferAsShorts = SDL_Swap16( *wavBufferAsShorts );
393#endif*/
394    alBufferData(buffer, format, pcm, size, vorbisInfo->rate);
395    if (DEBUG_LEVEL >= 3)
396      SoundEngine::checkError("OggPlayer::stream()::BUFFER", __LINE__);
397
398    return true;
399  }
400
401
402  /**
403   * @brief releases a stream
404   */
405  void OggPlayer::release()
406  {
407    if (this->state & OggPlayer::SourceAllocated)
408    {
409      assert(alIsSource(this->source));
410      if (this->state & OggPlayer::Playing);
411      {
412        // Kill the Music Thread.
413        this->state &= ~OggPlayer::Playing;
414        this->suspend();
415
416        SoundEngine::checkError("OggPlayer::release()::alSourceStop", __LINE__);
417      }
418      empty();
419      alSourcei(this->source, AL_BUFFER, 0);
420      SoundEngine::getInstance()->pushALSource(this->source);
421      this->source = 0;
422      this->state &= ~SourceAllocated;
423    }
424    if (this->state & OggPlayer::BuffersAllocated)
425    {
426      assert (this->buffers[0] != 0 && this->buffers[1] != 0);
427      alDeleteBuffers(2, buffers);
428      SoundEngine::checkError("OggPlayer::release()::alDeleteBuffers", __LINE__);
429      this->buffers[0] = 0;
430      this->buffers[1] = 0;
431      this->state &= ~OggPlayer::BuffersAllocated;
432    }
433
434    if (this->state & OggPlayer::FileOpened)
435    {
436      ov_clear(&oggStream);
437      this->state &= ~OggPlayer::FileOpened;
438    }
439
440  }
441
442
443  /**
444   * @brief empties the buffers
445   */
446  void OggPlayer::empty()
447  {
448    int queued;
449
450    alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
451
452    while(queued--)
453    {
454      ALuint buffer;
455
456      alSourceUnqueueBuffers(source, 1, &buffer);
457      SoundEngine::checkError("OggPlayer::empty()::unqueue Buffers", __LINE__);
458    }
459  }
460
461
462  /////////////////////
463  // DEBUG_LEVEL FUNCTIONS //
464  /////////////////////
465  /**
466   * displays some info about the ogg-file
467   */
468  void OggPlayer::debug() const
469  {
470    std::cout
471    << "version         " << vorbisInfo->version         << "\n"
472    << "channels        " << vorbisInfo->channels        << "\n"
473    << "rate (hz)       " << vorbisInfo->rate            << "\n"
474    << "bitrate upper   " << vorbisInfo->bitrate_upper   << "\n"
475    << "bitrate nominal " << vorbisInfo->bitrate_nominal << "\n"
476    << "bitrate lower   " << vorbisInfo->bitrate_lower   << "\n"
477    << "bitrate window  " << vorbisInfo->bitrate_window  << "\n"
478    << "\n"
479    << "vendor " << vorbisComment->vendor << "\n";
480
481    for(int i = 0; i < vorbisComment->comments; i++)
482      std::cout << "   " << vorbisComment->user_comments[i] << "\n";
483
484    std::cout << std::endl;
485  }
486
487
488  void OggPlayer::printState() const
489  {
490    PRINTF(0)("OggPlayer is in the following States: ");
491    if (this->state & OggPlayer::FileOpened)
492      PRINT(0)("FileOpened ");
493    if (this->state & OggPlayer::SourceAllocated)
494      PRINT(0)("SourceAllocated ");
495    if (this->state & OggPlayer::BuffersAllocated)
496      PRINT(0)("BuffersAllocated ");
497    if (this->state & OggPlayer::Stopped)
498      PRINT(0)("Stopped ");
499    if (this->state & OggPlayer::Playing)
500      PRINT(0)("Playing ");
501    if (this->state & OggPlayer::Paused)
502      PRINT(0)("Paused ");
503    if (this->state & OggPlayer::Error)
504      PRINT(0)("Error ");
505    PRINT(0)("\n");
506  }
507
508  /**
509   * returns errors
510   * @param code the error-code
511   * @return the error as a String
512   */
513  const char* OggPlayer::getVorbisError(int code)
514  {
515    switch(code)
516    {
517      case OV_EREAD:
518        return ("Read from media.");
519      case OV_ENOTVORBIS:
520        return ("Not Vorbis data.");
521      case OV_EVERSION:
522        return ("Vorbis version mismatch.");
523      case OV_EBADHEADER:
524        return ("Invalid Vorbis header.");
525      case OV_EFAULT:
526        return ("Internal logic fault (bug or heap/stack corruption.");
527      default:
528        return ("Unknown Ogg error.");
529    }
530  }
531
532}
Note: See TracBrowser for help on using the repository browser.