~ubuntu-branches/debian/jessie/scummvm/jessie

« back to all changes in this revision

Viewing changes to audio/mods/protracker.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Moritz Muehlenhoff
  • Date: 2011-05-25 19:02:23 UTC
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: james.westby@ubuntu.com-20110525190223-fiqm0oaec714xk31
Tags: upstream-1.3.0
ImportĀ upstreamĀ versionĀ 1.3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ScummVM - Graphic Adventure Engine
 
2
 *
 
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.
 
6
 *
 
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.
 
11
 
 
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.
 
16
 
 
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.
 
20
 *
 
21
 * $URL$
 
22
 * $Id$
 
23
 *
 
24
 */
 
25
 
 
26
#include "audio/mods/protracker.h"
 
27
#include "audio/mods/paula.h"
 
28
#include "audio/mods/module.h"
 
29
 
 
30
#include "audio/audiostream.h"
 
31
 
 
32
#include "common/textconsole.h"
 
33
 
 
34
namespace Modules {
 
35
 
 
36
class ProtrackerStream : public ::Audio::Paula {
 
37
private:
 
38
        Module _module;
 
39
 
 
40
        int _tick;
 
41
        int _row;
 
42
        int _pos;
 
43
 
 
44
        int _speed;
 
45
        int _bpm;
 
46
 
 
47
        // For effect 0xB - Jump To Pattern;
 
48
        bool _hasJumpToPattern;
 
49
        int _jumpToPattern;
 
50
 
 
51
        // For effect 0xD - PatternBreak;
 
52
        bool _hasPatternBreak;
 
53
        int _skipRow;
 
54
 
 
55
        // For effect 0xE6 - Pattern Loop
 
56
        bool _hasPatternLoop;
 
57
        int _patternLoopCount;
 
58
        int _patternLoopRow;
 
59
 
 
60
        // For effect 0xEE - Pattern Delay
 
61
        byte _patternDelay;
 
62
 
 
63
        static const int16 sinetable[];
 
64
 
 
65
        struct {
 
66
                byte sample;
 
67
                uint16 period;
 
68
                Offset offset;
 
69
 
 
70
                byte vol;
 
71
                byte finetune;
 
72
 
 
73
                // For effect 0x0 - Arpeggio
 
74
                bool arpeggio;
 
75
                byte arpeggioNotes[3];
 
76
 
 
77
                // For effect 0x3 - Porta to note
 
78
                uint16 portaToNote;
 
79
                byte portaToNoteSpeed;
 
80
 
 
81
                // For effect 0x4 - Vibrato
 
82
                int vibrato;
 
83
                byte vibratoPos;
 
84
                byte vibratoSpeed;
 
85
                byte vibratoDepth;
 
86
 
 
87
                // For effect 0xED - Delay sample
 
88
                byte delaySample;
 
89
                byte delaySampleTick;
 
90
        } _track[4];
 
91
 
 
92
public:
 
93
        ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo);
 
94
 
 
95
private:
 
96
        void interrupt();
 
97
 
 
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;
 
108
                        }
 
109
                }
 
110
        }
 
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;
 
116
        }
 
117
        void doVolSlide(int track, byte ex, byte ey) {
 
118
                int vol = _track[track].vol;
 
119
                if (ex == 0)
 
120
                        vol -= ey;
 
121
                else if (ey == 0)
 
122
                        vol += ex;
 
123
 
 
124
                if (vol < 0)
 
125
                        vol = 0;
 
126
                else if (vol > 64)
 
127
                        vol = 64;
 
128
 
 
129
                _track[track].vol = vol;
 
130
        }
 
131
 
 
132
        void updateRow();
 
133
        void updateEffects();
 
134
 
 
135
};
 
136
 
 
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
 
146
};
 
147
 
 
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);
 
151
        assert(result);
 
152
 
 
153
        _tick = _row = _pos = 0;
 
154
 
 
155
        _speed = 6;
 
156
        _bpm = 125;
 
157
 
 
158
        _hasJumpToPattern = false;
 
159
        _jumpToPattern = 0;
 
160
 
 
161
        _hasPatternBreak = false;
 
162
        _skipRow = 0;
 
163
 
 
164
        _hasPatternLoop = false;
 
165
        _patternLoopCount = 0;
 
166
        _patternLoopRow = 0;
 
167
 
 
168
        _patternDelay = 0;
 
169
 
 
170
        memset(_track, 0, sizeof(_track));
 
171
 
 
172
        startPaula();
 
173
}
 
174
 
 
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;
 
180
                const note_t note =
 
181
                    _module.pattern[_module.songpos[_pos]][_row][track];
 
182
 
 
183
                const int effect = note.effect >> 8;
 
