Abstract Ogg Vorbis sound decoding into the SoundStream class. Note: the SoundStream class is deliberately not exposed to scripts, as it requires low-level data access and is used from the sound mixing thread.

This commit is contained in:
Lasse Öörni 2014-03-19 00:11:20 +02:00
Родитель 4bf765d711
Коммит acb993e4ba
10 изменённых файлов: 502 добавлений и 241 удалений

Просмотреть файл

@ -0,0 +1,81 @@
//
// Copyright (c) 2008-2014 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "Precompiled.h"
#include "OggVorbisSoundStream.h"
#include "Sound.h"
#include <stb_vorbis.h>
#include "DebugNew.h"
namespace Urho3D
{
OggVorbisSoundStream::OggVorbisSoundStream(const Sound* sound)
{
assert(sound && sound->IsCompressed());
SetFormat(sound->GetIntFrequency(), sound->IsSixteenBit(), sound->IsStereo());
// If the sound is looped, the stream will automatically rewind at end
SetStopAtEnd(!sound->IsLooped());
// Initialize decoder
data_ = sound->GetData();
dataSize_ = sound->GetDataSize();
int error;
decoder_ = stb_vorbis_open_memory((unsigned char*)data_.Get(), dataSize_, &error, 0);
}
OggVorbisSoundStream::~OggVorbisSoundStream()
{
// Close decoder
if (decoder_)
{
stb_vorbis* vorbis = static_cast<stb_vorbis*>(decoder_);
stb_vorbis_close(vorbis);
decoder_ = 0;
}
}
unsigned OggVorbisSoundStream::GetData(signed char* dest, unsigned numBytes)
{
if (!decoder_)
return 0;
stb_vorbis* vorbis = static_cast<stb_vorbis*>(decoder_);
unsigned channels = stereo_ ? 2 : 1;
unsigned outSamples = stb_vorbis_get_samples_short_interleaved(vorbis, channels, (short*)dest, numBytes >> 1);
// Rewind and retry if produced no output and should loop
if (!outSamples && !stopAtEnd_)
{
stb_vorbis_seek_start(vorbis);
outSamples = stb_vorbis_get_samples_short_interleaved(vorbis, channels, (short*)dest, numBytes >> 1);
}
return (outSamples * channels) << 1;
}
}

Просмотреть файл

@ -0,0 +1,54 @@
//
// Copyright (c) 2008-2014 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#pragma once
#include "ArrayPtr.h"
#include "SoundStream.h"
namespace Urho3D
{
class Sound;
/// Ogg Vorbis sound stream.
class URHO3D_API OggVorbisSoundStream : public SoundStream
{
public:
/// Construct from an Ogg Vorbis compressed sound.
OggVorbisSoundStream(const Sound* sound);
/// Destruct.
~OggVorbisSoundStream();
/// Produce sound data into destination. Return number of bytes produced. Called by SoundSource from the mixing thread.
virtual unsigned GetData(signed char* dest, unsigned numBytes);
protected:
/// Decoder state.
void* decoder_;
/// Compressed sound data.
SharedArrayPtr<signed char> data_;
/// Compressed sound data size in bytes.
unsigned dataSize_;
};
}

Просмотреть файл

@ -24,6 +24,7 @@
#include "Context.h"
#include "FileSystem.h"
#include "Log.h"
#include "OggVorbisSoundStream.h"
#include "Profiler.h"
#include "ResourceCache.h"
#include "Sound.h"
@ -309,43 +310,9 @@ void Sound::FixInterpolation()
}
}
void* Sound::AllocateDecoder()
SharedPtr<SoundStream> Sound::GetDecoderStream() const
{
if (!compressed_)
return 0;
int error;
stb_vorbis* vorbis = stb_vorbis_open_memory((unsigned char*)data_.Get(), dataSize_, &error, 0);
return vorbis;
}
unsigned Sound::Decode(void* decoder, signed char* dest, unsigned bytes)
{
if (!decoder)
return 0;
unsigned soundSources = stereo_ ? 2 : 1;
stb_vorbis* vorbis = static_cast<stb_vorbis*>(decoder);
unsigned outSamples = stb_vorbis_get_samples_short_interleaved(vorbis, soundSources, (short*)dest, bytes >> 1);
return (outSamples * soundSources) << 1;
}
void Sound::RewindDecoder(void* decoder)
{
if (!decoder)
return;
stb_vorbis* vorbis = static_cast<stb_vorbis*>(decoder);
stb_vorbis_seek_start(vorbis);
}
void Sound::FreeDecoder(void* decoder)
{
if (!decoder)
return;
stb_vorbis* vorbis = static_cast<stb_vorbis*>(decoder);
stb_vorbis_close(vorbis);
return compressed_ ? SharedPtr<SoundStream>(new OggVorbisSoundStream(this)) : SharedPtr<SoundStream>();
}
float Sound::GetLength() const

