mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-15 22:38:09 +00:00
00db87563a
svn-id: r39053
226 lines
5.8 KiB
C++
226 lines
5.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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "common/system.h"
|
|
|
|
#include "sci/tools.h"
|
|
#include "sci/sfx/mixer.h"
|
|
|
|
#include "sound/audiostream.h"
|
|
#include "sound/mixer.h"
|
|
|
|
namespace Sci {
|
|
|
|
class PCMFeedAudioStream : public Audio::AudioStream {
|
|
protected:
|
|
enum FeedMode {
|
|
FEED_MODE_ALIVE,
|
|
FEED_MODE_IDLE,
|
|
FEED_MODE_DEAD,
|
|
FEED_MODE_RESTART
|
|
};
|
|
|
|
/* Whether feed is alive or dead. */
|
|
FeedMode _mode;
|
|
|
|
/* Blank gap in frames. */
|
|
int _gap;
|
|
|
|
/* Feed. */
|
|
sfx_pcm_feed_t *_feed;
|
|
|
|
/* Timestamp of next frame requested by stream driver. */
|
|
sfx_timestamp_t _time;
|
|
|
|
public:
|
|
PCMFeedAudioStream(sfx_pcm_feed_t *feed) : _feed(feed) {
|
|
_feed->frame_size = SFX_PCM_FRAME_SIZE(_feed->conf);
|
|
_mode = FEED_MODE_ALIVE;
|
|
_gap = 0;
|
|
_time = sfx_new_timestamp(g_system->getMillis(), _feed->conf.rate);
|
|
}
|
|
|
|
~PCMFeedAudioStream() {
|
|
_feed->destroy(_feed);
|
|
}
|
|
|
|
virtual int readBuffer(int16 *buffer, const int numSamples);
|
|
|
|
virtual bool isStereo() const { return _feed->conf.stereo; }
|
|
virtual int getRate() const { return _feed->conf.rate; }
|
|
|
|
virtual bool endOfData() const { return _mode == FEED_MODE_DEAD; }
|
|
|
|
protected:
|
|
void queryTimestamp();
|
|
};
|
|
|
|
void PCMFeedAudioStream::queryTimestamp() {
|
|
if (_feed->get_timestamp) {
|
|
sfx_timestamp_t stamp;
|
|
int val = _feed->get_timestamp(_feed, &stamp);
|
|
|
|
switch (val) {
|
|
case PCM_FEED_TIMESTAMP:
|
|
_gap = sfx_timestamp_frame_diff(stamp, _time);
|
|
|
|
if (_gap >= 0)
|
|
_mode = FEED_MODE_ALIVE;
|
|
else {
|
|
// FIXME: I don't quite understand what FEED_MODE_RESTART is for.
|
|
// The original DC mixer seemed to just stop and restart the stream.
|
|
// But why? To catch up with lagging sound?
|
|
_mode = FEED_MODE_RESTART;
|
|
_time = sfx_new_timestamp(g_system->getMillis(), _feed->conf.rate);
|
|
_gap = sfx_timestamp_frame_diff(stamp, _time);
|
|
|
|
if (_gap < 0)
|
|
_gap = 0;
|
|
}
|
|
break;
|
|
case PCM_FEED_IDLE:
|
|
_mode = FEED_MODE_IDLE;
|
|
break;
|
|
case PCM_FEED_EMPTY:
|
|
_mode = FEED_MODE_DEAD;
|
|
_gap = 0;
|
|
}
|
|
} else {
|
|
_mode = FEED_MODE_DEAD;
|
|
_gap = 0;
|
|
}
|
|
}
|
|
|
|
static void U8_to_S16(byte *buf, int samples) {
|
|
for (int i = samples - 1; i >= 0; i--) {
|
|
buf[i * 2 + 1] = buf[i] - 0x80;
|
|
buf[i * 2] = 0;
|
|
}
|
|
}
|
|
|
|
int PCMFeedAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
|
// FIXME: If ScummVM's mixer supported timestamps, then it would pass them
|
|
// as a parameter to this function. But currently, it doesn't. Therefore, we
|
|
// create a fake timestamp based on the current time. For comparison, a real
|
|
// timestamp could be adjusted for pauses in sound processing. And it would
|
|
// be synced for all audio streams.
|
|
sfx_timestamp_t timestamp = sfx_new_timestamp(g_system->getMillis(), _feed->conf.rate);
|
|
|
|
int channels, frames_req;
|
|
int frames_recv = 0;
|
|
|
|
_time = timestamp;
|
|
channels = _feed->conf.stereo == SFX_PCM_MONO ? 1 : 2;
|
|
frames_req = numSamples / channels;
|
|
|
|
while (frames_req != frames_recv) {
|
|
int frames_left = frames_req - frames_recv;
|
|
byte *buf_pos = ((byte *)buffer) + frames_recv * channels * 2;
|
|
|
|
if (_mode == FEED_MODE_IDLE)
|
|
queryTimestamp();
|
|
|
|
if (_mode == FEED_MODE_IDLE || _mode == FEED_MODE_DEAD) {
|
|
memset(buf_pos, 0, frames_left * channels * 2);
|
|
|
|
_time = sfx_timestamp_add(_time, frames_left);
|
|
break;
|
|
}
|
|
|
|
if (_gap) {
|
|
int frames = _gap;
|
|
|
|
if (frames > frames_left)
|
|
frames = frames_left;
|
|
|
|
memset(buf_pos, 0, frames * channels * 2);
|
|
|
|
_gap -= frames;
|
|
frames_recv += frames;
|
|
_time = sfx_timestamp_add(_time, frames);
|
|
} else {
|
|
int frames = _feed->poll(_feed, buf_pos, frames_left);
|
|
|
|
if (_feed->conf.format == SFX_PCM_FORMAT_U8)
|
|
U8_to_S16(buf_pos, frames * channels);
|
|
|
|
frames_recv += frames;
|
|
_time = sfx_timestamp_add(_time, frames);
|
|
|
|
if (frames < frames_left)
|
|
queryTimestamp();
|
|
}
|
|
}
|
|
|
|
return numSamples;
|
|
}
|
|
|
|
void mixer_subscribe(sfx_pcm_feed_t *feed) {
|
|
if ((feed->conf.format != SFX_PCM_FORMAT_S16_NATIVE) && (feed->conf.format != SFX_PCM_FORMAT_U8)) {
|
|
error("[soft-mixer] Unsupported feed format %d", feed->conf.format);
|
|
}
|
|
|
|
PCMFeedAudioStream *newStream = new PCMFeedAudioStream(feed);
|
|
|
|
// FIXME: Is this sound type appropriate? The mixer seems to handle music, too.
|
|
g_system->getMixer()->playInputStream(Audio::Mixer::kSFXSoundType, 0, newStream);
|
|
|
|
debug(2, "[soft-mixer] Subscribed %s-%x (%d Hz, %d/%x)",
|
|
feed->debug_name, feed->debug_nr, feed->conf.rate, feed->conf.stereo, feed->conf.format);
|
|
}
|
|
|
|
|
|
int mixer_process() {
|
|
// TODO
|
|
/*
|
|
feed_state_t *state, *state_next;
|
|
|
|
TAILQ_FOREACH(state, &feeds, entry) {
|
|
snd_stream_poll(handle);
|
|
}
|
|
|
|
state = TAILQ_FIRST(&feeds);
|
|
while (state) {
|
|
state_next = TAILQ_NEXT(state, entry);
|
|
if (_mode == FEED_MODE_DEAD) {
|
|
snd_stream_stop(handle);
|
|
snd_stream_destroy(handle);
|
|
feed->destroy(feed);
|
|
TAILQ_REMOVE(&feeds, state, entry);
|
|
}
|
|
else if (_mode == FEED_MODE_RESTART) {
|
|
snd_stream_stop(handle);
|
|
snd_stream_start(handle, feed->conf.rate,
|
|
feed->conf.stereo != SFX_PCM_MONO);
|
|
_mode = FEED_MODE_ALIVE;
|
|
}
|
|
state = state_next;
|
|
}
|
|
*/
|
|
return SFX_OK;
|
|
}
|
|
|
|
} // End of namespace Sci
|