184
 
 
185
                if (note.sample) {
 
186
                        if (_track[track].sample != note.sample) {
 
187
                                _track[track].vibratoPos = 0;
 
188
                        }
 
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;
 
192
                }
 
193
 
 
194
                if (note.period) {
 
195
                        if (effect != 3 && effect != 5) {
 
196
                                if (_track[track].finetune)
 
197
                                        _track[track].period = _module.noteToPeriod(note.note, _track[track].finetune);
 
198
                                else
 
199
                                        _track[track].period = note.period;
 
200
                                _track[track].offset = Offset(0);
 
201
                        }
 
202
                }
 
203
 
 
204
                const byte exy = note.effect & 0xff;
 
205
                const byte ex = (note.effect >> 4) & 0xf;
 
206
                const byte ey = note.effect & 0xf;
 
207
 
 
208
                int vol;
 
209
                switch (effect) {
 
210
                case 0x0:
 
211
                        if (exy) {
 
212
                                _track[track].arpeggio = true;
 
213
                                if (note.period) {
 
214
                                        _track[track].arpeggioNotes[0] = note.note;
 
215
                                        _track[track].arpeggioNotes[1] = note.note + ex;
 
216
                                        _track[track].arpeggioNotes[2] = note.note + ey;
 
217
                                }
 
218
                        }
 
219
                        break;
 
220
                case 0x1:
 
221
                        break;
 
222
                case 0x2:
 
223
                        break;
 
224
                case 0x3:
 
225
                        if (note.period)
 
226
                                _track[track].portaToNote = note.period;
 
227
                        if (exy)
 
228
                                _track[track].portaToNoteSpeed = exy;
 
229
                        break;
 
230
                case 0x4:
 
231
                        if (exy) {
 
232
                                _track[track].vibratoSpeed = ex;
 
233
                                _track[track].vibratoDepth = ey;
 
234
                        }
 
235
                        break;
 
236
                case 0x5:
 
237
                        doPorta(track);
 
238
                        doVolSlide(track, ex, ey);
 
239
                        break;
 
240
                case 0x6:
 
241
                        doVibrato(track);
 
242
                        doVolSlide(track, ex, ey);
 
243
                        break;
 
244
                case 0x9: // Set sample offset
 
245
                        if (exy) {
 
246
                                _track[track].offset = Offset(exy * 256);
 
247
                                setChannelOffset(track, _track[track].offset);
 
248
                        }
 
249
                        break;
 
250
                case 0xA:
 
251
                        break;
 
252
                case 0xB:
 
253
                        _hasJumpToPattern = true;
 
254
                        _jumpToPattern = exy;
 
255
                        break;
 
256
                case 0xC:
 
257
                        _track[track].vol = exy;
 
258
                        break;
 
259
                case 0xD:
 
260
                        _hasPatternBreak = true;
 
261
                        _skipRow = ex * 10 + ey;
 
262
                        break;
 
263
                case 0xE:
 
264
                        switch (ex) {
 
265
                        case 0x0: // Switch filters off
 
266
                                break;
 
267
                        case 0x1: // Fine slide up
 
268
                                _track[track].period -= exy;
 
269
                                break;
 
270
                        case 0x2: // Fine slide down
 
271
                                _track[track].period += exy;
 
272
                                break;
 
273
                        case 0x5: // Set finetune
 
274
                                _track[track].finetune = ey;
 
275
                                _module.sample[_track[track].sample].finetune = ey;
 
276
                                if (note.period) {
 
277
                                        if (ey)
 
278
                                                _track[track].period = _module.noteToPeriod(note.note, ey);
 
279
                                        else
 
280
                                                _track[track].period = note.period;
 
281
                                }
 
282
                                break;
 
283
                        case 0x6:
 
284
                                if (ey == 0) {
 
285
                                        _patternLoopRow = _row;
 
286
                                } else {
 
287
                                        _patternLoopCount++;
 
288
                                        if (_patternLoopCount <= ey)
 
289
                                                _hasPatternLoop = true;
 
290
                                        else
 
291
                                                _patternLoopCount = 0;
 
292
                                }
 
293
                                break;
 
294
                        case 0x9:
 
295
                                break;  // Retrigger note
 
296
                        case 0xA: // Fine volume slide up
 
297
                                vol = _track[track].vol + ey;
 
298
                                if (vol > 64)
 
299
                                        vol = 64;
 
300
                                _track[track].vol = vol;
 
301
                                break;
 
302
                        case 0xB: // Fine volume slide down
 
303
                                vol = _track[track].vol - ey;
 
304
                                if (vol < 0)
 
305
                                        vol = 0;
 
306
                                _track[track].vol = vol;
 
307
                                break;
 
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;
 
313
                                break;
 
314
                        case 0xE: // Pattern delay
 
315
                                _patternDelay = ey;
 
316
                                break;
 
317
                        default:
 
318
                                warning("Unimplemented effect %X", note.effect);
 
319
                        }
 
320
                        break;
 
321
 
 
322
                case 0xF:
 
323
                        if (exy < 0x20) {
 
324
                                _speed = exy;
 
325
                        } else {
 
326
                                _bpm = exy;
 
327
                                setInterruptFreq((int) (getRate() / (_bpm * 0.4)));
 
328
                        }
 
329
                        break;
 
330
                default:
 
331
                        warning("Unimplemented effect %X", note.effect);
 
332
                }
 