Просмотреть файл

@ -28,6 +28,8 @@
namespace Urho3D
{
class SoundStream;
/// %Sound resource.
class URHO3D_API Sound : public Resource
{
@ -63,15 +65,10 @@ public:
/// Fix interpolation by copying data from loop start to loop end (looped), or adding silence (oneshot.)
void FixInterpolation();
/// Create and return a compressed audio decoder instance. Return null if fails.
void* AllocateDecoder();
/// Decode compressed audio data. Return number of actually decoded bytes.
unsigned Decode(void* decoder, signed char* dest, unsigned bytes);
/// Rewind the decoder to beginning of audio data.
void RewindDecoder(void* decoder);
/// Free the decoder instance.
void FreeDecoder(void* decoder);
/// Return a new instance of a decoder sound stream. Used by compressed sounds.
SharedPtr<SoundStream> GetDecoderStream() const;
/// Return shared sound data.
SharedArrayPtr<signed char> GetData() const { return data_; }
/// Return sound data start.
signed char* GetStart() const { return data_.Get(); }
/// Return loop start.
@ -85,16 +82,16 @@ public:
/// Return sample size.
unsigned GetSampleSize() const;
/// Return default frequency as a float.
float GetFrequency() { return (float)frequency_; }
float GetFrequency() const { return (float)frequency_; }
/// Return default frequency as an integer.
unsigned GetIntFrequency() { return frequency_; }
unsigned GetIntFrequency() const { return frequency_; }
/// Return whether is looped.
bool IsLooped() const { return looped_; }
/// Return whether data is sixteen bit.
bool IsSixteenBit() const { return sixteenBit_; }
/// Return whether data is stereo.
bool IsStereo() const { return stereo_; }
/// Return whether is compressed in Ogg Vorbis format.
/// Return whether is compressed.
bool IsCompressed() const { return compressed_; }
private:

Просмотреть файл

@ -26,6 +26,7 @@
#include "ResourceCache.h"
#include "Sound.h"
#include "SoundSource.h"
#include "SoundStream.h"
#include <cstring>
@ -115,8 +116,8 @@ SoundSource::SoundSource(Context* context) :
position_(0),
fractPosition_(0),
timePosition_(0.0f),
decoder_(0),
decodePosition_(0)
streamWritePosition_(0),
streamStopped_(false)
{
audio_ = GetSubsystem<Audio>();
@ -128,8 +129,6 @@ SoundSource::~SoundSource()
{
if (audio_)
audio_->RemoveSoundSource(this);
FreeDecoder();
}
void SoundSource::RegisterObject(Context* context)
@ -152,7 +151,7 @@ void SoundSource::Play(Sound* sound)
{
if (!audio_)
return;
// If no frequency set yet, set from the sound's default
if (frequency_ == 0.0f && sound)
SetFrequency(sound->GetFrequency());
@ -190,6 +189,34 @@ void SoundSource::Play(Sound* sound, float frequency, float gain, float panning)
Play(sound);
}
void SoundSource::Play(SoundStream* stream)
{
if (!audio_)
return;
// If no frequency set yet, set from the stream's default
if (frequency_ == 0.0f && stream)
SetFrequency(stream->GetFrequency());
SharedPtr<SoundStream> streamPtr(stream);
// If sound source is currently playing, have to lock the audio mutex. When stream playback is explicitly
// requested, clear the existing sound if any
if (position_)
{
MutexLock lock(audio_->GetMutex());
sound_.Reset();
PlayLockless(streamPtr);
}
else
{
sound_.Reset();
PlayLockless(streamPtr);
}
// Stream playback is not supported for network replication, no need to mark network dirty
}
void SoundSource::Stop()
{
if (!audio_)
@ -201,10 +228,9 @@ void SoundSource::Stop()
MutexLock lock(audio_->GetMutex());
StopLockless();
}
// Free the compressed sound decoder now if any
FreeDecoder();
else
StopLockless();
MarkNetworkUpdate();
}
@ -248,90 +274,19 @@ void SoundSource::SetAutoRemove(bool enable)
bool SoundSource::IsPlaying() const
{
return sound_ != 0 && position_ != 0;
return (sound_ || soundStream_) && position_ != 0;
}
void SoundSource::SetPlayPosition(signed char* pos)
{
if (!audio_ || !sound_)
// Setting play position on a stream is not supported
if (!audio_ || !sound_ || soundStream_)
return;
MutexLock lock(audio_->GetMutex());
SetPlayPositionLockless(pos);
}
void SoundSource::PlayLockless(Sound* sound)
{
// Reset the time position in any case
timePosition_ = 0.0f;
if (sound)
{
if (!sound->IsCompressed())
{
// Uncompressed sound start
signed char* start = sound->GetStart();
if (start)
{
// Free decoder in case previous sound was compressed
FreeDecoder();
sound_ = sound;
position_ = start;
fractPosition_ = 0;
return;
}
}
else
{
// Compressed sound start
if (sound == sound_ && decoder_)
{
// If same compressed sound is already playing, rewind the decoder
sound_->RewindDecoder(decoder_);
return;
}
else
{
// Else just set the new sound with a dummy start position. The mixing routine will allocate the new decoder
FreeDecoder();
sound_ = sound;
position_ = sound->GetStart();
return;
}
}
}
// If sound pointer is null or if sound has no data, stop playback
FreeDecoder();
sound_.Reset();
position_ = 0;
}
void SoundSource::StopLockless()
{
position_ = 0;
timePosition_ = 0.0f;
}
void SoundSource::SetPlayPositionLockless(signed char* pos)
{
// Setting position on a compressed sound is not supported
if (!sound_ || sound_->IsCompressed())
return;
signed char* start = sound_->GetStart();
signed char* end = sound_->GetEnd();
if (pos < start)
pos = start;
if (sound_->IsSixteenBit() && (pos - start) & 1)
++pos;
if (pos > end)
pos = end;
position_ = pos;
timePosition_ = ((float)(int)(size_t)(pos - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
}
void SoundSource::Update(float timeStep)
{
if (!audio_ || !IsEnabledEffective())
@ -341,9 +296,9 @@ void SoundSource::Update(float timeStep)
if (!audio_->IsInitialized())
MixNull(timeStep);
// Free the decoder if playback has stopped
if (!position_ && decoder_)
FreeDecoder();
// Free the stream if playback has stopped
if (soundStream_ && !position_)
StopLockless();
// Check for autoremove
if (autoRemove_)
@ -365,92 +320,73 @@ void SoundSource::Update(float timeStep)
void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, bool interpolation)
{
if (!position_ || !sound_ || !IsEnabledEffective())
if (!position_ || (!sound_ && !soundStream_) || !IsEnabledEffective())
return;
if (sound_->IsCompressed())
if (soundStream_ && streamBuffer_)
{
if (decoder_)
unsigned streamBufferSize = streamBuffer_->GetDataSize();
// Decode new audio in stream mode. If stream experienced an underrun, try restarting
if (streamStopped_)
{
// If decoder already exists, decode new compressed audio
bool eof = false;
unsigned currentPos = position_ - decodeBuffer_->GetStart();
unsigned totalBytes;
// Handle possible wraparound
if (currentPos >= decodePosition_)
totalBytes = currentPos - decodePosition_;
// If stream should stop at end, do not loop after current buffer is played
if (soundStream_->GetStopAtEnd())
streamBuffer_->SetLooped(false);
else
totalBytes = decodeBuffer_->GetDataSize() - decodePosition_ + currentPos;
while (totalBytes)
{
// Calculate size of current decode work unit (may need to do in two parts if wrapping)
unsigned bytes = decodeBuffer_->GetDataSize() - decodePosition_;
if (bytes > totalBytes)
bytes = totalBytes;
unsigned outBytes = 0;
if (!eof)
unsigned outBytes = soundStream_->GetData(streamBuffer_->GetStart(), streamBufferSize);
if (outBytes)
{
outBytes = sound_->Decode(decoder_, decodeBuffer_->GetStart() + decodePosition_, bytes);
// If decoded less than the requested amount, has reached end. Rewind (looped) or fill rest with zero (oneshot)
if (outBytes < bytes)
{
if (sound_->IsLooped())
{
sound_->RewindDecoder(decoder_);
timePosition_ = 0.0f;
}
else
{
decodeBuffer_->SetLooped(false); // Stop after the current decode buffer has been played
eof = true;
}
}
// If did not get a full buffer, fill the rest with zero
if (outBytes < streamBufferSize)
memset(streamBuffer_->GetStart() + outBytes, 0, streamBufferSize - outBytes);
streamStopped_ = false;
// Start playback from beginning of stream buffer again so that there is minimal latency
position_ = streamBuffer_->GetStart();
fractPosition_ = 0;
streamWritePosition_ = 0;
}
else
{
memset(decodeBuffer_->GetStart() + decodePosition_, 0, bytes);
outBytes = bytes;
}
decodePosition_ += outBytes;
decodePosition_ %= decodeBuffer_->GetDataSize();
totalBytes -= outBytes;
}
// Correct interpolation of the looping buffer
decodeBuffer_->FixInterpolation();
}
else
{
// Setup the decoder and decode initial buffer
decoder_ = sound_->AllocateDecoder();
unsigned sampleSize = sound_->GetSampleSize();
unsigned decodeBufferSize = sampleSize * sound_->GetIntFrequency() * DECODE_BUFFER_LENGTH / 1000;
decodeBuffer_ = new Sound(context_);
decodeBuffer_->SetSize(decodeBufferSize);
decodeBuffer_->SetFormat(sound_->GetIntFrequency(), true, sound_->IsStereo());
// Clear the decode buffer, then fill with initial audio data and set it to loop
memset(decodeBuffer_->GetStart(), 0, decodeBufferSize);
sound_->Decode(decoder_, decodeBuffer_->GetStart(), decodeBufferSize);
decodeBuffer_->SetLooped(true);
decodePosition_ = 0;
// Start playing the decode buffer
position_ = decodeBuffer_->GetStart();
fractPosition_ = 0;
unsigned currentPos = position_ - streamBuffer_->GetStart();
unsigned totalBytes;
// Correct initial interpolation of the looping buffer
decodeBuffer_->FixInterpolation();
// Handle possible wraparound
if (currentPos >= streamWritePosition_)
totalBytes = currentPos - streamWritePosition_;
else
totalBytes = streamBuffer_->GetDataSize() - streamWritePosition_ + currentPos;
while (totalBytes)
{
// Calculate size of current stream data request (may need to do in two parts if wrapping)
unsigned bytes = streamBuffer_->GetDataSize() - streamWritePosition_;
if (bytes > totalBytes)
bytes = totalBytes;
unsigned outBytes = soundStream_->GetData(streamBuffer_->GetStart() + streamWritePosition_, bytes);
// If got less than the requested amount, stream reached end or experienced underrun. Fill rest with zero
if (outBytes < bytes)
{
streamStopped_ = true;
memset(streamBuffer_->GetStart() + streamWritePosition_ + outBytes, 0, bytes - outBytes);
}
streamWritePosition_ += bytes;
streamWritePosition_ %= streamBuffer_->GetDataSize();
totalBytes -= bytes;
}
}
// Correct interpolation of the stream buffer
streamBuffer_->FixInterpolation();
}
// If compressed, play the decode buffer. Otherwise play the original sound
Sound* sound = sound_->IsCompressed() ? decodeBuffer_ : sound_;
// If streaming, play the stream buffer. Otherwise play the original sound
Sound* sound = soundStream_ ? streamBuffer_ : sound_;
if (!sound)
return;
@ -491,10 +427,10 @@ void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, boo
}
// Update the time position
if (!sound_->IsCompressed())
if (soundStream_)
timePosition_ += ((float)samples / (float)mixRate) * frequency_ / soundStream_->GetFrequency();
else if (sound_)
timePosition_ = ((float)(int)(size_t)(position_ - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
else
timePosition_ += ((float)samples / (float)mixRate) * frequency_ / sound_->GetFrequency();
}
void SoundSource::SetSoundAttr(ResourceRef value)
@ -505,8 +441,9 @@ void SoundSource::SetSoundAttr(ResourceRef value)
Play(newSound);
else
{
// When changing the sound and not playing, make sure the old decoder (if any) is freed
FreeDecoder();
// When changing the sound and not playing, free previous sound stream and stream buffer (if any)
soundStream_.Reset();
streamBuffer_.Reset();
sound_ = newSound;
}
}
@ -541,6 +478,104 @@ int SoundSource::GetPositionAttr() const
return 0;
}
void SoundSource::PlayLockless(Sound* sound)
{
// Reset the time position in any case
timePosition_ = 0.0f;
if (sound)
{
if (!sound->IsCompressed())
{
// Uncompressed sound start
signed char* start = sound->GetStart();
if (start)
{
// Free existing stream & stream buffer if any
soundStream_.Reset();
streamBuffer_.Reset();
sound_ = sound;
position_ = start;
fractPosition_ = 0;
return;
}
}
else
{
// Compressed sound start
PlayLockless(sound->GetDecoderStream());
return;
}
}
// If sound pointer is null or if sound has no data, stop playback
StopLockless();
sound_.Reset();
}
void SoundSource::PlayLockless(SharedPtr<SoundStream> stream)
{
// Reset the time position in any case
timePosition_ = 0.0f;
if (stream)
{
// Setup the stream buffer
unsigned sampleSize = stream->GetSampleSize();
unsigned streamBufferSize = sampleSize * stream->GetIntFrequency() * STREAM_BUFFER_LENGTH / 1000;
streamBuffer_ = new Sound(context_);
streamBuffer_->SetSize(streamBufferSize);
streamBuffer_->SetFormat(stream->GetIntFrequency(), stream->IsSixteenBit(), stream->IsStereo());
streamBuffer_->SetLooped(true);
// Fill stream buffer with initial data
unsigned outBytes = stream->GetData(streamBuffer_->GetStart(), streamBufferSize);
if (outBytes < streamBufferSize)
memset(streamBuffer_->GetStart() + outBytes, 0, streamBufferSize - outBytes);
soundStream_ = stream;
streamWritePosition_ = 0;
streamStopped_ = false;
position_ = streamBuffer_->GetStart();
fractPosition_ = 0;
return;
}
// If stream pointer is null, stop playback
StopLockless();
}
void SoundSource::StopLockless()
{
position_ = 0;
timePosition_ = 0.0f;
streamStopped_ = true;
// Free the sound stream and decode buffer if a stream was playing
soundStream_.Reset();
streamBuffer_.Reset();
}
void SoundSource::SetPlayPositionLockless(signed char* pos)
{
// Setting position on a stream is not supported
if (!sound_ || soundStream_)
return;
signed char* start = sound_->GetStart();
signed char* end = sound_->GetEnd();
if (pos < start)
pos = start;
if (sound_->IsSixteenBit() && (pos - start) & 1)
++pos;
if (pos > end)
pos = end;
position_ = pos;
timePosition_ = ((float)(int)(size_t)(pos - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
}
void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
{
float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
@ -1224,15 +1259,4 @@ void SoundSource::MixNull(float timeStep)
}
}
void SoundSource::FreeDecoder()
{
if (sound_ && decoder_)
{
sound_->FreeDecoder(decoder_);
decoder_ = 0;
}
decodeBuffer_.Reset();
}
}

Просмотреть файл

@ -30,9 +30,10 @@ namespace Urho3D
class Audio;
class Sound;
class SoundStream;
// Compressed audio decode buffer length in milliseconds
static const int DECODE_BUFFER_LENGTH = 100;
static const int STREAM_BUFFER_LENGTH = 100;
/// %Sound source component with stereo position.
class URHO3D_API SoundSource : public Component
@ -55,6 +56,8 @@ public:
void Play(Sound* sound, float frequency, float gain);
/// Play a sound with specified frequency, gain and panning.
void Play(Sound* sound, float frequency, float gain, float panning);
/// Start playing a sound stream.
void Play(SoundStream* stream);
/// Stop playback.
void Stop();
/// Set sound type, determines the master gain group.
@ -93,12 +96,6 @@ public:
/// Return whether is playing.
bool IsPlaying() const;
/// Play a sound without locking the audio mutex. Called internally.
void PlayLockless(Sound* sound);
/// Stop sound without locking the audio mutex. Called internally.
void StopLockless();
/// Set new playback position without locking the audio mutex. Called internally.
void SetPlayPositionLockless(signed char* position);
/// Update the sound source. Perform subclass specific operations. Called by Audio.
virtual void Update(float timeStep);
/// Mix sound source output to a 32-bit clipping buffer. Called by Audio.
@ -134,6 +131,14 @@ protected:
bool autoRemove_;
private:
/// Play a sound without locking the audio mutex. Called internally.
void PlayLockless(Sound* sound);
/// Play a sound stream without locking the audio mutex. Called internally.
void PlayLockless(SharedPtr<SoundStream> stream);
/// Stop sound without locking the audio mutex. Called internally.
void StopLockless();
/// Set new playback position without locking the audio mutex. Called internally.
void SetPlayPositionLockless(signed char* position);
/// Mix mono sample to mono buffer.
void MixMonoToMono(Sound* sound, int* dest, unsigned samples, int mixRate);
/// Mix mono sample to stereo buffer.
@ -154,23 +159,23 @@ private:
void MixZeroVolume(Sound* sound, unsigned samples, int mixRate);
/// Advance playback pointer to simulate audio playback in headless mode.
void MixNull(float timeStep);
/// Free the decoder if any.
void FreeDecoder();
/// Sound.
/// Sound that is being played.
SharedPtr<Sound> sound_;
/// Sound stream that is being played.
SharedPtr<SoundStream> soundStream_;
/// Playback position.
volatile signed char *position_;
/// Playback fractional position.
volatile int fractPosition_;
/// Playback time position.
volatile float timePosition_;
/// Ogg Vorbis decoder.
void* decoder_;
/// Decode buffer.
SharedPtr<Sound> decodeBuffer_;
/// Previous decode buffer position.
unsigned decodePosition_;
SharedPtr<Sound> streamBuffer_;
/// Position in stream buffer the next audio data from the stream will be written to.
unsigned streamWritePosition_;
/// Stream underrun flag.
bool streamStopped_;
};
}

Просмотреть файл

@ -0,0 +1,65 @@
//
// Copyright (c) 2008-2014 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "Precompiled.h"
#include "SoundStream.h"
#include "DebugNew.h"
namespace Urho3D
{
SoundStream::SoundStream() :
frequency_(44100),
stopAtEnd_(false),
sixteenBit_(false),
stereo_(false)
{
}
SoundStream::~SoundStream()
{
}
void SoundStream::SetFormat(unsigned frequency, bool sixteenBit, bool stereo)
{
frequency_ = frequency;
sixteenBit_ = sixteenBit;
stereo_ = stereo;
}
void SoundStream::SetStopAtEnd(bool enable)
{
stopAtEnd_ = enable;
}
unsigned SoundStream::GetSampleSize() const
{
unsigned size = 1;
if (sixteenBit_)
size <<= 1;
if (stereo_)
size <<= 1;
return size;
}
};

Просмотреть файл

@ -0,0 +1,71 @@
//
// Copyright (c) 2008-2014 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#pragma once
#include "RefCounted.h"
namespace Urho3D
{
/// Base class for sound streams.
class URHO3D_API SoundStream : public RefCounted
{
public:
/// Construct.
SoundStream();
/// Destruct.
~SoundStream();
/// Produce sound data into destination. Return number of bytes produced. Called by SoundSource from the mixing thread.
virtual unsigned GetData(signed char* dest, unsigned numBytes) = 0;
/// Set sound data format.
void SetFormat(unsigned frequency, bool sixteenBit, bool stereo);
/// Set whether playback should stop when no more data (GetData() returns 0 bytes.) Default false.
void SetStopAtEnd(bool enable);
/// Return sample size.
unsigned GetSampleSize() const;
/// Return default frequency as a float.
float GetFrequency() { return (float)frequency_; }
/// Return default frequency as an integer.
unsigned GetIntFrequency() { return frequency_; }
/// Return whether playback should stop when no more data.
bool GetStopAtEnd() const { return stopAtEnd_; }
/// Return whether data is sixteen bit.
bool IsSixteenBit() const { return sixteenBit_; }
/// Return whether data is stereo.
bool IsStereo() const { return stereo_; }
protected:
/// Default frequency.
unsigned frequency_;
/// Stop when no more data flag.
bool stopAtEnd_;
/// Sixteen bit flag.
bool sixteenBit_;
/// Stereo flag.
bool stereo_;
};
}

Просмотреть файл

@ -22,8 +22,8 @@ class Sound : public Resource
float GetLength() const;
unsigned GetDataSize() const;
unsigned GetSampleSize() const;
float GetFrequency();
unsigned GetIntFrequency();
float GetFrequency() const;
unsigned GetIntFrequency() const;
bool IsLooped() const;
bool IsSixteenBit() const;
bool IsStereo() const;

Просмотреть файл

@ -26,9 +26,6 @@ class SoundSource : public Component
bool GetAutoRemove() const;
bool IsPlaying() const;
void PlayLockless(Sound* sound);
void StopLockless();
tolua_readonly tolua_property__get_set Sound* sound;
tolua_property__get_set SoundType soundType;
tolua_readonly tolua_property__get_set float timePosition;