281 lines
6.8 KiB
C++
281 lines
6.8 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "ags/engine/media/audio/sound_clip.h"
|
|
#include "ags/engine/media/audio/audio_defines.h"
|
|
#include "ags/ags.h"
|
|
|
|
namespace AGS3 {
|
|
|
|
SOUNDCLIP::SOUNDCLIP() : _panning(12. / 8), _panningAsPercentage(0),
|
|
_sourceClipID(-1), _sourceClipType(0), _speed(1000), _priority(50),
|
|
_xSource(-1), _ySource(-1), _maximumPossibleDistanceAway(0), _muted(false),
|
|
_vol100(0), _vol255(0), _volModifier(0), _repeat(false), _directionalVolModifier(0) {
|
|
}
|
|
|
|
void SOUNDCLIP::set_volume100(int volume) {
|
|
_vol100 = volume;
|
|
_vol255 = (volume * 255) / 100;
|
|
adjust_volume();
|
|
}
|
|
|
|
// Sets the current volume property in units of 255
|
|
void SOUNDCLIP::set_volume255(int volume) {
|
|
_vol255 = volume;
|
|
_vol100 = (_vol255 * 100) / 255;
|
|
adjust_volume();
|
|
}
|
|
|
|
void SOUNDCLIP::set_volume_direct(int vol_percent, int vol_absolute) {
|
|
_vol255 = vol_absolute;
|
|
_vol100 = vol_percent;
|
|
adjust_volume();
|
|
}
|
|
|
|
void SOUNDCLIP::set_mute(bool mute) {
|
|
_muted = mute;
|
|
adjust_volume();
|
|
}
|
|
|
|
void SOUNDCLIP::apply_volume_modifier(int mod) {
|
|
_volModifier = mod;
|
|
adjust_volume();
|
|
}
|
|
|
|
void SOUNDCLIP::apply_directional_modifier(int mod) {
|
|
_directionalVolModifier = mod;
|
|
adjust_volume();
|
|
}
|
|
|
|
bool SOUNDCLIP::update() {
|
|
if (!is_ready())
|
|
return false;
|
|
|
|
if (_paramsChanged) {
|
|
auto vol_f = static_cast<float>(get_final_volume()) / 255.0f;
|
|
if (vol_f < 0.0f) {
|
|
vol_f = 0.0f;
|
|
}
|
|
if (vol_f > 1.0f) {
|
|
vol_f = 1.0f;
|
|
}
|
|
|
|
auto speed_f = static_cast<float>(_speed) / 1000.0f;
|
|
if (speed_f <= 0.0) {
|
|
speed_f = 1.0f;
|
|
}
|
|
|
|
// Sets the pan position, ranging from -100 (left) to +100 (right)
|
|
auto panning_f = (static_cast<float>(_panning) / 100.0f);
|
|
if (panning_f < -1.0f) {
|
|
panning_f = -1.0f;
|
|
}
|
|
if (panning_f > 1.0f) {
|
|
panning_f = 1.0f;
|
|
}
|
|
|
|
//audio_core_slot_configure(slot_, vol_f, speed_f, panning_f);
|
|
_paramsChanged = false;
|
|
}
|
|
/*
|
|
float pos_f, posms_f;
|
|
PlaybackState core_state = audio_core_slot_get_play_state(slot_, pos_f, posms_f);
|
|
pos = static_cast<int>(pos_f);
|
|
posMs = static_cast<int>(posms_f);
|
|
if (state == core_state || core_state == PlayStateError || core_state == PlayStateFinished) {
|
|
state = core_state;
|
|
return is_ready();
|
|
}
|
|
|
|
switch (state) {
|
|
case PlaybackState::PlayStatePlaying:
|
|
state = audio_core_slot_play(slot_);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
*/
|
|
return is_ready();
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
SoundClipWaveBase::SoundClipWaveBase(Audio::AudioStream *stream, bool repeat) :
|
|
SOUNDCLIP(), _state(SoundClipInitial), _stream(stream) {
|
|
_mixer = ::AGS::g_vm->_mixer;
|
|
_repeat = repeat;
|
|
_vol255 = 255;
|
|
|
|
if (repeat) {
|
|
Audio::RewindableAudioStream *str = dynamic_cast<Audio::RewindableAudioStream *>(stream);
|
|
if (str)
|
|
_stream = new Audio::LoopingAudioStream(str, 0);
|
|
}
|
|
}
|
|
|
|
SoundClipWaveBase::~SoundClipWaveBase() {
|
|
_mixer->stopHandle(_soundHandle);
|
|
delete _stream;
|
|
_stream = nullptr;
|
|
}
|
|
|
|
void SoundClipWaveBase::poll() {
|
|
bool playing = is_playing();
|
|
if (playing)
|
|
_state = SoundClipPlaying;
|
|
else if (_state == SoundClipPlaying)
|
|
_state = SoundClipStopped;
|
|
}
|
|
|
|
int SoundClipWaveBase::play() {
|
|
if (_soundType != Audio::Mixer::kPlainSoundType) {
|
|
if (!_stream) {
|
|
warning("Sound stream is null");
|
|
return 0;
|
|
}
|
|
if (_stream->getRate() < 262144) // maximum accepted value in audio/rate.cpp
|
|
_mixer->playStream(_soundType, &_soundHandle, _stream,
|
|
-1, _vol255, 0, DisposeAfterUse::NO);
|
|
else
|
|
warning("Invalid sound clip sample rate: %d! Skipping", _stream->getRate());
|
|
} else {
|
|
_waitingToPlay = true;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void SoundClipWaveBase::setType(Audio::Mixer::SoundType type) {
|
|
assert(type != Audio::Mixer::kPlainSoundType);
|
|
_soundType = type;
|
|
|
|
if (_waitingToPlay) {
|
|
_waitingToPlay = false;
|
|
|
|
play();
|
|
}
|
|
}
|
|
|
|
int SoundClipWaveBase::play_from(int position) {
|
|
if (position != 0)
|
|
seek(position);
|
|
|
|
play();
|
|
return 1;
|
|
}
|
|
|
|
void SoundClipWaveBase::pause() {
|
|
_mixer->pauseHandle(_soundHandle, false);
|
|
_state = SoundClipPaused;
|
|
}
|
|
|
|
void SoundClipWaveBase::resume() {
|
|
_mixer->pauseHandle(_soundHandle, false);
|
|
_state = SoundClipPlaying;
|
|
poll();
|
|
}
|
|
|
|
bool SoundClipWaveBase::is_playing() {
|
|
return _mixer->isSoundHandleActive(_soundHandle) || is_paused();
|
|
}
|
|
|
|
bool SoundClipWaveBase::is_paused() {
|
|
return _state == SoundClipPaused;
|
|
}
|
|
|
|
int SoundClipWaveBase::pos_to_posms(int pos) const {
|
|
// The pos meaning depends on the sound type:
|
|
// - WAV / VOC - the sample number
|
|
// - OGG / MP3 - milliseconds
|
|
// - MOD - the pattern number
|
|
switch (get_sound_type()) {
|
|
case MUS_WAVE: // Pos is in samples
|
|
if (!_stream)
|
|
return 0;
|
|
return static_cast<int>((static_cast<int64_t>(pos) * 1000) / _stream->getRate());
|
|
case MUS_MOD: /* TODO: reimplement */
|
|
// better say that it does not work than return wrong value
|
|
return 0;
|
|
default:
|
|
return pos;
|
|
}
|
|
}
|
|
|
|
void SoundClipWaveBase::seek(int pos) {
|
|
seek_ms(pos_to_posms(pos));
|
|
}
|
|
|
|
void SoundClipWaveBase::seek_ms(int pos_ms) {
|
|
Audio::SeekableAudioStream *stream =
|
|
dynamic_cast<Audio::SeekableAudioStream *>(_stream);
|
|
|
|
if (stream) {
|
|
stream->seek(Audio::Timestamp(pos_ms));
|
|
} else {
|
|
warning("Audio stream did not support seeking");
|
|
}
|
|
}
|
|
|
|
int SoundClipWaveBase::get_pos() {
|
|
return _mixer->getSoundElapsedTime(_soundHandle);
|
|
}
|
|
|
|
int SoundClipWaveBase::get_pos_ms() {
|
|
return _mixer->getSoundElapsedTime(_soundHandle);
|
|
}
|
|
|
|
int SoundClipWaveBase::get_length_ms() {
|
|
Audio::SeekableAudioStream *stream =
|
|
dynamic_cast<Audio::SeekableAudioStream *>(_stream);
|
|
|
|
if (stream) {
|
|
return stream->getLength().msecs();
|
|
} else {
|
|
warning("Unable to determine audio stream length");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SoundClipWaveBase::set_panning(int newPanning) {
|
|
_mixer->setChannelBalance(_soundHandle, newPanning);
|
|
}
|
|
|
|
void SoundClipWaveBase::set_speed(int new_speed) {
|
|
_speed = new_speed;
|
|
|
|
if (!_stream) {
|
|
warning("set_speed: sound stream is null");
|
|
return;
|
|
}
|
|
|
|
// get initial channel rate
|
|
const uint32_t rate = _stream->getRate();
|
|
|
|
// default speed = 1000, calculate new sample rate proportionally
|
|
_mixer->setChannelRate(_soundHandle, rate * new_speed / 1000);
|
|
}
|
|
|
|
void SoundClipWaveBase::adjust_volume() {
|
|
_mixer->setChannelVolume(_soundHandle, get_final_volume());
|
|
}
|
|
|
|
} // namespace AGS3
|