mirror of https://github.com/OpenRCT2/OpenRCT2.git
Make Channel an interface
This commit is contained in:
parent
0d4531d10d
commit
46b4dbb4cb
|
@ -346,95 +346,225 @@ void Source_SampleStream::Unload()
|
||||||
buffersize = 0;
|
buffersize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel::Channel()
|
class AudioChannel : public IAudioChannel
|
||||||
{
|
{
|
||||||
SetRate(1);
|
private:
|
||||||
SetVolume(SDL_MIX_MAXVOLUME);
|
Source * _source = nullptr;
|
||||||
SetPan(0.5f);
|
SpeexResamplerState * _resampler = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
Channel::~Channel()
|
int _group = MIXER_GROUP_SOUND;
|
||||||
{
|
double _rate = 0;
|
||||||
if (resampler) {
|
unsigned long _offset = 0;
|
||||||
speex_resampler_destroy(resampler);
|
int _loop = 0;
|
||||||
resampler = 0;
|
|
||||||
}
|
|
||||||
if (deletesourceondone) {
|
|
||||||
delete source;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Channel::Play(Source& source, int loop = MIXER_LOOP_NONE)
|
int _volume = 1;
|
||||||
{
|
float _volume_l = 0.f;
|
||||||
Channel::source = &source;
|
float _volume_r = 0.f;
|
||||||
Channel::loop = loop;
|
float _oldvolume_l = 0.f;
|
||||||
offset = 0;
|
float _oldvolume_r = 0.f;
|
||||||
done = false;
|
int _oldvolume = 0;
|
||||||
}
|
float _pan = 0;
|
||||||
|
|
||||||
void Channel::SetRate(double rate)
|
bool _stopping = false;
|
||||||
{
|
bool _done = true;
|
||||||
Channel::rate = rate;
|
bool _deleteondone = false;
|
||||||
if (Channel::rate < 0.001) {
|
bool _deletesourceondone = false;
|
||||||
Channel::rate = 0.001;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Channel::SetVolume(int volume)
|
public:
|
||||||
{
|
AudioChannel()
|
||||||
Channel::volume = volume;
|
{
|
||||||
if (volume > SDL_MIX_MAXVOLUME) {
|
SetRate(1);
|
||||||
Channel::volume = SDL_MIX_MAXVOLUME;
|
SetVolume(SDL_MIX_MAXVOLUME);
|
||||||
}
|
SetPan(0.5f);
|
||||||
if (volume < 0) {
|
}
|
||||||
Channel::volume = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Channel::SetPan(float pan)
|
~AudioChannel() override
|
||||||
{
|
{
|
||||||
Channel::pan = pan;
|
if (_resampler != nullptr)
|
||||||
if (pan > 1) {
|
{
|
||||||
Channel::pan = 1;
|
speex_resampler_destroy(_resampler);
|
||||||
}
|
_resampler = nullptr;
|
||||||
if (pan < 0) {
|
}
|
||||||
Channel::pan = 0;
|
if (_deletesourceondone)
|
||||||
}
|
{
|
||||||
double decibels = (std::abs(Channel::pan - 0.5) * 2.0) * 100.0;
|
delete _source;
|
||||||
double attenuation = pow(10, decibels / 20.0);
|
}
|
||||||
if (Channel::pan <= 0.5) {
|
}
|
||||||
volume_l = 1.0;
|
|
||||||
volume_r = float(1.0 / attenuation);
|
|
||||||
} else {
|
|
||||||
volume_r = 1.0;
|
|
||||||
volume_l = float(1.0 / attenuation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Channel::IsPlaying()
|
Source * GetSource() const override
|
||||||
{
|
{
|
||||||
return !done;
|
return _source;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long Channel::GetOffset()
|
SpeexResamplerState * GetResampler() const override
|
||||||
{
|
{
|
||||||
return offset;
|
return _resampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Channel::SetOffset(unsigned long offset)
|
void SetResampler(SpeexResamplerState * value) override
|
||||||
{
|
{
|
||||||
if (source && offset < source->Length()) {
|
_resampler = value;
|
||||||
int samplesize = source->Format().channels * source->Format().BytesPerSample();
|
}
|
||||||
Channel::offset = (offset / samplesize) * samplesize;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Channel::SetGroup(int group)
|
int GetGroup() const override
|
||||||
{
|
{
|
||||||
Channel::group = group;
|
return _group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetGroup(int group)
|
||||||
|
{
|
||||||
|
_group = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetRate() const override
|
||||||
|
{
|
||||||
|
return _rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetRate(double rate)
|
||||||
|
{
|
||||||
|
_rate = Math::Max(0.001, rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long GetOffset() const override
|
||||||
|
{
|
||||||
|
return _offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetOffset(unsigned long offset)
|
||||||
|
{
|
||||||
|
if (_source && offset < _source->Length())
|
||||||
|
{
|
||||||
|
AudioFormat format = _source->Format();
|
||||||
|
int samplesize = format.channels * format.BytesPerSample();
|
||||||
|
_offset = (offset / samplesize) * samplesize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int GetLoop() const override
|
||||||
|
{
|
||||||
|
return _loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetLoop(int value) override
|
||||||
|
{
|
||||||
|
_loop = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetVolume() const override
|
||||||
|
{
|
||||||
|
return _volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetVolumeL() const override
|
||||||
|
{
|
||||||
|
return _volume_l;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetVolumeR() const override
|
||||||
|
{
|
||||||
|
return _volume_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetOldVolumeL() const override
|
||||||
|
{
|
||||||
|
return _oldvolume_l;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetOldVolumeR() const override
|
||||||
|
{
|
||||||
|
return _oldvolume_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetOldVolume() const override
|
||||||
|
{
|
||||||
|
return _oldvolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetVolume(int volume) override
|
||||||
|
{
|
||||||
|
_volume = Math::Clamp(0, volume, SDL_MIX_MAXVOLUME);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetPan() const override
|
||||||
|
{
|
||||||
|
return _pan;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPan(float pan)
|
||||||
|
{
|
||||||
|
_pan = Math::Clamp(0.0f, pan, 1.0f);
|
||||||
|
double decibels = (std::abs(_pan - 0.5) * 2.0) * 100.0;
|
||||||
|
double attenuation = pow(10, decibels / 20.0);
|
||||||
|
if (_pan <= 0.5)
|
||||||
|
{
|
||||||
|
_volume_l = 1.0;
|
||||||
|
_volume_r = (float)(1.0 / attenuation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_volume_r = 1.0;
|
||||||
|
_volume_l = (float)(1.0 / attenuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsStopping() const override
|
||||||
|
{
|
||||||
|
return _stopping;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStopping(bool value) override
|
||||||
|
{
|
||||||
|
_stopping = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDone() const override
|
||||||
|
{
|
||||||
|
return _done;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDone(bool value) override
|
||||||
|
{
|
||||||
|
_done = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeleteOnDone() const
|
||||||
|
{
|
||||||
|
return _deleteondone;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDeleteOnDone(bool value) override
|
||||||
|
{
|
||||||
|
_deleteondone = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDeleteSourceOnDone(bool value) override
|
||||||
|
{
|
||||||
|
_deletesourceondone = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPlaying() const override
|
||||||
|
{
|
||||||
|
return !_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Play(Source& source, int loop)
|
||||||
|
{
|
||||||
|
_source = &source;
|
||||||
|
_loop = loop;
|
||||||
|
_offset = 0;
|
||||||
|
_done = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateOldVolume() override
|
||||||
|
{
|
||||||
|
_oldvolume = _volume;
|
||||||
|
_oldvolume_l = _volume_l;
|
||||||
|
_oldvolume_r = _volume_r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class AudioMixer : public IAudioMixer
|
class AudioMixer : public IAudioMixer
|
||||||
{
|
{
|
||||||
|
@ -442,7 +572,7 @@ private:
|
||||||
SDL_AudioDeviceID deviceid = 0;
|
SDL_AudioDeviceID deviceid = 0;
|
||||||
AudioFormat format = { 0 };
|
AudioFormat format = { 0 };
|
||||||
uint8 * effectbuffer = nullptr;
|
uint8 * effectbuffer = nullptr;
|
||||||
std::list<Channel *> channels;
|
std::list<IAudioChannel *> channels;
|
||||||
Source_Null source_null;
|
Source_Null source_null;
|
||||||
float volume = 1.0f;
|
float volume = 1.0f;
|
||||||
float adjust_sound_vol = 0.0f;
|
float adjust_sound_vol = 0.0f;
|
||||||
|
@ -531,24 +661,25 @@ public:
|
||||||
SDL_UnlockAudioDevice(deviceid);
|
SDL_UnlockAudioDevice(deviceid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel * Play(Source& source, int loop, bool deleteondone, bool deletesourceondone) override
|
IAudioChannel * Play(Source& source, int loop, bool deleteondone, bool deletesourceondone) override
|
||||||
{
|
{
|
||||||
Lock();
|
Lock();
|
||||||
Channel* newchannel = new (std::nothrow) Channel;
|
IAudioChannel * newchannel = new (std::nothrow) AudioChannel;
|
||||||
if (newchannel) {
|
if (newchannel != nullptr)
|
||||||
|
{
|
||||||
newchannel->Play(source, loop);
|
newchannel->Play(source, loop);
|
||||||
newchannel->deleteondone = deleteondone;
|
newchannel->SetDeleteOnDone(deleteondone);
|
||||||
newchannel->deletesourceondone = deletesourceondone;
|
newchannel->SetDeleteSourceOnDone(deletesourceondone);
|
||||||
channels.push_back(newchannel);
|
channels.push_back(newchannel);
|
||||||
}
|
}
|
||||||
Unlock();
|
Unlock();
|
||||||
return newchannel;
|
return newchannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stop(Channel& channel) override
|
void Stop(IAudioChannel * channel) override
|
||||||
{
|
{
|
||||||
Lock();
|
Lock();
|
||||||
channel.stopping = true;
|
channel->SetStopping(true);
|
||||||
Unlock();
|
Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,109 +723,137 @@ private:
|
||||||
static void SDLCALL Callback(void * arg, uint8 * stream, int length)
|
static void SDLCALL Callback(void * arg, uint8 * stream, int length)
|
||||||
{
|
{
|
||||||
auto mixer = static_cast<AudioMixer *>(arg);
|
auto mixer = static_cast<AudioMixer *>(arg);
|
||||||
|
|
||||||
memset(stream, 0, length);
|
memset(stream, 0, length);
|
||||||
std::list<Channel*>::iterator i = mixer->channels.begin();
|
std::list<IAudioChannel *>::iterator it = mixer->channels.begin();
|
||||||
while (i != mixer->channels.end()) {
|
while (it != mixer->channels.end())
|
||||||
Channel * channel = *i;
|
{
|
||||||
mixer->MixChannel(*channel, stream, length);
|
IAudioChannel * channel = *it;
|
||||||
if ((channel->done && channel->deleteondone) || channel->stopping)
|
mixer->MixChannel(channel, stream, length);
|
||||||
|
if ((channel->IsDone() && channel->DeleteOnDone()) || channel->IsStopping())
|
||||||
{
|
{
|
||||||
delete channel;
|
delete channel;
|
||||||
i = mixer->channels.erase(i);
|
it = mixer->channels.erase(it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
i++;
|
it++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MixChannel(Channel& channel, uint8* data, int length)
|
void MixChannel(IAudioChannel * channel, uint8* data, int length)
|
||||||
{
|
{
|
||||||
// Did the volume level get changed? Recalculate level in this case.
|
// Did the volume level get changed? Recalculate level in this case.
|
||||||
if (setting_sound_vol != gConfigSound.sound_volume) {
|
if (setting_sound_vol != gConfigSound.sound_volume)
|
||||||
|
{
|
||||||
setting_sound_vol = gConfigSound.sound_volume;
|
setting_sound_vol = gConfigSound.sound_volume;
|
||||||
adjust_sound_vol = powf(setting_sound_vol / 100.f, 10.f / 6.f);
|
adjust_sound_vol = powf(setting_sound_vol / 100.f, 10.f / 6.f);
|
||||||
}
|
}
|
||||||
if (setting_music_vol != gConfigSound.ride_music_volume) {
|
if (setting_music_vol != gConfigSound.ride_music_volume)
|
||||||
|
{
|
||||||
setting_music_vol = gConfigSound.ride_music_volume;
|
setting_music_vol = gConfigSound.ride_music_volume;
|
||||||
adjust_music_vol = powf(setting_music_vol / 100.f, 10.f / 6.f);
|
adjust_music_vol = powf(setting_music_vol / 100.f, 10.f / 6.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not mix channel if channel is a sound and sound is disabled
|
// Do not mix channel if channel is a sound and sound is disabled
|
||||||
if (channel.group == MIXER_GROUP_SOUND && !gConfigSound.sound_enabled) {
|
if (channel->GetGroup() == MIXER_GROUP_SOUND && !gConfigSound.sound_enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel.source && channel.source->Length() > 0 && !channel.done) {
|
Source * source = channel->GetSource();
|
||||||
AudioFormat streamformat = channel.source->Format();
|
if (source != nullptr && source->Length() > 0 && !channel->IsDone())
|
||||||
|
{
|
||||||
|
AudioFormat streamformat = source->Format();
|
||||||
int loaded = 0;
|
int loaded = 0;
|
||||||
SDL_AudioCVT cvt;
|
SDL_AudioCVT cvt;
|
||||||
cvt.len_ratio = 1;
|
cvt.len_ratio = 1;
|
||||||
do {
|
do
|
||||||
|
{
|
||||||
int samplesize = format.channels * format.BytesPerSample();
|
int samplesize = format.channels * format.BytesPerSample();
|
||||||
int samples = length / samplesize;
|
int samples = length / samplesize;
|
||||||
int samplesloaded = loaded / samplesize;
|
int samplesloaded = loaded / samplesize;
|
||||||
double rate = 1;
|
double rate = 1;
|
||||||
if (format.format == AUDIO_S16SYS) {
|
if (format.format == AUDIO_S16SYS)
|
||||||
rate = channel.rate;
|
{
|
||||||
|
rate = channel->GetRate();
|
||||||
}
|
}
|
||||||
int samplestoread = (int)((samples - samplesloaded) * rate);
|
int samplestoread = (int)((samples - samplesloaded) * rate);
|
||||||
int lengthloaded = 0;
|
int lengthloaded = 0;
|
||||||
if (channel.offset < channel.source->Length()) {
|
if (channel->GetOffset() < source->Length())
|
||||||
|
{
|
||||||
bool mustconvert = false;
|
bool mustconvert = false;
|
||||||
if (MustConvert(*channel.source)) {
|
if (MustConvert(*source))
|
||||||
if (SDL_BuildAudioCVT(&cvt, streamformat.format, streamformat.channels, streamformat.freq, format.format, format.channels, format.freq) == -1) {
|
{
|
||||||
|
if (SDL_BuildAudioCVT(&cvt, streamformat.format, streamformat.channels, streamformat.freq, format.format, format.channels, format.freq) == -1)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mustconvert = true;
|
mustconvert = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8* datastream = 0;
|
const uint8 * datastream = nullptr;
|
||||||
int toread = (int)(samplestoread / cvt.len_ratio) * samplesize;
|
int toread = (int)(samplestoread / cvt.len_ratio) * samplesize;
|
||||||
int readfromstream = (channel.source->GetSome(channel.offset, &datastream, toread));
|
int readfromstream = source->GetSome(channel->GetOffset(), &datastream, toread);
|
||||||
if (readfromstream == 0) {
|
if (readfromstream == 0)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8* dataconverted = 0;
|
uint8* dataconverted = 0;
|
||||||
const uint8* tomix = 0;
|
const uint8* tomix = 0;
|
||||||
|
|
||||||
if (mustconvert) {
|
if (mustconvert)
|
||||||
|
{
|
||||||
// tofix: there seems to be an issue with converting audio using SDL_ConvertAudio in the callback vs preconverted, can cause pops and static depending on sample rate and channels
|
// tofix: there seems to be an issue with converting audio using SDL_ConvertAudio in the callback vs preconverted, can cause pops and static depending on sample rate and channels
|
||||||
if (Convert(cvt, datastream, readfromstream, &dataconverted)) {
|
if (Convert(cvt, datastream, readfromstream, &dataconverted))
|
||||||
|
{
|
||||||
tomix = dataconverted;
|
tomix = dataconverted;
|
||||||
lengthloaded = cvt.len_cvt;
|
lengthloaded = cvt.len_cvt;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
tomix = datastream;
|
tomix = datastream;
|
||||||
lengthloaded = readfromstream;
|
lengthloaded = readfromstream;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool effectbufferloaded = false;
|
bool effectbufferloaded = false;
|
||||||
if (rate != 1 && format.format == AUDIO_S16SYS) {
|
if (rate != 1 && format.format == AUDIO_S16SYS)
|
||||||
|
{
|
||||||
int in_len = (int)((double)lengthloaded / samplesize);
|
int in_len = (int)((double)lengthloaded / samplesize);
|
||||||
int out_len = samples;
|
int out_len = samples;
|
||||||
if (!channel.resampler) {
|
|
||||||
channel.resampler = speex_resampler_init(format.channels, format.freq, format.freq, 0, 0);
|
SpeexResamplerState * resampler = channel->GetResampler();
|
||||||
|
if (resampler == nullptr)
|
||||||
|
{
|
||||||
|
resampler = speex_resampler_init(format.channels, format.freq, format.freq, 0, 0);
|
||||||
|
channel->SetResampler(resampler);
|
||||||
}
|
}
|
||||||
if (readfromstream == toread) {
|
if (readfromstream == toread)
|
||||||
|
{
|
||||||
// use buffer lengths for conversion ratio so that it fits exactly
|
// use buffer lengths for conversion ratio so that it fits exactly
|
||||||
speex_resampler_set_rate(channel.resampler, in_len, samples - samplesloaded);
|
speex_resampler_set_rate(resampler, in_len, samples - samplesloaded);
|
||||||
} else {
|
|
||||||
// reached end of stream so we cant use buffer length as resampling ratio
|
|
||||||
speex_resampler_set_rate(channel.resampler, format.freq, (int)(format.freq * (1 / rate)));
|
|
||||||
}
|
}
|
||||||
speex_resampler_process_interleaved_int(channel.resampler, (const spx_int16_t*)tomix, (spx_uint32_t*)&in_len, (spx_int16_t*)effectbuffer, (spx_uint32_t*)&out_len);
|
else
|
||||||
|
{
|
||||||
|
// reached end of stream so we cant use buffer length as resampling ratio
|
||||||
|
speex_resampler_set_rate(resampler, format.freq, (int)(format.freq * (1 / rate)));
|
||||||
|
}
|
||||||
|
speex_resampler_process_interleaved_int(resampler, (const spx_int16_t*)tomix, (spx_uint32_t*)&in_len, (spx_int16_t*)effectbuffer, (spx_uint32_t*)&out_len);
|
||||||
effectbufferloaded = true;
|
effectbufferloaded = true;
|
||||||
tomix = effectbuffer;
|
tomix = effectbuffer;
|
||||||
lengthloaded = (out_len * samplesize);
|
lengthloaded = (out_len * samplesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel.pan != 0.5f && format.channels == 2) {
|
if (channel->GetPan() != 0.5f && format.channels == 2)
|
||||||
if (!effectbufferloaded) {
|
{
|
||||||
|
if (!effectbufferloaded)
|
||||||
|
{
|
||||||
memcpy(effectbuffer, tomix, lengthloaded);
|
memcpy(effectbuffer, tomix, lengthloaded);
|
||||||
effectbufferloaded = true;
|
effectbufferloaded = true;
|
||||||
tomix = effectbuffer;
|
tomix = effectbuffer;
|
||||||
|
@ -710,13 +869,14 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
int mixlength = lengthloaded;
|
int mixlength = lengthloaded;
|
||||||
if (loaded + mixlength > length) {
|
if (loaded + mixlength > length)
|
||||||
|
{
|
||||||
mixlength = length - loaded;
|
mixlength = length - loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
float volumeadjust = volume;
|
float volumeadjust = volume;
|
||||||
volumeadjust *= (gConfigSound.master_volume / 100.0f);
|
volumeadjust *= (gConfigSound.master_volume / 100.0f);
|
||||||
switch (channel.group) {
|
switch (channel->GetGroup()) {
|
||||||
case MIXER_GROUP_SOUND:
|
case MIXER_GROUP_SOUND:
|
||||||
volumeadjust *= adjust_sound_vol;
|
volumeadjust *= adjust_sound_vol;
|
||||||
|
|
||||||
|
@ -729,15 +889,18 @@ private:
|
||||||
volumeadjust *= adjust_music_vol;
|
volumeadjust *= adjust_music_vol;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int startvolume = (int)(channel.oldvolume * volumeadjust);
|
int startvolume = (int)(channel->GetOldVolume() * volumeadjust);
|
||||||
int endvolume = (int)(channel.volume * volumeadjust);
|
int endvolume = (int)(channel->GetVolume() * volumeadjust);
|
||||||
if (channel.stopping) {
|
if (channel->IsStopping())
|
||||||
|
{
|
||||||
endvolume = 0;
|
endvolume = 0;
|
||||||
}
|
}
|
||||||
int mixvolume = (int)(channel.volume * volumeadjust);
|
int mixvolume = (int)(channel->GetVolume() * volumeadjust);
|
||||||
if (startvolume != endvolume) {
|
if (startvolume != endvolume)
|
||||||
|
{
|
||||||
// fade between volume levels to smooth out sound and minimize clicks from sudden volume changes
|
// fade between volume levels to smooth out sound and minimize clicks from sudden volume changes
|
||||||
if (!effectbufferloaded) {
|
if (!effectbufferloaded)
|
||||||
|
{
|
||||||
memcpy(effectbuffer, tomix, lengthloaded);
|
memcpy(effectbuffer, tomix, lengthloaded);
|
||||||
effectbufferloaded = true;
|
effectbufferloaded = true;
|
||||||
tomix = effectbuffer;
|
tomix = effectbuffer;
|
||||||
|
@ -756,72 +919,85 @@ private:
|
||||||
|
|
||||||
SDL_MixAudioFormat(&data[loaded], tomix, format.format, mixlength, mixvolume);
|
SDL_MixAudioFormat(&data[loaded], tomix, format.format, mixlength, mixvolume);
|
||||||
|
|
||||||
if (dataconverted) {
|
if (dataconverted)
|
||||||
|
{
|
||||||
delete[] dataconverted;
|
delete[] dataconverted;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.offset += readfromstream;
|
channel->SetOffset(channel->GetOffset() + readfromstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
loaded += lengthloaded;
|
loaded += lengthloaded;
|
||||||
|
|
||||||
if (channel.loop != 0 && channel.offset >= channel.source->Length()) {
|
int loop = channel->GetLoop();
|
||||||
if (channel.loop != -1) {
|
if (loop != 0 && channel->GetOffset() >= source->Length())
|
||||||
channel.loop--;
|
{
|
||||||
|
if (loop != -1)
|
||||||
|
{
|
||||||
|
channel->SetLoop(loop - 1);
|
||||||
}
|
}
|
||||||
channel.offset = 0;
|
channel->SetOffset(0);
|
||||||
}
|
}
|
||||||
} while (loaded < length && channel.loop != 0 && !channel.stopping);
|
}
|
||||||
|
while (loaded < length && channel->GetLoop() != 0 && !channel->IsStopping());
|
||||||
|
|
||||||
channel.oldvolume = channel.volume;
|
channel->UpdateOldVolume();
|
||||||
channel.oldvolume_l = channel.volume_l;
|
if (channel->GetLoop() == 0 && channel->GetOffset() >= source->Length())
|
||||||
channel.oldvolume_r = channel.volume_r;
|
{
|
||||||
if (channel.loop == 0 && channel.offset >= channel.source->Length()) {
|
channel->SetDone(true);
|
||||||
channel.done = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectPanS16(Channel& channel, sint16* data, int length)
|
static void EffectPanS16(const IAudioChannel * channel, sint16 * data, int length)
|
||||||
{
|
{
|
||||||
const float dt = 1.0f / (length * 2);
|
const float dt = 1.0f / (length * 2);
|
||||||
float left_volume = channel.oldvolume_l;
|
float volumeL = channel->GetOldVolumeL();
|
||||||
float right_volume = channel.oldvolume_r;
|
float volumeR = channel->GetOldVolumeR();
|
||||||
const float d_left = dt * (channel.volume_l - channel.oldvolume_l);
|
const float d_left = dt * (channel->GetVolumeL() - channel->GetOldVolumeL());
|
||||||
const float d_right = dt * (channel.volume_r - channel.oldvolume_r);
|
const float d_right = dt * (channel->GetVolumeR() - channel->GetOldVolumeR());
|
||||||
|
|
||||||
for (int i = 0; i < length * 2; i += 2) {
|
for (int i = 0; i < length * 2; i += 2)
|
||||||
data[i] = (sint16)(data[i] * left_volume);
|
{
|
||||||
data[i + 1] = (sint16)(data[i + 1] * right_volume);
|
data[i] = (sint16)(data[i] * volumeL);
|
||||||
left_volume += d_left;
|
data[i + 1] = (sint16)(data[i + 1] * volumeR);
|
||||||
right_volume += d_right;
|
volumeL += d_left;
|
||||||
|
volumeR += d_right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectPanU8(Channel& channel, uint8* data, int length)
|
static void EffectPanU8(const IAudioChannel * channel, uint8 * data, int length)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < length * 2; i += 2) {
|
float volumeL = channel->GetVolumeL();
|
||||||
|
float volumeR = channel->GetVolumeR();
|
||||||
|
float oldVolumeL = channel->GetOldVolumeL();
|
||||||
|
float oldVolumeR = channel->GetOldVolumeR();
|
||||||
|
|
||||||
|
for (int i = 0; i < length * 2; i += 2)
|
||||||
|
{
|
||||||
float t = (float)i / (length * 2);
|
float t = (float)i / (length * 2);
|
||||||
data[i] = (uint8)(data[i] * ((1.0 - t) * channel.oldvolume_l + t * channel.volume_l));
|
data[i] = (uint8)(data[i] * ((1.0 - t) * oldVolumeL + t * volumeL));
|
||||||
data[i + 1] = (uint8)(data[i + 1] * ((1.0 - t) * channel.oldvolume_r + t * channel.volume_r));
|
data[i + 1] = (uint8)(data[i + 1] * ((1.0 - t) * oldVolumeR + t * volumeR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectFadeS16(sint16* data, int length, int startvolume, int endvolume)
|
static void EffectFadeS16(sint16 * data, int length, int startvolume, int endvolume)
|
||||||
{
|
{
|
||||||
float startvolume_f = (float)startvolume / SDL_MIX_MAXVOLUME;
|
float startvolume_f = (float)startvolume / SDL_MIX_MAXVOLUME;
|
||||||
float endvolume_f = (float)endvolume / SDL_MIX_MAXVOLUME;
|
float endvolume_f = (float)endvolume / SDL_MIX_MAXVOLUME;
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
float t = (float)i / length;
|
float t = (float)i / length;
|
||||||
data[i] = (sint16)(data[i] * ((1 - t) * startvolume_f + t * endvolume_f));
|
data[i] = (sint16)(data[i] * ((1 - t) * startvolume_f + t * endvolume_f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectFadeU8(uint8* data, int length, int startvolume, int endvolume)
|
static void EffectFadeU8(uint8* data, int length, int startvolume, int endvolume)
|
||||||
{
|
{
|
||||||
float startvolume_f = (float)startvolume / SDL_MIX_MAXVOLUME;
|
float startvolume_f = (float)startvolume / SDL_MIX_MAXVOLUME;
|
||||||
float endvolume_f = (float)endvolume / SDL_MIX_MAXVOLUME;
|
float endvolume_f = (float)endvolume / SDL_MIX_MAXVOLUME;
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
float t = (float)i / length;
|
float t = (float)i / length;
|
||||||
data[i] = (uint8)(data[i] * ((1 - t) * startvolume_f + t * endvolume_f));
|
data[i] = (uint8)(data[i] * ((1 - t) * startvolume_f + t * endvolume_f));
|
||||||
}
|
}
|
||||||
|
@ -830,7 +1006,10 @@ private:
|
||||||
bool MustConvert(Source& source)
|
bool MustConvert(Source& source)
|
||||||
{
|
{
|
||||||
const AudioFormat sourceformat = source.Format();
|
const AudioFormat sourceformat = source.Format();
|
||||||
if (sourceformat.format != format.format || sourceformat.channels != format.channels || sourceformat.freq != format.freq) {
|
if (sourceformat.format != format.format ||
|
||||||
|
sourceformat.channels != format.channels ||
|
||||||
|
sourceformat.freq != format.freq)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -864,7 +1043,7 @@ void Mixer_Init(const char * device)
|
||||||
|
|
||||||
void * Mixer_Play_Effect(size_t id, int loop, int volume, float pan, double rate, int deleteondone)
|
void * Mixer_Play_Effect(size_t id, int loop, int volume, float pan, double rate, int deleteondone)
|
||||||
{
|
{
|
||||||
Channel * channel = nullptr;
|
IAudioChannel * channel = nullptr;
|
||||||
if (!gOpenRCT2Headless && gConfigSound.sound_enabled)
|
if (!gOpenRCT2Headless && gConfigSound.sound_enabled)
|
||||||
{
|
{
|
||||||
if (id >= SOUND_MAXID)
|
if (id >= SOUND_MAXID)
|
||||||
|
@ -893,7 +1072,7 @@ void Mixer_Stop_Channel(void * channel)
|
||||||
{
|
{
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
gMixer->Stop(*static_cast<Channel*>(channel));
|
gMixer->Stop(static_cast<IAudioChannel*>(channel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,17 +1081,17 @@ void Mixer_Channel_Volume(void * channel, int volume)
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
gMixer->Lock();
|
gMixer->Lock();
|
||||||
static_cast<Channel*>(channel)->SetVolume(volume);
|
static_cast<IAudioChannel*>(channel)->SetVolume(volume);
|
||||||
gMixer->Unlock();
|
gMixer->Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mixer_Channel_Pan(void* channel, float pan)
|
void Mixer_Channel_Pan(void * channel, float pan)
|
||||||
{
|
{
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
gMixer->Lock();
|
gMixer->Lock();
|
||||||
static_cast<Channel*>(channel)->SetPan(pan);
|
static_cast<IAudioChannel*>(channel)->SetPan(pan);
|
||||||
gMixer->Unlock();
|
gMixer->Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -922,7 +1101,7 @@ void Mixer_Channel_Rate(void* channel, double rate)
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
gMixer->Lock();
|
gMixer->Lock();
|
||||||
static_cast<Channel*>(channel)->SetRate(rate);
|
static_cast<IAudioChannel*>(channel)->SetRate(rate);
|
||||||
gMixer->Unlock();
|
gMixer->Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -932,7 +1111,7 @@ int Mixer_Channel_IsPlaying(void * channel)
|
||||||
bool isPlaying = false;
|
bool isPlaying = false;
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
isPlaying = static_cast<Channel*>(channel)->IsPlaying();
|
isPlaying = static_cast<IAudioChannel*>(channel)->IsPlaying();
|
||||||
}
|
}
|
||||||
return isPlaying;
|
return isPlaying;
|
||||||
}
|
}
|
||||||
|
@ -942,7 +1121,7 @@ unsigned long Mixer_Channel_GetOffset(void * channel)
|
||||||
unsigned long offset = 0;
|
unsigned long offset = 0;
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
offset = static_cast<Channel*>(channel)->GetOffset();
|
offset = static_cast<IAudioChannel*>(channel)->GetOffset();
|
||||||
}
|
}
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
@ -952,7 +1131,7 @@ int Mixer_Channel_SetOffset(void * channel, unsigned long offset)
|
||||||
int result = 0;
|
int result = 0;
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
result = static_cast<Channel*>(channel)->SetOffset(offset);
|
result = static_cast<IAudioChannel*>(channel)->SetOffset(offset);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -961,13 +1140,13 @@ void Mixer_Channel_SetGroup(void * channel, int group)
|
||||||
{
|
{
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
static_cast<Channel*>(channel)->SetGroup(group);
|
static_cast<IAudioChannel *>(channel)->SetGroup(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void * Mixer_Play_Music(int pathId, int loop, int streaming)
|
void * Mixer_Play_Music(int pathId, int loop, int streaming)
|
||||||
{
|
{
|
||||||
Channel * channel = nullptr;
|
IAudioChannel * channel = nullptr;
|
||||||
if (!gOpenRCT2Headless)
|
if (!gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
IAudioMixer * mixer = gMixer;
|
IAudioMixer * mixer = gMixer;
|
||||||
|
|
|
@ -120,30 +120,56 @@ class Channel
|
||||||
public:
|
public:
|
||||||
Channel();
|
Channel();
|
||||||
~Channel();
|
~Channel();
|
||||||
void Play(Source& source, int loop);
|
|
||||||
void SetRate(double rate);
|
|
||||||
void SetVolume(int volume);
|
|
||||||
void SetPan(float pan);
|
|
||||||
bool IsPlaying();
|
|
||||||
unsigned long GetOffset();
|
|
||||||
bool SetOffset(unsigned long offset);
|
|
||||||
void SetGroup(int group);
|
|
||||||
|
|
||||||
int loop = 0;
|
};
|
||||||
unsigned long offset = 0;
|
|
||||||
double rate = 0;
|
interface IAudioChannel
|
||||||
int volume = 1;
|
{
|
||||||
float volume_l = 0.f, volume_r = 0.f;
|
virtual ~IAudioChannel() = default;
|
||||||
float oldvolume_l = 0.f, oldvolume_r = 0.f;
|
|
||||||
float pan = 0;
|
virtual Source * GetSource() const abstract;
|
||||||
bool done = true;
|
|
||||||
bool deleteondone = false;
|
virtual SpeexResamplerState * GetResampler() const abstract;
|
||||||
bool deletesourceondone = false;
|
virtual void SetResampler(SpeexResamplerState * value) abstract;
|
||||||
bool stopping = false;
|
|
||||||
int oldvolume = 0;
|
virtual int GetGroup() const abstract;
|
||||||
int group = MIXER_GROUP_SOUND;
|
virtual void SetGroup(int group) abstract;
|
||||||
SpeexResamplerState* resampler = nullptr;
|
|
||||||
Source* source = nullptr;
|
virtual double GetRate() const abstract;
|
||||||
|
virtual void SetRate(double rate) abstract;
|
||||||
|
|
||||||
|
virtual unsigned long GetOffset() const abstract;
|
||||||
|
virtual bool SetOffset(unsigned long offset) abstract;
|
||||||
|
|
||||||
|
virtual int GetLoop() const abstract;
|
||||||
|
virtual void SetLoop(int value) abstract;
|
||||||
|
|
||||||
|
virtual int GetVolume() const abstract;
|
||||||
|
virtual float GetVolumeL() const abstract;
|
||||||
|
virtual float GetVolumeR() const abstract;
|
||||||
|
virtual float GetOldVolumeL() const abstract;
|
||||||
|
virtual float GetOldVolumeR() const abstract;
|
||||||
|
virtual int GetOldVolume() const abstract;
|
||||||
|
virtual void SetVolume(int volume) abstract;
|
||||||
|
|
||||||
|
virtual float GetPan() const abstract;
|
||||||
|
virtual void SetPan(float pan) abstract;
|
||||||
|
|
||||||
|
virtual bool IsStopping() const abstract;
|
||||||
|
virtual void SetStopping(bool value) abstract;
|
||||||
|
|
||||||
|
virtual bool IsDone() const abstract;
|
||||||
|
virtual void SetDone(bool value) abstract;
|
||||||
|
|
||||||
|
virtual bool DeleteOnDone() const abstract;
|
||||||
|
virtual void SetDeleteOnDone(bool value) abstract;
|
||||||
|
|
||||||
|
virtual void SetDeleteSourceOnDone(bool value) abstract;
|
||||||
|
|
||||||
|
virtual bool IsPlaying() const abstract;
|
||||||
|
|
||||||
|
virtual void Play(Source& source, int loop = MIXER_LOOP_NONE) abstract;
|
||||||
|
virtual void UpdateOldVolume() abstract;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IAudioMixer
|
interface IAudioMixer
|
||||||
|
@ -154,8 +180,8 @@ interface IAudioMixer
|
||||||
virtual void Close() abstract;
|
virtual void Close() abstract;
|
||||||
virtual void Lock() abstract;
|
virtual void Lock() abstract;
|
||||||
virtual void Unlock() abstract;
|
virtual void Unlock() abstract;
|
||||||
virtual Channel * Play(Source& source, int loop, bool deleteondone, bool deletesourceondone) abstract;
|
virtual IAudioChannel * Play(Source& source, int loop, bool deleteondone, bool deletesourceondone) abstract;
|
||||||
virtual void Stop(Channel& channel) abstract;
|
virtual void Stop(IAudioChannel * channel) abstract;
|
||||||
virtual bool LoadMusic(size_t pathid) abstract;
|
virtual bool LoadMusic(size_t pathid) abstract;
|
||||||
virtual void SetVolume(float volume) abstract;
|
virtual void SetVolume(float volume) abstract;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue