Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: new Mutex (do not think that it hlps… but hey lets try it )

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