Files
2026-02-02 04:50:13 +01:00

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