1
/* ScummVM - Graphic Adventure Engine
3
* ScummVM is the legal property of its developers, whose names
4
* are too numerous to list here. Please refer to the COPYRIGHT
5
* file distributed with this source distribution.
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License
9
* as published by the Free Software Foundation; either version 2
10
* of the License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26
#include "audio/mods/protracker.h"
27
#include "audio/mods/paula.h"
28
#include "audio/mods/module.h"
30
#include "audio/audiostream.h"
32
#include "common/textconsole.h"
36
class ProtrackerStream : public ::Audio::Paula {
47
// For effect 0xB - Jump To Pattern;
48
bool _hasJumpToPattern;
51
// For effect 0xD - PatternBreak;
52
bool _hasPatternBreak;
55
// For effect 0xE6 - Pattern Loop
57
int _patternLoopCount;
60
// For effect 0xEE - Pattern Delay
63
static const int16 sinetable[];
73
// For effect 0x0 - Arpeggio
75
byte arpeggioNotes[3];
77
// For effect 0x3 - Porta to note
79
byte portaToNoteSpeed;
81
// For effect 0x4 - Vibrato
87
// For effect 0xED - Delay sample
93
ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo);
98
void doPorta(int track) {
99
if (_track[track].portaToNote && _track[track].portaToNoteSpeed) {
100
if (_track[track].period < _track[track].portaToNote) {
101
_track[track].period += _track[track].portaToNoteSpeed;
102
if (_track[track].period > _track[track].portaToNote)
103
_track[track].period = _track[track].portaToNote;
104
} else if (_track[track].period > _track[track].portaToNote) {
105
_track[track].period -= _track[track].portaToNoteSpeed;
106
if (_track[track].period < _track[track].portaToNote)
107
_track[track].period = _track[track].portaToNote;
111
void doVibrato(int track) {
112
_track[track].vibrato =
113
(_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128;
114
_track[track].vibratoPos += _track[track].vibratoSpeed;
115
_track[track].vibratoPos %= 64;
117
void doVolSlide(int track, byte ex, byte ey) {
118
int vol = _track[track].vol;
129
_track[track].vol = vol;
133
void updateEffects();
137
const int16 ProtrackerStream::sinetable[64] = {
138
0, 24, 49, 74, 97, 120, 141, 161,
139
180, 197, 212, 224, 235, 244, 250, 253,
140
255, 253, 250, 244, 235, 224, 212, 197,
141
180, 161, 141, 120, 97, 74, 49, 24,
142
0, -24, -49, -74, -97, -120, -141, -161,
143
-180, -197, -212, -224, -235, -244, -250, -253,
144
-255, -253, -250, -244, -235, -224, -212, -197,
145
-180, -161, -141, -120, -97, -74, -49, -24
148
ProtrackerStream::ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo) :
149
Paula(stereo, rate, rate/50) {
150
bool result = _module.load(*stream, offs);
153
_tick = _row = _pos = 0;
158
_hasJumpToPattern = false;
161
_hasPatternBreak = false;
164
_hasPatternLoop = false;
165
_patternLoopCount = 0;
170
memset(_track, 0, sizeof(_track));
175
void ProtrackerStream::updateRow() {
176
for (int track = 0; track < 4; track++) {
177
_track[track].arpeggio = false;
178
_track[track].vibrato = 0;
179
_track[track].delaySampleTick = 0;
181
_module.pattern[_module.songpos[_pos]][_row][track];
183
const int effect = note.effect >> 8;
186
if (_track[track].sample != note.sample) {
187
_track[track].vibratoPos = 0;
189
_track[track].sample = note.sample;
190
_track[track].finetune = _module.sample[note.sample - 1].finetune;
191
_track[track].vol = _module.sample[note.sample - 1].vol;
195
if (effect != 3 && effect != 5) {
196
if (_track[track].finetune)
197
_track[track].period = _module.noteToPeriod(note.note, _track[track].finetune);
199
_track[track].period = note.period;
200
_track[track].offset = Offset(0);
204
const byte exy = note.effect & 0xff;
205
const byte ex = (note.effect >> 4) & 0xf;
206
const byte ey = note.effect & 0xf;
212
_track[track].arpeggio = true;
214
_track[track].arpeggioNotes[0] = note.note;
215
_track[track].arpeggioNotes[1] = note.note + ex;
216
_track[track].arpeggioNotes[2] = note.note + ey;
226
_track[track].portaToNote = note.period;
228
_track[track].portaToNoteSpeed = exy;
232
_track[track].vibratoSpeed = ex;
233
_track[track].vibratoDepth = ey;
238
doVolSlide(track, ex, ey);
242
doVolSlide(track, ex, ey);
244
case 0x9: // Set sample offset
246
_track[track].offset = Offset(exy * 256);
247
setChannelOffset(track, _track[track].offset);
253
_hasJumpToPattern = true;
254
_jumpToPattern = exy;
257
_track[track].vol = exy;
260
_hasPatternBreak = true;
261
_skipRow = ex * 10 + ey;
265
case 0x0: // Switch filters off
267
case 0x1: // Fine slide up
268
_track[track].period -= exy;
270
case 0x2: // Fine slide down
271
_track[track].period += exy;
273
case 0x5: // Set finetune
274
_track[track].finetune = ey;
275
_module.sample[_track[track].sample].finetune = ey;
278
_track[track].period = _module.noteToPeriod(note.note, ey);
280
_track[track].period = note.period;
285
_patternLoopRow = _row;
288
if (_patternLoopCount <= ey)
289
_hasPatternLoop = true;
291
_patternLoopCount = 0;
295
break; // Retrigger note
296
case 0xA: // Fine volume slide up
297
vol = _track[track].vol + ey;
300
_track[track].vol = vol;
302
case 0xB: // Fine volume slide down
303
vol = _track[track].vol - ey;
306
_track[track].vol = vol;
308
case 0xD: // Delay sample
309
_track[track].delaySampleTick = ey;
310
_track[track].delaySample = _track[track].sample;
311
_track[track].sample = 0;
312
_track[track].vol = 0;
314
case 0xE: // Pattern delay
318
warning("Unimplemented effect %X", note.effect);
327
setInterruptFreq((int) (getRate() / (_bpm * 0.4)));
331
warning("Unimplemented effect %X", note.effect);
336
void ProtrackerStream::updateEffects() {
337
for (int track = 0; track < 4; track++) {
338
_track[track].vibrato = 0;
341
_module.pattern[_module.songpos[_pos]][_row][track];
343
const int effect = note.effect >> 8;
345
const int exy = note.effect & 0xff;
346
const int ex = (note.effect >> 4) & 0xf;
347
const int ey = (note.effect) & 0xf;
352
const int idx = (_tick == 1) ? 0 : (_tick % 3);
353
_track[track].period =
354
_module.noteToPeriod(_track[track].arpeggioNotes[idx],
355
_track[track].finetune);
359
_track[track].period -= exy;
362
_track[track].period += exy;
372
doVolSlide(track, ex, ey);
376
doVolSlide(track, ex, ey);
379
doVolSlide(track, ex, ey);
384
break; // Pattern loop
385
case 0x9: // Retrigger note
386
if (ey && (_tick % ey) == 0)
387
_track[track].offset = Offset(0);
389
case 0xD: // Delay sample
390
if (_tick == _track[track].delaySampleTick) {
391
_track[track].sample = _track[track].delaySample;
392
_track[track].offset = Offset(0);
393
if (_track[track].sample)
394
_track[track].vol = _module.sample[_track[track].sample - 1].vol;
403
void ProtrackerStream::interrupt() {
406
for (track = 0; track < 4; track++) {
407
_track[track].offset = getChannelOffset(track);
408
if (_tick == 0 && _track[track].arpeggio) {
409
_track[track].period = _module.noteToPeriod(_track[track].arpeggioNotes[0],
410
_track[track].finetune);
415
if (_hasJumpToPattern) {
416
_hasJumpToPattern = false;
417
_pos = _jumpToPattern;
419
} else if (_hasPatternBreak) {
420
_hasPatternBreak = false;
422
_pos = (_pos + 1) % _module.songlen;
424
} else if (_hasPatternLoop) {
425
_hasPatternLoop = false;
426
_row = _patternLoopRow;
430
_pos = (_pos + 1) % _module.songlen;
438
_tick = (_tick + 1) % (_speed + _patternDelay * _speed);
444
for (track = 0; track < 4; track++) {
445
setChannelVolume(track, _track[track].vol);
446
setChannelPeriod(track, _track[track].period + _track[track].vibrato);
447
if (_track[track].sample) {
448
sample_t &sample = _module.sample[_track[track].sample - 1];
449
setChannelData(track,
451
sample.replen > 2 ? sample.data + sample.repeat : 0,
454
setChannelOffset(track, _track[track].offset);
455
_track[track].sample = 0;
460
} // End of namespace Modules
464
AudioStream *makeProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo) {
465
return new Modules::ProtrackerStream(stream, offs, rate, stereo);
468
} // End of namespace Audio