Add WIP (not yet plugged in) Protracker modules player
svn-id: r24351
This commit is contained in:
parent
0a51fbb326
commit
7282e24d3b
5 changed files with 1012 additions and 0 deletions
177
sound/mods/module.cpp
Normal file
177
sound/mods/module.cpp
Normal file
|
@ -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
|
75
sound/mods/module.h
Normal file
75
sound/mods/module.h
Normal file
|
@ -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
|
603
sound/mods/protracker.cpp
Normal file
603
sound/mods/protracker.cpp
Normal file
|
@ -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
|
||||
|
155
sound/mods/protracker.h
Normal file
155
sound/mods/protracker.h
Normal file
|
@ -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
|
|
@ -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 \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue