diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/mods/module.cpp | 177 | ||||
-rw-r--r-- | sound/mods/module.h | 75 | ||||
-rw-r--r-- | sound/mods/protracker.cpp | 603 | ||||
-rw-r--r-- | sound/mods/protracker.h | 155 | ||||
-rw-r--r-- | sound/module.mk | 2 |
5 files changed, 1012 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 diff --git a/sound/module.mk b/sound/module.mk index f4ba8eeed4..af299a3357 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -18,6 +18,8 @@ MODULE_OBJS := \ voc.o \ vorbis.o \ wave.o \ + mods/module.o \ + mods/protracker.o \ softsynth/adlib.o \ softsynth/ym2612.o \ softsynth/fluidsynth.o \ |