aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/player/v2.cpp
diff options
context:
space:
mode:
authorFilippos Karapetis2013-11-01 06:57:20 +0200
committerFilippos Karapetis2013-11-01 06:57:53 +0200
commit651bf899399de2b5c2d8e62a5bb4964b037950d2 (patch)
tree103dfc3e6677e7d5c26e13e8866648782f226247 /engines/scumm/player/v2.cpp
parent37d4316a4014970171dfea8fc19f8c9e978e9102 (diff)
downloadscummvm-rg350-651bf899399de2b5c2d8e62a5bb4964b037950d2.tar.gz
scummvm-rg350-651bf899399de2b5c2d8e62a5bb4964b037950d2.tar.bz2
scummvm-rg350-651bf899399de2b5c2d8e62a5bb4964b037950d2.zip
SCUMM: Move all players to a separate "player" directory
There are 34 player .cpp/.h player files, so they have been placed in their own directory, to logically separate them from the rest of the engine
Diffstat (limited to 'engines/scumm/player/v2.cpp')
-rw-r--r--engines/scumm/player/v2.cpp327
1 files changed, 327 insertions, 0 deletions
diff --git a/engines/scumm/player/v2.cpp b/engines/scumm/player/v2.cpp
new file mode 100644
index 0000000000..e3b3c978dd
--- /dev/null
+++ b/engines/scumm/player/v2.cpp
@@ -0,0 +1,327 @@
+/* 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.
+ *
+ */
+
+#include "scumm/player/v2.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+#define SPK_DECAY 0xa000 /* Depends on sample rate */
+#define PCJR_DECAY 0xa000 /* Depends on sample rate */
+
+#define NG_PRESET 0x0f35 /* noise generator preset */
+#define FB_WNOISE 0x12000 /* feedback for white noise */
+#define FB_PNOISE 0x08000 /* feedback for periodic noise */
+
+
+Player_V2::Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr)
+ : Player_V2Base(scumm, mixer, pcjr) {
+
+ int i;
+
+ // Initialize square generator
+ _level = 0;
+
+ _RNG = NG_PRESET;
+
+ _pcjr = pcjr;
+
+ if (_pcjr) {
+ _decay = PCJR_DECAY;
+ _update_step = (_sampleRate << FIXP_SHIFT) / (111860 * 2);
+ } else {
+ _decay = SPK_DECAY;
+ _update_step = (_sampleRate << FIXP_SHIFT) / (1193000 * 2);
+ }
+
+ // Adapt _decay to sample rate. It must be squared when
+ // sample rate doubles.
+ for (i = 0; (_sampleRate << i) < 30000; i++)
+ _decay = _decay * _decay / 65536;
+
+ _timer_output = 0;
+ for (i = 0; i < 4; i++)
+ _timer_count[i] = 0;
+
+ setMusicVolume(255);
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+Player_V2::~Player_V2() {
+ Common::StackLock lock(_mutex);
+ _mixer->stopHandle(_soundHandle);
+}
+
+void Player_V2::setMusicVolume (int vol) {
+ if (vol > 255)
+ vol = 255;
+
+ /* scale to int16, FIXME: find best value */
+ double out = vol * 128 / 3;
+
+ /* build volume table (2dB per step) */
+ for (int i = 0; i < 15; i++) {
+ /* limit volume to avoid clipping */
+ if (out > 0xffff)
+ _volumetable[i] = 0xffff;
+ else
+ _volumetable[i] = (int) out;
+
+ out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
+ }
+ _volumetable[15] = 0;
+}
+
+void Player_V2::stopAllSounds() {
+ Common::StackLock lock(_mutex);
+
+ for (int i = 0; i < 4; i++) {
+ clear_channel(i);
+ }
+ _next_nr = _current_nr = 0;
+ _next_data = _current_data = 0;
+}
+
+void Player_V2::stopSound(int nr) {
+ Common::StackLock lock(_mutex);
+
+ if (_next_nr == nr) {
+ _next_nr = 0;
+ _next_data = 0;
+ }
+ if (_current_nr == nr) {
+ for (int i = 0; i < 4; i++) {
+ clear_channel(i);
+ }
+ _current_nr = 0;
+ _current_data = 0;
+ chainNextSound();
+ }
+}
+
+void Player_V2::startSound(int nr) {
+ Common::StackLock lock(_mutex);
+
+ byte *data = _vm->getResourceAddress(rtSound, nr);
+ assert(data);
+
+ int cprio = _current_data ? *(_current_data + _header_len) : 0;
+ int prio = *(data + _header_len);
+ int nprio = _next_data ? *(_next_data + _header_len) : 0;
+
+ int restartable = *(data + _header_len + 1);
+
+ if (!_current_nr || cprio <= prio) {
+ int tnr = _current_nr;
+ int tprio = cprio;
+ byte *tdata = _current_data;
+
+ chainSound(nr, data);
+ nr = tnr;
+ prio = tprio;
+ data = tdata;
+ restartable = data ? *(data + _header_len + 1) : 0;
+ }
+
+ if (!_current_nr) {
+ nr = 0;
+ _next_nr = 0;
+ _next_data = 0;
+ }
+
+ if (nr != _current_nr
+ && restartable
+ && (!_next_nr
+ || nprio <= prio)) {
+
+ _next_nr = nr;
+ _next_data = data;
+ }
+}
+
+int Player_V2::getSoundStatus(int nr) const {
+ return _current_nr == nr || _next_nr == nr;
+}
+
+int Player_V2::readBuffer(int16 *data, const int numSamples) {
+ Common::StackLock lock(_mutex);
+
+ uint step;
+ uint len = numSamples / 2;
+
+ do {
+ if (!(_next_tick >> FIXP_SHIFT)) {
+ _next_tick += _tick_len;
+ nextTick();
+ }
+
+ step = len;
+ if (step > (_next_tick >> FIXP_SHIFT))
+ step = (_next_tick >> FIXP_SHIFT);
+ if (_pcjr)
+ generatePCjrSamples(data, step);
+ else
+ generateSpkSamples(data, step);
+ data += 2 * step;
+ _next_tick -= step << FIXP_SHIFT;
+ } while (len -= step);
+
+ return numSamples;
+}
+
+void Player_V2::lowPassFilter(int16 *sample, uint len) {
+ for (uint i = 0; i < len; i++) {
+ _level = (int) (_level * _decay
+ + sample[0] * (0x10000 - _decay)) >> 16;
+ sample[0] = sample[1] = _level;
+ sample += 2;
+ }
+}
+
+void Player_V2::squareGenerator(int channel, int freq, int vol,
+ int noiseFeedback, int16 *sample, uint len) {
+ int32 period = _update_step * freq;
+ int32 nsample;
+ if (period == 0)
+ period = _update_step;
+
+ for (uint i = 0; i < len; i++) {
+ uint32 duration = 0;
+
+ if (_timer_output & (1 << channel))
+ duration += _timer_count[channel];
+
+ _timer_count[channel] -= (1 << FIXP_SHIFT);
+ while (_timer_count[channel] <= 0) {
+
+ if (noiseFeedback) {
+ if (_RNG & 1) {
+ _RNG ^= noiseFeedback;
+ _timer_output ^= (1 << channel);
+ }
+ _RNG >>= 1;
+ } else {
+ _timer_output ^= (1 << channel);
+ }
+
+ if (_timer_output & (1 << channel))
+ duration += period;
+
+ _timer_count[channel] += period;
+ }
+
+ if (_timer_output & (1 << channel))
+ duration -= _timer_count[channel];
+
+ nsample = *sample +
+ (((int32) (duration - (1 << (FIXP_SHIFT - 1)))
+ * (int32) _volumetable[vol]) >> FIXP_SHIFT);
+ /* overflow: clip value */
+ if (nsample > 0x7fff)
+ nsample = 0x7fff;
+ if (nsample < -0x8000)
+ nsample = -0x8000;
+ *sample = nsample;
+ // The following write isn't necessary, because the lowPassFilter does it for us
+ //sample[1] = sample[0];
+ sample += 2;
+ }
+}
+
+void Player_V2::generateSpkSamples(int16 *data, uint len) {
+ int winning_channel = -1;
+ for (int i = 0; i < 4; i++) {
+ if (winning_channel == -1
+ && _channels[i].d.volume
+ && _channels[i].d.time_left) {
+ winning_channel = i;
+ }
+ }
+
+ memset(data, 0, 2 * sizeof(int16) * len);
+ if (winning_channel != -1) {
+ squareGenerator(0, _channels[winning_channel].d.freq, 0,
+ 0, data, len);
+ } else if (_level == 0)
+ /* shortcut: no sound is being played. */
+ return;
+
+ lowPassFilter(data, len);
+}
+
+void Player_V2::generatePCjrSamples(int16 *data, uint len) {
+ int i, j;
+ int freq, vol;
+
+ memset(data, 0, 2 * sizeof(int16) * len);
+ bool hasdata = false;
+
+ for (i = 1; i < 3; i++) {
+ freq = _channels[i].d.freq >> 6;
+ if (_channels[i].d.volume && _channels[i].d.time_left) {
+ for (j = 0; j < i; j++) {
+ if (_channels[j].d.volume
+ && _channels[j].d.time_left
+ && freq == (_channels[j].d.freq >> 6)) {
+ /* HACK: this channel is playing at
+ * the same frequency as another.
+ * Synchronize it to the same phase to
+ * prevent interference.
+ */
+ _timer_count[i] = _timer_count[j];
+ _timer_output ^= (1 << i) &
+ (_timer_output ^ _timer_output << (i - j));
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ freq = _channels[i].d.freq >> 6;
+ vol = (65535 - _channels[i].d.volume) >> 12;
+ if (!_channels[i].d.volume || !_channels[i].d.time_left) {
+ _timer_count[i] -= len << FIXP_SHIFT;
+ if (_timer_count[i] < 0)
+ _timer_count[i] = 0;
+ } else if (i < 3) {
+ hasdata = true;
+ squareGenerator(i, freq, vol, 0, data, len);
+ } else {
+ int noiseFB = (freq & 4) ? FB_WNOISE : FB_PNOISE;
+ int n = (freq & 3);
+
+ freq = (n == 3) ? 2 * (_channels[2].d.freq>>6) : 1 << (5 + n);
+ hasdata = true;
+ squareGenerator(i, freq, vol, noiseFB, data, len);
+ }
+#if 0
+ debug(9, "channel[%d]: freq %d %.1f ; volume %d",
+ i, freq, 111860.0 / freq, vol);
+#endif
+ }
+
+ if (_level || hasdata)
+ lowPassFilter(data, len);
+}
+
+} // End of namespace Scumm