mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-21 03:31:40 +00:00
AUDIO: Make subclassing Module and ProtrackerStream easier
To implement a MOD variant for the Chewy engine, this commit makes some changes to make subclassing of the ProtrackerStream and associated Module class easier.
This commit is contained in:
parent
d0b5a35785
commit
caca2ea61f
@ -76,11 +76,11 @@ public:
|
||||
Module();
|
||||
~Module();
|
||||
|
||||
bool load(Common::SeekableReadStream &stream, int offs);
|
||||
virtual bool load(Common::SeekableReadStream &stream, int offs);
|
||||
static byte periodToNote(int16 period, byte finetune = 0);
|
||||
static int16 noteToPeriod(byte note, byte finetune = 0);
|
||||
|
||||
private:
|
||||
protected:
|
||||
static const int16 periods[16][60];
|
||||
static const uint32 signatures[];
|
||||
};
|
||||
|
@ -52,10 +52,10 @@ Paula::Paula(bool stereo, int rate, uint interruptFreq, FilterMode filterMode, i
|
||||
_filterState.a0[2] = filterCalculateA0(rate, 7000);
|
||||
|
||||
clearVoices();
|
||||
_voice[0].panning = 191;
|
||||
_voice[1].panning = 63;
|
||||
_voice[2].panning = 63;
|
||||
_voice[3].panning = 191;
|
||||
_voice[0].panning = PANNING_RIGHT;
|
||||
_voice[1].panning = PANNING_LEFT;
|
||||
_voice[2].panning = PANNING_LEFT;
|
||||
_voice[3].panning = PANNING_RIGHT;
|
||||
|
||||
if (_intFreq == 0)
|
||||
_intFreq = _rate;
|
||||
|
@ -36,6 +36,10 @@ namespace Audio {
|
||||
class Paula : public AudioStream {
|
||||
public:
|
||||
static const int NUM_VOICES = 4;
|
||||
// Default panning value for left channels.
|
||||
static const int PANNING_LEFT = 63;
|
||||
// Default panning value for right channels.
|
||||
static const int PANNING_RIGHT = 191;
|
||||
enum {
|
||||
kPalSystemClock = 7093790,
|
||||
kNtscSystemClock = 7159090,
|
||||
|
@ -28,114 +28,6 @@
|
||||
|
||||
namespace Modules {
|
||||
|
||||
class ProtrackerStream : public ::Audio::Paula {
|
||||
private:
|
||||
Module _module;
|
||||
|
||||
int _tick;
|
||||
int _row;
|
||||
int _pos;
|
||||
|
||||
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;
|
||||
|
||||
// For effect 0xEE - Pattern Delay
|
||||
byte _patternDelay;
|
||||
|
||||
static const int16 sinetable[];
|
||||
|
||||
struct Track {
|
||||
byte sample;
|
||||
byte lastSample;
|
||||
uint16 period;
|
||||
Offset offset;
|
||||
|
||||
byte vol;
|
||||
byte finetune;
|
||||
|
||||
// For effect 0x0 - Arpeggio
|
||||
bool arpeggio;
|
||||
byte arpeggioNotes[3];
|
||||
|
||||
// For effect 0x3 - Porta to note
|
||||
uint16 portaToNote;
|
||||
byte portaToNoteSpeed;
|
||||
|
||||
// For effect 0x4 - Vibrato
|
||||
int vibrato;
|
||||
byte vibratoPos;
|
||||
byte vibratoSpeed;
|
||||
byte vibratoDepth;
|
||||
|
||||
// For effect 0xED - Delay sample
|
||||
byte delaySample;
|
||||
byte delaySampleTick;
|
||||
} _track[4];
|
||||
|
||||
public:
|
||||
ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo);
|
||||
|
||||
Modules::Module *getModule() {
|
||||
// Ordinarily, the Module is not meant to be seen outside of
|
||||
// this class, but occasionally, it's useful to be able to
|
||||
// manipulate it directly. The Hopkins engine uses this to
|
||||
// repair a broken song.
|
||||
return &_module;
|
||||
}
|
||||
|
||||
private:
|
||||
void interrupt() override;
|
||||
|
||||
void doPorta(int track) {
|
||||
if (_track[track].portaToNote && _track[track].portaToNoteSpeed) {
|
||||
int distance = _track[track].period - _track[track].portaToNote;
|
||||
int sign = distance > 0 ? 1 : -1;
|
||||
|
||||
if ((sign * distance) > _track[track].portaToNoteSpeed)
|
||||
_track[track].period -= sign * _track[track].portaToNoteSpeed;
|
||||
else
|
||||
_track[track].period = _track[track].portaToNote;
|
||||
}
|
||||
}
|
||||
void doVibrato(int track) {
|
||||
_track[track].vibrato =
|
||||
(_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128;
|
||||
_track[track].vibratoPos += _track[track].vibratoSpeed;
|
||||
_track[track].vibratoPos %= 64;
|
||||
}
|
||||
void doVolSlide(int track, byte ex, byte ey) {
|
||||
int 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;
|
||||
}
|
||||
|
||||
void updateRow();
|
||||
void updateEffects();
|
||||
|
||||
};
|
||||
|
||||
const int16 ProtrackerStream::sinetable[64] = {
|
||||
0, 24, 49, 74, 97, 120, 141, 161,
|
||||
180, 197, 212, 224, 235, 244, 250, 253,
|
||||
@ -147,10 +39,8 @@ const int16 ProtrackerStream::sinetable[64] = {
|
||||
-180, -161, -141, -120, -97, -74, -49, -24
|
||||
};
|
||||
|
||||
ProtrackerStream::ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo) :
|
||||
Paula(stereo, rate, rate/50) {
|
||||
bool result = _module.load(*stream, offs);
|
||||
assert(result);
|
||||
ProtrackerStream::ProtrackerStream(int rate, bool stereo) : Paula(stereo, rate, rate / 50) {
|
||||
_module = nullptr;
|
||||
|
||||
_tick = _row = _pos = 0;
|
||||
|
||||
@ -170,17 +60,31 @@ ProtrackerStream::ProtrackerStream(Common::SeekableReadStream *stream, int offs,
|
||||
_patternDelay = 0;
|
||||
|
||||
ARRAYCLEAR(_track);
|
||||
}
|
||||
|
||||
ProtrackerStream::ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo) :
|
||||
ProtrackerStream(rate, stereo) {
|
||||
_module = new Module();
|
||||
bool result = _module->load(*stream, offs);
|
||||
assert(result);
|
||||
|
||||
startPaula();
|
||||
}
|
||||
|
||||
ProtrackerStream::~ProtrackerStream() {
|
||||
if (_module) {
|
||||
delete _module;
|
||||
_module = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtrackerStream::updateRow() {
|
||||
for (int track = 0; track < 4; track++) {
|
||||
_track[track].arpeggio = false;
|
||||
_track[track].vibrato = 0;
|
||||
_track[track].delaySampleTick = 0;
|
||||
const note_t note =
|
||||
_module.pattern[_module.songpos[_pos]][_row][track];
|
||||
_module->pattern[_module->songpos[_pos]][_row][track];
|
||||
|
||||
const int effect = note.effect >> 8;
|
||||
|
||||
@ -190,14 +94,14 @@ void ProtrackerStream::updateRow() {
|
||||
}
|
||||
_track[track].sample = note.sample;
|
||||
_track[track].lastSample = note.sample;
|
||||
_track[track].finetune = _module.sample[note.sample - 1].finetune;
|
||||
_track[track].vol = _module.sample[note.sample - 1].vol;
|
||||
_track[track].finetune = _module->sample[note.sample - 1].finetune;
|
||||
_track[track].vol = _module->sample[note.sample - 1].vol;
|
||||
}
|
||||
|
||||
if (note.period) {
|
||||
if (effect != 3 && effect != 5) {
|
||||
if (_track[track].finetune)
|
||||
_track[track].period = _module.noteToPeriod(note.note, _track[track].finetune);
|
||||
_track[track].period = _module->noteToPeriod(note.note, _track[track].finetune);
|
||||
else
|
||||
_track[track].period = note.period;
|
||||
|
||||
@ -215,7 +119,7 @@ void ProtrackerStream::updateRow() {
|
||||
case 0x0:
|
||||
if (exy) {
|
||||
_track[track].arpeggio = true;
|
||||
byte trackNote = _module.periodToNote(_track[track].period);
|
||||
byte trackNote = _module->periodToNote(_track[track].period);
|
||||
_track[track].arpeggioNotes[0] = trackNote;
|
||||
_track[track].arpeggioNotes[1] = trackNote + ex;
|
||||
_track[track].arpeggioNotes[2] = trackNote + ey;
|
||||
@ -276,10 +180,10 @@ void ProtrackerStream::updateRow() {
|
||||
break;
|
||||
case 0x5: // Set finetune
|
||||
_track[track].finetune = ey;
|
||||
_module.sample[_track[track].sample].finetune = ey;
|
||||
_module->sample[_track[track].sample].finetune = ey;
|
||||
if (note.period) {
|
||||
if (ey)
|
||||
_track[track].period = _module.noteToPeriod(note.note, ey);
|
||||
_track[track].period = _module->noteToPeriod(note.note, ey);
|
||||
else
|
||||
_track[track].period = note.period;
|
||||
}
|
||||
@ -342,7 +246,7 @@ void ProtrackerStream::updateEffects() {
|
||||
_track[track].vibrato = 0;
|
||||
|
||||
const note_t note =
|
||||
_module.pattern[_module.songpos[_pos]][_row][track];
|
||||
_module->pattern[_module->songpos[_pos]][_row][track];
|
||||
|
||||
const int effect = note.effect >> 8;
|
||||
|
||||
@ -355,7 +259,7 @@ void ProtrackerStream::updateEffects() {
|
||||
if (exy) {
|
||||
const int idx = (_tick == 1) ? 0 : (_tick % 3);
|
||||
_track[track].period =
|
||||
_module.noteToPeriod(_track[track].arpeggioNotes[idx],
|
||||
_module->noteToPeriod(_track[track].arpeggioNotes[idx],
|
||||
_track[track].finetune);
|
||||
}
|
||||
break;
|
||||
@ -395,7 +299,7 @@ void ProtrackerStream::updateEffects() {
|
||||
_track[track].sample = _track[track].delaySample;
|
||||
_track[track].offset = Offset(0);
|
||||
if (_track[track].sample)
|
||||
_track[track].vol = _module.sample[_track[track].sample - 1].vol;
|
||||
_track[track].vol = _module->sample[_track[track].sample - 1].vol;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -414,7 +318,7 @@ void ProtrackerStream::interrupt() {
|
||||
for (track = 0; track < 4; track++) {
|
||||
_track[track].offset = getChannelOffset(track);
|
||||
if (_tick == 0 && _track[track].arpeggio) {
|
||||
_track[track].period = _module.noteToPeriod(_track[track].arpeggioNotes[0],
|
||||
_track[track].period = _module->noteToPeriod(_track[track].arpeggioNotes[0],
|
||||
_track[track].finetune);
|
||||
}
|
||||
}
|
||||
@ -427,7 +331,7 @@ void ProtrackerStream::interrupt() {
|
||||
} else if (_hasPatternBreak) {
|
||||
_hasPatternBreak = false;
|
||||
_row = _skipRow;
|
||||
_pos = (_pos + 1) % _module.songlen;
|
||||
_pos = (_pos + 1) % _module->songlen;
|
||||
_patternLoopRow = 0;
|
||||
} else if (_hasPatternLoop) {
|
||||
_hasPatternLoop = false;
|
||||
@ -435,7 +339,7 @@ void ProtrackerStream::interrupt() {
|
||||
}
|
||||
if (_row >= 64) {
|
||||
_row = 0;
|
||||
_pos = (_pos + 1) % _module.songlen;
|
||||
_pos = (_pos + 1) % _module->songlen;
|
||||
_patternLoopRow = 0;
|
||||
}
|
||||
|
||||
@ -453,7 +357,7 @@ void ProtrackerStream::interrupt() {
|
||||
setChannelVolume(track, _track[track].vol);
|
||||
setChannelPeriod(track, _track[track].period + _track[track].vibrato);
|
||||
if (_track[track].sample) {
|
||||
sample_t &sample = _module.sample[_track[track].sample - 1];
|
||||
sample_t &sample = _module->sample[_track[track].sample - 1];
|
||||
setChannelData(track,
|
||||
sample.data,
|
||||
sample.replen > 2 ? sample.data + sample.repeat : nullptr,
|
||||
@ -465,6 +369,40 @@ void ProtrackerStream::interrupt() {
|
||||
}
|
||||
}
|
||||
|
||||
void ProtrackerStream::doPorta(int track) {
|
||||
if (_track[track].portaToNote && _track[track].portaToNoteSpeed) {
|
||||
int distance = _track[track].period - _track[track].portaToNote;
|
||||
int sign = distance > 0 ? 1 : -1;
|
||||
|
||||
if ((sign * distance) > _track[track].portaToNoteSpeed)
|
||||
_track[track].period -= sign * _track[track].portaToNoteSpeed;
|
||||
else
|
||||
_track[track].period = _track[track].portaToNote;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtrackerStream::doVibrato(int track) {
|
||||
_track[track].vibrato =
|
||||
(_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128;
|
||||
_track[track].vibratoPos += _track[track].vibratoSpeed;
|
||||
_track[track].vibratoPos %= 64;
|
||||
}
|
||||
|
||||
void ProtrackerStream::doVolSlide(int track, byte ex, byte ey) {
|
||||
int 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;
|
||||
}
|
||||
|
||||
} // End of namespace Modules
|
||||
|
||||
namespace Audio {
|
||||
|
@ -26,17 +26,107 @@
|
||||
* - parallaction
|
||||
* - gob
|
||||
* - hopkins
|
||||
* - chewy (subclass)
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_MODS_PROTRACKER_H
|
||||
#define AUDIO_MODS_PROTRACKER_H
|
||||
|
||||
#include "audio/mods/paula.h"
|
||||
#include "audio/mods/module.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Modules {
|
||||
class Module;
|
||||
|
||||
class ProtrackerStream : public ::Audio::Paula {
|
||||
protected:
|
||||
Module *_module;
|
||||
|
||||
private:
|
||||
int _tick;
|
||||
int _row;
|
||||
int _pos;
|
||||
|
||||
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;
|
||||
|
||||
// For effect 0xEE - Pattern Delay
|
||||
byte _patternDelay;
|
||||
|
||||
static const int16 sinetable[];
|
||||
|
||||
struct Track {
|
||||
byte sample;
|
||||
byte lastSample;
|
||||
uint16 period;
|
||||
Offset offset;
|
||||
|
||||
byte vol;
|
||||
byte finetune;
|
||||
|
||||
// For effect 0x0 - Arpeggio
|
||||
bool arpeggio;
|
||||
byte arpeggioNotes[3];
|
||||
|
||||
// For effect 0x3 - Porta to note
|
||||
uint16 portaToNote;
|
||||
byte portaToNoteSpeed;
|
||||
|
||||
// For effect 0x4 - Vibrato
|
||||
int vibrato;
|
||||
byte vibratoPos;
|
||||
byte vibratoSpeed;
|
||||
byte vibratoDepth;
|
||||
|
||||
// For effect 0xED - Delay sample
|
||||
byte delaySample;
|
||||
byte delaySampleTick;
|
||||
} _track[4];
|
||||
|
||||
public:
|
||||
ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo);
|
||||
|
||||
protected:
|
||||
ProtrackerStream(int rate, bool stereo);
|
||||
|
||||
public:
|
||||
virtual ~ProtrackerStream();
|
||||
|
||||
Modules::Module *getModule() {
|
||||
// Ordinarily, the Module is not meant to be seen outside of
|
||||
// this class, but occasionally, it's useful to be able to
|
||||
// manipulate it directly. The Hopkins engine uses this to
|
||||
// repair a broken song.
|
||||
return _module;
|
||||
}
|
||||
|
||||
private:
|
||||
void interrupt() override;
|
||||
|
||||
void doPorta(int track);
|
||||
void doVibrato(int track);
|
||||
void doVolSlide(int track, byte ex, byte ey);
|
||||
|
||||
void updateRow();
|
||||
void updateEffects();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
Loading…
x
Reference in New Issue
Block a user