aboutsummaryrefslogtreecommitdiff
path: root/sound/mods
diff options
context:
space:
mode:
Diffstat (limited to 'sound/mods')
-rw-r--r--sound/mods/module.cpp177
-rw-r--r--sound/mods/module.h75
-rw-r--r--sound/mods/protracker.cpp603
-rw-r--r--sound/mods/protracker.h155
4 files changed, 1010 insertions, 0 deletions
diff --git a/sound/mods/module.cpp b/sound/mods/module.cpp
new file mode 100644
index 0000000000..26abf3147e
--- /dev/null
+++ b/sound/mods/module.cpp
@@ -0,0 +1,177 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ * Based on code by madmoose
+ *
+ * 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/stdafx.h"
+#include "common/system.h"
+#include "common/file.h"
+#include "common/stream.h"
+
+#include "sound/mods/module.h"
+
+namespace Modules {
+
+bool Module::load(const char *fn) {
+ Common::File f;
+
+ if (!f.open(fn))
+ return false;
+
+ int bufsz = f.size();
+ byte *buf = new byte[bufsz];
+
+ int r = f.read(buf, bufsz);
+ assert(r);
+
+ f.close();
+
+ Common::MemoryReadStream st(buf, bufsz);
+
+ st.read(songname, 20);
+ songname[0] = '\0';
+
+ for (int i = 0; i < 31; ++i) {
+ st.read(sample[i].name, 22);
+ sample[i].name[22] = '\0';
+ sample[i].len = 2 * st.readUint16BE();
+
+ sample[i].finetune = st.readByte();
+ assert(sample[i].finetune < 0x10);
+
+ sample[i].vol = st.readByte();
+ sample[i].repeat = 2 * st.readUint16BE();
+ sample[i].replen = 2 * st.readUint16BE();
+
+ //printf("\"%s\"\tlen: %d\tfinetune: %d\tvol: %d\trepeat: %d\treplen: %d\n",
+ // sample[i].name, sample[i].len, sample[i].finetune, sample[i].vol, sample[i].repeat, sample[i].replen);
+
+ }
+
+ songlen = 2 * st.readByte();
+ undef = 2 * st.readByte();
+
+ st.read(songpos, 128);
+ st.read(sig, 4); // Should be "M.K."
+ assert(0 == memcmp(sig, "M.K.", 4));
+
+ int maxpattern = 0;
+ for (int i = 0; i < 128; ++i)
+ if (maxpattern < songpos[i])
+ maxpattern = songpos[i];
+
+ pattern = new pattern_t[maxpattern + 1];
+
+ for (int i = 0; i <= maxpattern; ++i) {
+ for (int j = 0; j < 64; ++j) {
+ for (int k = 0; k < 4; ++k) {
+ uint32 note = st.readUint32BE();
+ pattern[i][j][k].sample = (note & 0xf0000000) >> 24 | (note & 0x0000f000) >> 12;
+
+ pattern[i][j][k].period = (note >> 16) & 0xfff;
+ pattern[i][j][k].effect = note & 0xfff;
+/*
+ const char *notename;
+
+ switch (pattern[i][j][k].period) {
+ case 856: notename = "C-1"; break;
+ case 808: notename = "C#1"; break;
+ case 762: notename = "D-1"; break;
+ case 720: notename = "D#1"; break;
+ case 678: notename = "E-1"; break;
+ case 640: notename = "E#1"; break;
+ case 604: notename = "F-1"; break;
+ case 570: notename = "F#1"; break;
+ case 538: notename = "A-1"; break;
+ case 508: notename = "A#1"; break;
+ case 480: notename = "B-1"; break;
+ case 453: notename = "B#1"; break;
+ case 428: notename = "C-2"; break;
+ case 404: notename = "C#2"; break;
+ case 381: notename = "D-2"; break;
+ case 360: notename = "D#2"; break;
+ case 339: notename = "E-2"; break;
+ case 320: notename = "E#2"; break;
+ case 302: notename = "F-2"; break;
+ case 285: notename = "F#2"; break;
+ case 269: notename = "A-2"; break;
+ case 254: notename = "A#2"; break;
+ case 240: notename = "B-2"; break;
+ case 226: notename = "B#2"; break;
+ case 214: notename = "C-3"; break;
+ case 202: notename = "C#3"; break;
+ case 190: notename = "D-3"; break;
+ case 180: notename = "D#3"; break;
+ case 170: notename = "E-3"; break;
+ case 160: notename = "E#3"; break;
+ case 151: notename = "F-3"; break;
+ case 143: notename = "F#3"; break;
+ case 135: notename = "A-3"; break;
+ case 127: notename = "A#3"; break;
+ case 120: notename = "B-3"; break;
+ case 113: notename = "B#3"; break;
+ case 0: notename = " "; break;
+ default: notename = "???"; break;
+ }
+
+ if (k > 0) printf(" | ");
+
+ if (pattern[i][j][k].sample)
+ printf("%2d %s %3X", pattern[i][j][k].sample, notename, pattern[i][j][k].effect);
+ else
+ printf(" %s %3X", notename, pattern[i][j][k].effect);
+*/
+ }
+ //printf("\n");
+ }
+ }
+
+ for (int i = 0; i < 31; ++i) {
+ if (!sample[i].len)
+ sample[i].data = 0;
+ else {
+ sample[i].data = new byte[sample[i].len];
+ st.read((byte *)sample[i].data, sample[i].len);
+ }
+ }
+
+ assert(st.eos());
+
+ delete[] buf;
+
+ return true;
+}
+
+Module::Module() {
+ pattern = NULL;
+ for (int i = 0; i < 31; ++i) {
+ sample[i].data = NULL;
+ }
+}
+
+Module::~Module() {
+ delete[] pattern;
+ for (int i = 0; i < 31; ++i) {
+ delete[] sample[i].data;
+ }
+}
+
+} // End of namespace Modules
diff --git a/sound/mods/module.h b/sound/mods/module.h
new file mode 100644
index 0000000000..3b39f3991e
--- /dev/null
+++ b/sound/mods/module.h
@@ -0,0 +1,75 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ * Based on code by madmoose
+ *
+ * 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$
+ *
+ */
+
+#ifndef __SOUND_MODS_MODULE_H__
+#define __SOUND_MODS_MODULE_H__
+
+namespace Modules {
+
+/*
+ * Storing notes and patterns like this
+ * wastes insane amounts of memory.
+ *
+ * They should be stored in memory
+ * like they are in the file.
+ */
+
+typedef struct {
+ byte sample;
+ uint16 period;
+ uint16 effect;
+} note_t;
+
+typedef note_t pattern_t[64][4];
+
+typedef struct {
+ byte name[23];
+ uint16 len;
+ byte finetune;
+ byte vol;
+ uint16 repeat;
+ uint16 replen;
+ byte *data;
+} sample_t;
+
+class Module {
+public:
+ byte songname[21];
+
+ sample_t sample[32];
+
+ byte songlen;
+ byte undef;
+ byte songpos[128];
+ byte sig[4];
+ pattern_t *pattern;
+
+ Module();
+ ~Module();
+
+ bool load(const char *fn);
+};
+
+} // End of namespace Modules
+
+#endif
diff --git a/sound/mods/protracker.cpp b/sound/mods/protracker.cpp
new file mode 100644
index 0000000000..31dd2e3dc2
--- /dev/null
+++ b/sound/mods/protracker.cpp
@@ -0,0 +1,603 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ * Based on code by madmoose
+ *
+ * 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/stdafx.h"
+#include "common/system.h"
+
+#include "sound/mods/protracker.h"
+
+namespace Modules {
+
+#define DUMP 1
+
+void ProtrackerPlayer::init(OSystem *system) {
+ _system = system;
+
+ _buf = new SoundBuffer();
+
+ _system->setSoundCallback(&audioCallback, this);
+}
+
+void ProtrackerPlayer::start() {
+ if (_module) {
+ _tick = _row = _pos = 0;
+ _hasJumpToPattern = false;
+ _hasPatternBreak = false;
+ _hasPatternLoop = false;
+ _patternLoopCount = 0;
+ _patternLoopRow = 0;
+ _speed = 6;
+ _bpm = 125;
+
+ _generatedSamplesOverflow = 0.0;
+
+ for (int t = 0; t < 4; t++) {
+ _track[t].sample = 0;
+ _track[t].period = 0;
+ _track[t].offset = 0.0;
+
+ _track[t].vibrato = 0;
+ }
+
+ //_system->startAudio();
+ }
+}
+
+void ProtrackerPlayer::pause() {
+}
+
+void ProtrackerPlayer::stop() {
+ //_system->stopAudio();
+}
+
+void ProtrackerPlayer::loadModule(const char *fn) {
+ if (_module)
+ delete _module;
+
+ _module = new Module();
+ _module->load(fn);
+}
+
+void ProtrackerPlayer::generateSound() {
+ _generatedSamplesOverflow += 5.0 * 44100.0 / (2.0 * _bpm);
+ int samples = (int)floor(_generatedSamplesOverflow);
+ _generatedSamplesOverflow -= samples;
+
+ _buf->ensureCapacity(samples);
+
+ int16 *p = _buf->getEnd();
+ for (int i = 0; i < samples; i++)
+ p[i] = 0;
+
+ for (int track = 0; track < 4; track++) {
+ if (_track[track].sample > 0) {
+ p = _buf->getEnd();
+
+ double frequency =
+ (7093789.2 / 2.0) / (_track[track].period +
+ _track[track].vibrato);
+
+ double rate = frequency / 44100.0;
+ double offset = _track[track].offset;
+ int sample = _track[track].sample - 1;
+ int slen = _module->sample[sample].len;
+ byte *data = _module->sample[sample].data;
+
+ static bool did_warn_about_finetune = false;
+ if (!did_warn_about_finetune && _module->sample[sample].finetune != 0) {
+ did_warn_about_finetune = true;
+ puts("Warning! Finetuning not implemented!");
+ }
+
+ if (_module->sample[sample].replen > 2) {
+ int neededSamples = samples;
+
+ //printf("%d %d >= %d\n", (int)offset, (int)(offset + neededSamples*rate), _module->sample[sample].repeat + _module->sample[sample].replen);
+
+ while ((int)(offset + neededSamples * rate) >=
+ (_module->sample[sample].repeat + _module->sample[sample].replen)) {
+ //puts("/* The repeat length is the limiting factor */");
+ //printf("case 1: period: %d\trate: %g\toffset: %g\tsample: %d\tslen: %d\tvol: %d\n", _track[track].period, rate, _track[track].offset, _track[track].sample, slen, _track[track].vol);
+
+ int end =
+ (int)((_module->sample[sample].
+ repeat + _module->sample[sample].
+ replen - offset) / rate);
+
+ if ((int)(offset + rate * end) > slen)
+ warning("!!!!!!!!!");
+ //printf("writing %d-%d max %d\n", (int)offset, (int)(offset + rate*end), slen);
+
+ for (int i = 0; i < end; i++)
+ *p++ +=
+ _track[track].vol * data[(int)(offset + rate * i)];
+ _track[track].offset =
+ _module->sample[sample].repeat;
+ offset = _track[track].offset;
+ neededSamples -= end;
+ }
+ if (neededSamples > 0) {
+ //puts("/* The requested number of samples is the limiting factor, not the repeat length */");
+ //printf("case 2: period: %d\trate: %g\toffset: %g\tsample: %d\tslen: %d\tvol: %d\n", _track[track].period, rate, _track[track].offset, _track[track].sample, slen, _track[track].vol);
+
+ //if ((int)(offset + rate*neededSamples) > slen) puts("!!!!!!!!!");
+ //printf("writing %d-%d max %d\n", (int)offset, (int)(offset + rate*neededSamples), slen);
+ for (int i = 0; i < neededSamples; i++)
+ *p++ +=
+ _track[track].vol * data[(int)(offset + rate * i)];
+ _track[track].offset +=
+ rate * neededSamples;
+ }
+ } else {
+ if (offset < slen) {
+ if ((int)(offset + samples * rate) >=
+ slen) {
+ /* The end of the sample is the limiting factor */
+ //printf("case 3: period: %d\trate: %g\toffset: %g\tsample: %d\tslen: %d\tvol: %d\n", _track[track].period, rate, _track[track].offset, _track[track].sample, slen, _track[track].vol);
+
+ int end = (int)((slen - offset) / rate);
+ for (int i = 0; i < end; i++)
+ *p++ += _track[track].vol *
+ data[(int)(offset + rate * i)];
+ _track[track].offset = slen;
+ } else {
+ /* The requested number of samples is the limiting factor, not the sample */
+ //printf("case 4: period: %d\trate: %g\toffset: %g\tsample: %d\tslen: %d\tvol: %d\n", _track[track].period, rate, _track[track].offset, _track[track].sample, slen, _track[track].vol);
+
+ for (int i = 0; i < samples; i++)
+ *p++ += _track[track].vol *
+ data[(int)(offset + rate * i)];
+ _track[track].offset += rate * samples;
+ }
+ }
+ }
+ }
+ }
+
+ _buf->finish(samples);
+}
+
+void ProtrackerPlayer::updateRow() {
+ //printf("ProtrackerPlayer::updateRow(): tick: %d\tpos: %d->%d\trow: %d\n", _tick, _pos, _module->songpos[_pos], _row);
+
+#ifdef DUMP
+ printf("%3d:%3d:%2d: ", _pos, _module->songpos[_pos], _row);
+#endif
+ for (int track = 0; track < 4; track++) {
+ _track[track].vibrato = 0;
+ note_t note =
+ _module->pattern[_module->songpos[_pos]][_row][track];
+
+ int effect = note.effect >> 8;
+
+ if (note.sample) {
+ if (_track[track].sample != note.sample) {
+ _track[track].vibratoPos = 0;
+ }
+ _track[track].sample = note.sample;
+ _track[track].vol = _module->sample[note.sample - 1].vol;
+ }
+ if (note.period) {
+ if (effect != 3 && effect != 5) {
+ _track[track].period = note.period;
+ _track[track].offset = 0.0;
+ }
+ }
+
+ int exy = note.effect & 0xff;
+ int ex = (note.effect >> 4) & 0xf;
+ int ey = note.effect & 0xf;
+
+#ifdef DUMP
+ const char *notename;
+
+ switch (note.period) {
+ case 856:
+ notename = "C-1";
+ break;
+ case 808:
+ notename = "C#1";
+ break;
+ case 762:
+ notename = "D-1";
+ break;
+ case 720:
+ notename = "D#1";
+ break;
+ case 678:
+ notename = "E-1";
+ break;
+ case 640:
+ notename = "E#1";
+ break;
+ case 604:
+ notename = "F-1";
+ break;
+ case 570:
+ notename = "F#1";
+ break;
+ case 538:
+ notename = "A-1";
+ break;
+ case 508:
+ notename = "A#1";
+ break;
+ case 480:
+ notename = "B-1";
+ break;
+ case 453:
+ notename = "B#1";
+ break;
+ case 428:
+ notename = "C-2";
+ break;
+ case 404:
+ notename = "C#2";
+ break;
+ case 381:
+ notename = "D-2";
+ break;
+ case 360:
+ notename = "D#2";
+ break;
+ case 339:
+ notename = "E-2";
+ break;
+ case 320:
+ notename = "E#2";
+ break;
+ case 302:
+ notename = "F-2";
+ break;
+ case 285:
+ notename = "F#2";
+ break;
+ case 269:
+ notename = "A-2";
+ break;
+ case 254:
+ notename = "A#2";
+ break;
+ case 240:
+ notename = "B-2";
+ break;
+ case 226:
+ notename = "B#2";
+ break;
+ case 214:
+ notename = "C-3";
+ break;
+ case 202:
+ notename = "C#3";
+ break;
+ case 190:
+ notename = "D-3";
+ break;
+ case 180:
+ notename = "D#3";
+ break;
+ case 170:
+ notename = "E-3";
+ break;
+ case 160:
+ notename = "E#3";
+ break;
+ case 151:
+ notename = "F-3";
+ break;
+ case 143:
+ notename = "F#3";
+ break;
+ case 135:
+ notename = "A-3";
+ break;
+ case 127:
+ notename = "A#3";
+ break;
+ case 120:
+ notename = "B-3";
+ break;
+ case 113:
+ notename = "B#3";
+ break;
+ case 0:
+ notename = " ";
+ break;
+ default:
+ notename = "???";
+ break;
+ }
+
+ if (track > 0)
+ printf(" | ");
+
+ if (note.sample)
+ printf("%2d %s %3X", note.sample, notename,
+ note.effect);
+ else
+ printf(" %s %3X", notename, note.effect);
+#endif
+
+ switch (effect) {
+ case 0x0:
+ break;
+ case 0x1:
+ break;
+ case 0x2:
+ break;
+ case 0x3:
+ if (note.period)
+ _track[track].portaToNote = note.period;
+
+ if (exy)
+ _track[track].portaToNoteSpeed = exy;
+ break;
+ case 0x4:
+ if (ex || ey) {
+ _track[track].vibratoSpeed = ex;
+ _track[track].vibratoDepth = ey;
+ }
+ break;
+ case 0x5:
+ break;
+ case 0xA:
+ break;
+ case 0xB:
+ _hasJumpToPattern = true;
+ _jumpToPattern = exy;
+ break;
+ case 0xC:
+ _track[track].vol = exy;
+ break;
+ case 0xD:
+ _hasPatternBreak = true;
+ _skiprow = ex * 10 + ey;
+ break;
+
+ case 0xE:
+ switch (ex) {
+ case 0x6:
+ if (ey == 0) {
+ _patternLoopRow = _row;
+ } else {
+ _patternLoopCount++;
+ if (_patternLoopCount <= ey)
+ _hasPatternLoop = true;
+ else
+ _patternLoopCount = 0;
+ }
+ break;
+ case 0x9:
+ break; // Retrigger note
+ default:
+ printf("Unimplemented effect %X\n",
+ note.effect);
+ }
+ break;
+
+ case 0xF:
+ if (exy < 0x20) {
+ _speed = exy;
+ //printf("Speed: %x\n", _speed);
+ } else {
+ _bpm = exy;
+ //printf("BPM: %x\n", _bpm);
+ }
+ break;
+ default:
+ printf("Unimplemented effect %X\n", note.effect);
+ }
+ }
+#ifdef DUMP
+ putchar('\n');
+ fflush(NULL);
+#endif
+}
+
+void ProtrackerPlayer::updateEffects() {
+ //printf("ProtrackerPlayer::updateEffects(): tick: %d\tpos: %d->%d\trow: %d\n", _tick, _pos, _module->songpos[_pos], _row);
+
+ static const int16 sinetable[64] = {
+ 0, 24, 49, 74, 97, 120, 141, 161,
+ 180, 197, 212, 224, 235, 244, 250, 253,
+ 255, 253, 250, 244, 235, 224, 212, 197,
+ 180, 161, 141, 120, 97, 74, 49, 24,
+ 0, -24, -49, -74, -97, -120, -141, -161,
+ -180, -197, -212, -224, -235, -244, -250, -253,
+ -255, -253, -250, -244, -235, -224, -212, -197,
+ -180, -161, -141, -120, -97, -74, -49, -24
+ };
+
+ for (int track = 0; track < 4; track++) {
+ _track[track].vibrato = 0;
+
+ note_t note =
+ _module->pattern[_module->songpos[_pos]][_row][track];
+
+ int effect = note.effect >> 8;
+
+ int exy = note.effect & 0xff;
+ int ex = (note.effect >> 4) & 0xf;
+ int ey = (note.effect) & 0xf;
+
+ //printf("track %d: period: %3d\tsample: %2d\teffect: %x\n", track, note.period, note.sample, note.effect);
+
+ int vol;
+ switch (effect) {
+ case 0x0:
+ break;
+ case 0x1:
+ _track[track].period -= exy;
+ break;
+ case 0x2:
+ _track[track].period += exy;
+ break;
+ case 0x3:
+ if (_track[track].portaToNote && _track[track].portaToNoteSpeed) {
+ if (_track[track].period < _track[track].portaToNote) {
+ _track[track].period += _track[track].portaToNoteSpeed;
+ if (_track[track].period > _track[track].portaToNote)
+ _track[track].period = _track[track].portaToNote;
+ } else if (_track[track].period > _track[track].portaToNote) {
+ _track[track].period -= _track[track].portaToNoteSpeed;
+ if (_track[track].period < _track[track].portaToNote)
+ _track[track].period = _track[track].portaToNote;
+ }
+ }
+ break;
+ case 0x4:
+ _track[track].vibrato =
+ (_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128;
+ _track[track].vibratoPos += _track[track].vibratoSpeed;
+ _track[track].vibratoPos %= 64;
+ break;
+
+ case 0x5:
+ if (_track[track].portaToNote
+ && _track[track].portaToNoteSpeed) {
+ if (_track[track].period < _track[track].portaToNote) {
+ _track[track].period += _track[track].portaToNoteSpeed;
+ if (_track[track].period > _track[track].portaToNote)
+ _track[track].period = _track[track].portaToNote;
+ } else if (_track[track].period > _track[track].portaToNote) {
+ _track[track].period -= _track[track].portaToNoteSpeed;
+ if (_track[track].period < _track[track].portaToNote)
+ _track[track].period = _track[track].portaToNote;
+ }
+ }
+
+ vol = _track[track].vol;
+ if (ex == 0)
+ vol -= ey;
+ else if (ey == 0)
+ vol += ex;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > 64)
+ vol = 64;
+
+ _track[track].vol = vol;
+
+ break;
+
+ case 0x6:
+ _track[track].vibrato =
+ (_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128;
+ _track[track].vibratoPos += _track[track].vibratoSpeed;
+ _track[track].vibratoPos %= 64;
+
+ vol = _track[track].vol;
+ if (ex == 0)
+ vol -= ey;
+ else if (ey == 0)
+ vol += ex;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > 64)
+ vol = 64;
+
+ _track[track].vol = vol;
+
+ break;
+
+ case 0xA:
+ vol = _track[track].vol;
+ if (ex == 0)
+ vol -= ey;
+ else if (ey == 0)
+ vol += ex;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > 64)
+ vol = 64;
+
+ _track[track].vol = vol;
+
+ break;
+
+ case 0xE:
+ switch (ex) {
+ case 0x6:
+ break; // Pattern loop
+ case 0x9: // Retrigger note
+ if (ey && _tick % ey == 0)
+ _track[track].offset = 0.0;
+ break;
+ }
+ break;
+ }
+ }
+}
+
+void ProtrackerPlayer::mix(byte *buf0, int len) {
+ int16 *buf = (int16 *)buf0;
+ len /= 2;
+
+ while (_buf->size() < len) {
+ if (_tick == 0) {
+ if (_hasJumpToPattern) {
+ _hasJumpToPattern = false;
+ _pos = _jumpToPattern;
+ _row = 0;
+ } else if (_hasPatternBreak) {
+ _hasPatternBreak = false;
+ _row = _skiprow;
+ _pos = (_pos + 1) % _module->songlen;
+ _patternLoopRow = 0;
+ } else if (_hasPatternLoop) {
+ _hasPatternLoop = false;
+ _row = _patternLoopRow;
+ }
+ if (_row >= 64) {
+ _row = 0;
+ _pos = (_pos + 1) % _module->songlen;
+ _patternLoopRow = 0;
+ }
+
+ if (_patternDelay == 0) {
+ updateRow();
+ } else {
+ _patternDelay--;
+ }
+ } else
+ updateEffects();
+
+ _tick = (_tick + 1) % _speed;
+ if (_tick == 0)
+ _row++;
+
+ generateSound();
+ }
+
+ _buf->pop(buf, len);
+}
+
+void ProtrackerPlayer::audioCallback(void *param, byte *buf, int len) {
+ ((ProtrackerPlayer *)param)->mix(buf, len);
+}
+
+} // End of namespace Modules
+
diff --git a/sound/mods/protracker.h b/sound/mods/protracker.h
new file mode 100644
index 0000000000..6f8bc0bf44
--- /dev/null
+++ b/sound/mods/protracker.h
@@ -0,0 +1,155 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ * Based on code by madmoose
+ *
+ * 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$
+ *
+ */
+
+#ifndef __SOUND_MODS_PROTRACKER_H__
+#define __SOUND_MODS_PROTRACKER_H__
+
+#include "common/stdafx.h"
+#include "common/system.h"
+
+#include "sound/mods/module.h"
+
+namespace Modules {
+
+class SoundBuffer {
+ private:
+ int _capacity;
+ int _size;
+ int16 *_data;
+
+public:
+ SoundBuffer() {
+ _size = 0;
+ _capacity = 8192;
+ _data = (int16 *)malloc(_capacity * sizeof(int16));
+ assert(_data);
+ }
+
+ ~SoundBuffer() {
+ free(_data);
+ }
+
+ int size() {
+ return _size;
+ }
+
+ int16 *getEnd() {
+ return _data + _size;
+ }
+
+ void ensureCapacity(int len) {
+ if (_size + len > _capacity) {
+ do {
+ _capacity *= 2;
+ } while (_size + len > _capacity);
+
+ _data = (int16 *)realloc(_data, _capacity * sizeof(int16));
+ assert(_data);
+ memset(_data + _size, 0, len);
+ }
+ }
+
+ void finish(int len) {
+ _size += len;
+ }
+
+ void pop(int16 *dest, int len) {
+ assert(_size >= len);
+ memcpy(dest, _data, len * sizeof(int16));
+ memmove(_data, _data + len, (_size - len) * sizeof(int16));
+ _size -= len;
+ }
+};
+
+class ProtrackerPlayer {
+ OSystem *_system;
+ Module *_module;
+
+ SoundBuffer *_buf;
+ double _generatedSamplesOverflow;
+
+ int _tick;
+ int _row;
+ int _pos;
+
+ int _patternDelay;
+
+ int _speed;
+ int _bpm;
+
+ // For effect 0xB - Jump To Pattern;
+ bool _hasJumpToPattern;
+ int _jumpToPattern;
+
+ // For effect 0xD - PatternBreak;
+ bool _hasPatternBreak;
+ int _skiprow;
+
+ // For effect 0xE6 - Pattern Loop
+ bool _hasPatternLoop;
+ int _patternLoopCount;
+ int _patternLoopRow;
+
+ struct {
+ byte sample;
+ uint16 period;
+ double offset;
+
+ byte vol;
+
+ // For effect 0x3 - Porta to note
+ uint16 portaToNote;
+ byte portaToNoteSpeed;
+
+ // For effect 0x4 - Vibrato
+ int vibrato;
+ byte vibratoPos;
+ byte vibratoSpeed;
+ byte vibratoDepth;
+ } _track[4];
+
+public:
+ ProtrackerPlayer() : _system(NULL), _module(NULL) { };
+
+ void init(OSystem *system);
+
+ void start();
+ void pause();
+ void stop();
+
+ void loadModule(const char *fn);
+
+ void mix(byte *buf, int len);
+
+private:
+ void generateSound();
+
+ void updateRow();
+ void updateEffects();
+
+ static void audioCallback(void *param, byte *buf, int len);
+};
+
+} // End of namespace Modules
+
+#endif