333
        }
 
334
}
 
335
 
 
336
void ProtrackerStream::updateEffects() {
 
337
        for (int track = 0; track < 4; track++) {
 
338
                _track[track].vibrato = 0;
 
339
 
 
340
                const note_t note =
 
341
                    _module.pattern[_module.songpos[_pos]][_row][track];
 
342
 
 
343
                const int effect = note.effect >> 8;
 
344
 
 
345
                const int exy = note.effect & 0xff;
 
346
                const int ex = (note.effect >> 4) & 0xf;
 
347
                const int ey = (note.effect) & 0xf;
 
348
 
 
349
                switch (effect) {
 
350
                case 0x0:
 
351
                        if (exy) {
 
352
                                const int idx = (_tick == 1) ? 0 : (_tick % 3);
 
353
                                _track[track].period =
 
354
                                        _module.noteToPeriod(_track[track].arpeggioNotes[idx],
 
355
                                                        _track[track].finetune);
 
356
                        }
 
357
                        break;
 
358
                case 0x1:
 
359
                        _track[track].period -= exy;
 
360
                        break;
 
361
                case 0x2:
 
362
                        _track[track].period += exy;
 
363
                        break;
 
364
                case 0x3:
 
365
                        doPorta(track);
 
366
                        break;
 
367
                case 0x4:
 
368
                        doVibrato(track);
 
369
                        break;
 
370
                case 0x5:
 
371
                        doPorta(track);
 
372
                        doVolSlide(track, ex, ey);
 
373
                        break;
 
374
                case 0x6:
 
375
                        doVibrato(track);
 
376
                        doVolSlide(track, ex, ey);
 
377
                        break;
 
378
                case 0xA:
 
379
                        doVolSlide(track, ex, ey);
 
380
                        break;
 
381
                case 0xE:
 
382
                        switch (ex) {
 
383
                        case 0x6:
 
384
                                break;  // Pattern loop
 
385
                        case 0x9:       // Retrigger note
 
386
                                if (ey && (_tick % ey) == 0)
 
387
                                        _track[track].offset = Offset(0);
 
388
                                break;
 
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;
 
395
                                }
 
396
                                break;
 
397
                        }
 
398
                        break;
 
399
                }
 
400
        }
 
401
}
 
402
 
 
403
void ProtrackerStream::interrupt() {
 
404
        int track;
 
405
 
 
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);
 
411
                }
 
412
        }
 
413
 
 
414
        if (_tick == 0) {
 
415
                if (_hasJumpToPattern) {
 
416
                        _hasJumpToPattern = false;
 
417
                        _pos = _jumpToPattern;
 
418
                        _row = 0;
 
419
                } else if (_hasPatternBreak) {
 
420
                        _hasPatternBreak = false;
 
421
                        _row = _skipRow;
 
422
                        _pos = (_pos + 1) % _module.songlen;
 
423
                        _patternLoopRow = 0;
 
424
                } else if (_hasPatternLoop) {
 
425
                        _hasPatternLoop = false;
 
426
                        _row = _patternLoopRow;
 
427
                }
 
428
                if (_row >= 64) {
 
429
                        _row = 0;
 
430
                        _pos = (_pos + 1) % _module.songlen;
 
431
                        _patternLoopRow = 0;
 
432
                }
 
433
 
 
434
                updateRow();
 
435
        } else
 
436
                updateEffects();
 
437
 
 
438
        _tick = (_tick + 1) % (_speed + _patternDelay * _speed);
 
439
        if (_tick == 0) {
 
440
                _row++;
 
441
                _patternDelay = 0;
 
442
        }
 
443
 
 
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,
 
450
                                       sample.data,
 
451
                                       sample.replen > 2 ? sample.data + sample.repeat : 0,
 
452
                                       sample.len,
 
453
                                       sample.replen);
 
454
                        setChannelOffset(track, _track[track].offset);
 
455
                        _track[track].sample = 0;
 
456
                }
 
457
        }
 
458
}
 
459
 
 
460
} // End of namespace Modules
 
461
 
 
462
namespace Audio {
 
463
 
 
464
AudioStream *makeProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo) {
 
465
        return new Modules::ProtrackerStream(stream, offs, rate, stereo);
 
466
}
 
467
 
 
468
} // End of namespace Audio