Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: more compliant, less errors, this seems to be better…

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