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

« back to all changes in this revision

Viewing changes to sound/mods/tfmx.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Moritz Muehlenhoff
  • Date: 2010-05-07 18:57:09 UTC
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20100507185709-34v8yycywjrou5o3
Tags: upstream-1.1.1
ImportĀ upstreamĀ versionĀ 1.1.1

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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-1-1-1/sound/mods/tfmx.cpp $
 
22
 * $Id: tfmx.cpp 47541 2010-01-25 01:39:44Z lordhoto $
 
23
 *
 
24
 */
 
25
 
 
26
#include "common/scummsys.h"
 
27
#include "common/endian.h"
 
28
#include "common/stream.h"
 
29
#include "common/util.h"
 
30
#include "common/debug.h"
 
31
 
 
32
#include "sound/mods/tfmx.h"
 
33
 
 
34
// test for engines using this class.
 
35
#if defined(SOUND_MODS_TFMX_H)
 
36
 
 
37
// couple debug-functions
 
38
namespace {
 
39
        void displayPatternstep(const void *const vptr);
 
40
        void displayMacroStep(const void *const vptr);
 
41
 
 
42
        const uint16 noteIntervalls[64] = {
 
43
        1710, 1614, 1524, 1438, 1357, 1281, 1209, 1141, 1077, 1017,  960,  908,
 
44
         856,  810,  764,  720,  680,  642,  606,  571,  539,  509,  480,  454,
 
45
         428,  404,  381,  360,  340,  320,  303,  286,  270,  254,  240,  227,
 
46
         214,  202,  191,  180,  170,  160,  151,  143,  135,  127,  120,  113,
 
47
         214,  202,  191,  180,  170,  160,  151,  143,  135,  127,  120,  113,
 
48
         214,  202,  191,  180 };
 
49
}
 
50
 
 
51
namespace Audio {
 
52
 
 
53
Tfmx::Tfmx(int rate, bool stereo)
 
54
        : Paula(stereo, rate),
 
55
          _resource(),
 
56
          _resourceSample(),
 
57
          _playerCtx(),
 
58
          _deleteResource(false) {
 
59
 
 
60
        _playerCtx.stopWithLastPattern = false;
 
61
 
 
62
        for (int i = 0; i < kNumVoices; ++i)
 
63
                _channelCtx[i].paulaChannel = (byte)i;
 
64
 
 
65
        _playerCtx.volume = 0x40;
 
66
        _playerCtx.patternSkip = 6;
 
67
        stopSongImpl();
 
68
 
 
69
        setTimerBaseValue(kPalCiaClock);
 
70
        setInterruptFreqUnscaled(kPalDefaultCiaVal);
 
71
}
 
72
 
 
73
Tfmx::~Tfmx() {
 
74
        freeResourceDataImpl();
 
75
}
 
76
 
 
77
void Tfmx::interrupt() {
 
78
        assert(!_end);
 
79
        ++_playerCtx.tickCount;
 
80
 
 
81
        for (int i = 0; i < kNumVoices; ++i) {
 
82
                if (_channelCtx[i].dmaIntCount) {
 
83
                        // wait for DMA Interupts to happen
 
84
                        int doneDma = getChannelDmaCount(i);
 
85
                        if (doneDma >= _channelCtx[i].dmaIntCount) {
 
86
                                _channelCtx[i].dmaIntCount = 0;
 
87
                                _channelCtx[i].macroRun = true;
 
88
                        }
 
89
                }
 
90
        }
 
91
 
 
92
        for (int i = 0; i < kNumVoices; ++i) {
 
93
                ChannelContext &channel = _channelCtx[i];
 
94
 
 
95
                if (channel.sfxLockTime >= 0)
 
96
                        --channel.sfxLockTime;
 
97
                else {
 
98
                        channel.sfxLocked = false;
 
99
                        channel.customMacroPrio = 0;
 
100
                }
 
101
 
 
102
                // externally queued macros
 
103
                if (channel.customMacro) {
 
104
                        const byte *const noteCmd = (const byte *)&channel.customMacro;
 
105
                        channel.sfxLocked = false;
 
106
                        noteCommand(noteCmd[0], noteCmd[1], (noteCmd[2] & 0xF0) | (uint8)i, noteCmd[3]);
 
107
                        channel.customMacro = 0;
 
108
                        channel.sfxLocked = (channel.customMacroPrio != 0);
 
109
                }
 
110
 
 
111
                // apply timebased effects on Parameters
 
112
                if (channel.macroSfxRun > 0)
 
113
                        effects(channel);
 
114
 
 
115
                // see if we have to run the macro-program
 
116
                if (channel.macroRun) {
 
117
                        if (!channel.macroWait)
 
118
                                macroRun(channel);
 
119
                        else
 
120
                                --channel.macroWait;
 
121
                }
 
122
 
 
123
                Paula::setChannelPeriod(i, channel.period);
 
124
                if (channel.macroSfxRun >= 0)
 
125
                        channel.macroSfxRun = 1;
 
126
 
 
127
                // TODO: handling pending DMAOff?
 
128
        }
 
129
 
 
130
        // Patterns are only processed each _playerCtx.timerCount + 1 tick
 
131
        if (_playerCtx.song >= 0 && !_playerCtx.patternCount--) {
 
132
                _playerCtx.patternCount = _playerCtx.patternSkip;
 
133
                advancePatterns();
 
134
        }
 
135
}
 
136
 
 
137
void Tfmx::effects(ChannelContext &channel) {
 
138
        // addBegin
 
139
        if (channel.addBeginLength) {
 
140
                channel.sampleStart += channel.addBeginDelta;
 
141
                Paula::setChannelSampleStart(channel.paulaChannel, getSamplePtr(channel.sampleStart));
 
142
                if (!(--channel.addBeginCount)) {
 
143
                        channel.addBeginCount = channel.addBeginLength;
 
144
                        channel.addBeginDelta = -channel.addBeginDelta;
 
145
                }
 
146
        }
 
147
 
 
148
        // vibrato
 
149
        if (channel.vibLength) {
 
150
                channel.vibValue += channel.vibDelta;
 
151
                if (--channel.vibCount == 0) {
 
152
                        channel.vibCount = channel.vibLength;
 
153
                        channel.vibDelta = -channel.vibDelta;
 
154
                }
 
155
                if (!channel.portaDelta) {
 
156
                        // 16x16 bit multiplication, casts needed for the right results
 
157
                        channel.period = (uint16)(((uint32)channel.refPeriod * (uint16)((1 << 11) + channel.vibValue)) >> 11);
 
158
                }
 
159
        }
 
160
 
 
161
        // portamento
 
162
        if (channel.portaDelta && !(--channel.portaCount)) {
 
163
                channel.portaCount = channel.portaSkip;
 
164
 
 
165
                bool resetPorta = true;
 
166
                const uint16 period = channel.refPeriod;
 
167
                uint16 portaVal = channel.portaValue;
 
168
 
 
169
                if (period > portaVal) {
 
170
                        portaVal = ((uint32)portaVal * (uint16)((1 << 8) + channel.portaDelta)) >> 8;
 
171
                        resetPorta = (period <= portaVal);
 
172
 
 
173
                } else if (period < portaVal) {
 
174
                        portaVal = ((uint32)portaVal * (uint16)((1 << 8) - channel.portaDelta)) >> 8;
 
175
                        resetPorta = (period >= portaVal);
 
176
                }
 
177
 
 
178
                if (resetPorta) {
 
179
                        channel.portaDelta = 0;
 
180
                        channel.portaValue = period & 0x7FF;
 
181
                } else
 
182
                        channel.period = channel.portaValue = portaVal & 0x7FF;
 
183
        }
 
184
 
 
185
        // envelope
 
186
        if (channel.envSkip && !channel.envCount--) {
 
187
                channel.envCount = channel.envSkip;
 
188
 
 
189
                const int8 endVol = channel.envEndVolume;
 
190
                int8 volume = channel.volume;
 
191
                bool resetEnv = true;
 
192
 
 
193
                if (endVol > volume) {
 
194
                        volume += channel.envDelta;
 
195
                        resetEnv = endVol <= volume;
 
196
                } else {
 
197
                        volume -= channel.envDelta;
 
198
                        resetEnv = volume <= 0 || endVol >= volume;
 
199
                }
 
200
 
 
201
                if (resetEnv) {
 
202
                        channel.envSkip = 0;
 
203
                        volume = endVol;
 
204
                }
 
205
                channel.volume = volume;
 
206
        }
 
207
 
 
208
        // Fade
 
209
        if (_playerCtx.fadeDelta && !(--_playerCtx.fadeCount)) {
 
210
                _playerCtx.fadeCount = _playerCtx.fadeSkip;
 
211
 
 
212
                _playerCtx.volume += _playerCtx.fadeDelta;
 
213
                if (_playerCtx.volume == _playerCtx.fadeEndVolume)
 
214
                        _playerCtx.fadeDelta = 0;
 
215
        }
 
216
 
 
217
        // Volume
 
218
        const uint8 finVol = _playerCtx.volume * channel.volume >> 6;
 
219
        Paula::setChannelVolume(channel.paulaChannel, finVol);
 
220
}
 
221
 
 
222
void Tfmx::macroRun(ChannelContext &channel) {
 
223
        bool deferWait = channel.deferWait;
 
224
        for (;;) {
 
225
                const byte *const macroPtr = (const byte *)(getMacroPtr(channel.macroOffset) + channel.macroStep);
 
226
                ++channel.macroStep;
 
227
 
 
228
                switch (macroPtr[0]) {
 
229
                case 0x00:      // Reset + DMA Off. Parameters: deferWait, addset, vol
 
230
                        clearEffects(channel);
 
231
                        // FT
 
232
                case 0x13:      // DMA Off. Parameters:  deferWait, addset, vol
 
233
                        // TODO: implement PArameters
 
234
                        Paula::disableChannel(channel.paulaChannel);
 
235
                        channel.deferWait = deferWait = (macroPtr[1] != 0);
 
236
                        if (deferWait) {
 
237
                                // if set, then we expect a DMA On in the same tick.
 
238
                                channel.period = 4;
 
239
                                //Paula::setChannelPeriod(channel.paulaChannel, channel.period);
 
240
                                Paula::setChannelSampleLen(channel.paulaChannel, 1);
 
241
                                // in this state we then need to allow some commands that normally
 
242
                                // would halt the macroprogamm to continue instead.
 
243
                                // those commands are: Wait, WaitDMA, AddPrevNote, AddNote, SetNote, <unknown Cmd>
 
244
                                // DMA On is affected aswell
 
245
                                // TODO remember time disabled, remember pending dmaoff?.
 
246
                        }
 
247
 
 
248
                        if (macroPtr[2] || macroPtr[3]) {
 
249
                                channel.volume = (macroPtr[2] ? 0 : channel.relVol * 3) + macroPtr[3];
 
250
                                Paula::setChannelVolume(channel.paulaChannel, channel.volume);
 
251
                        }
 
252
                        continue;
 
253
 
 
254
                case 0x01:      // DMA On
 
255
                        // TODO: Parameter macroPtr[1] - en-/disable effects
 
256
                        channel.dmaIntCount = 0;
 
257
                        if (deferWait) {
 
258
                                // TODO
 
259
                                // there is actually a small delay in the player, but I think that
 
260
                                // only allows to clear DMA-State on real Hardware
 
261
                        }
 
262
                        Paula::setChannelPeriod(channel.paulaChannel, channel.period);
 
263
                        Paula::enableChannel(channel.paulaChannel);
 
264
                        channel.deferWait = deferWait = false;
 
265
                        continue;
 
266
 
 
267
                case 0x02:      // Set Beginn. Parameters: SampleOffset(L)
 
268
                        channel.addBeginLength = 0;
 
269
                        channel.sampleStart = READ_BE_UINT32(macroPtr) & 0xFFFFFF;
 
270
                        Paula::setChannelSampleStart(channel.paulaChannel, getSamplePtr(channel.sampleStart));
 
271
                        continue;
 
272
 
 
273
                case 0x03:      // SetLength. Parameters: SampleLength(W)
 
274
                        channel.sampleLen = READ_BE_UINT16(&macroPtr[2]);
 
275
                        Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen);
 
276
                        continue;
 
277
 
 
278
                case 0x04:      // Wait. Parameters: Ticks to wait(W).
 
279
                        // TODO: some unkown Parameter? (macroPtr[1] & 1)
 
280
                        channel.macroWait = READ_BE_UINT16(&macroPtr[2]);
 
281
                        break;
 
282
 
 
283
                case 0x10:      // Loop Key Up. Parameters: Loopcount, MacroStep(W)
 
284
                        if (channel.keyUp)
 
285
                                continue;
 
286
                        // FT
 
287
                case 0x05:      // Loop. Parameters: Loopcount, MacroStep(W)
 
288
                        if (channel.macroLoopCount != 0) {
 
289
                                if (channel.macroLoopCount == 0xFF)
 
290
                                        channel.macroLoopCount = macroPtr[1];
 
291
                                channel.macroStep = READ_BE_UINT16(&macroPtr[2]);
 
292
                        }
 
293
                        --channel.macroLoopCount;
 
294
                        continue;
 
295
 
 
296
                case 0x06:      // Jump. Parameters: MacroIndex, MacroStep(W)
 
297
                        // channel.macroIndex = macroPtr[1] & (kMaxMacroOffsets - 1);
 
298
                        channel.macroOffset = _resource->macroOffset[macroPtr[1] & (kMaxMacroOffsets - 1)];
 
299
                        channel.macroStep = READ_BE_UINT16(&macroPtr[2]);
 
300
                        channel.macroLoopCount = 0xFF;
 
301
                        continue;
 
302
 
 
303
                case 0x07:      // Stop Macro
 
304
                        channel.macroRun = false;
 
305
                        --channel.macroStep;
 
306
                        return;
 
307
 
 
308
                case 0x08:      // AddNote. Parameters: Note, Finetune(W)
 
309
                        setNoteMacro(channel, channel.note + macroPtr[1], READ_BE_UINT16(&macroPtr[2]));
 
310
                        break;
 
311
 
 
312
                case 0x09:      // SetNote. Parameters: Note, Finetune(W)
 
313
                        setNoteMacro(channel, macroPtr[1], READ_BE_UINT16(&macroPtr[2]));
 
314
                        break;
 
315
 
 
316
                case 0x0A:      // Clear Effects
 
317
                        clearEffects(channel);
 
318
                        continue;
 
319
 
 
320
                case 0x0B:      // Portamento. Parameters: count, speed
 
321
                        channel.portaSkip = macroPtr[1];
 
322
                        channel.portaCount = 1;
 
323
                        // if porta is already running, then keep using old value
 
324
                        if (!channel.portaDelta)
 
325
                                channel.portaValue = channel.refPeriod;
 
326
                        channel.portaDelta = READ_BE_UINT16(&macroPtr[2]);
 
327
                        continue;
 
328
 
 
329
                case 0x0C:      // Vibrato. Parameters: Speed, intensity
 
330
                        channel.vibLength = macroPtr[1];
 
331
                        channel.vibCount = macroPtr[1] / 2;
 
332
                        channel.vibDelta = macroPtr[3];
 
333
                        // TODO: Perhaps a bug, vibValue could be left uninitialised
 
334
                        if (!channel.portaDelta) {
 
335
                                channel.period = channel.refPeriod;
 
336
                                channel.vibValue = 0;
 
337
                        }
 
338
                        continue;
 
339
 
 
340
                case 0x0D:      // Add Volume. Parameters: note, addNoteFlag, volume
 
341
                        if (macroPtr[2] == 0xFE)
 
342
                                setNoteMacro(channel, channel.note + macroPtr[1], 0);
 
343
                        channel.volume = channel.relVol * 3 + macroPtr[3];
 
344
                        continue;
 
345
 
 
346
                case 0x0E:      // Set Volume. Parameters: note, addNoteFlag, volume
 
347
                        if (macroPtr[2] == 0xFE)
 
348
                                setNoteMacro(channel, channel.note + macroPtr[1], 0);
 
349
                        channel.volume = macroPtr[3];
 
350
                        continue;
 
351
 
 
352
                case 0x0F:      // Envelope. Parameters: speed, count, endvol
 
353
                        channel.envDelta = macroPtr[1];
 
354
                        channel.envCount = channel.envSkip = macroPtr[2];
 
355
                        channel.envEndVolume = macroPtr[3];
 
356
                        continue;
 
357
 
 
358
                case 0x11:      // Add Beginn. Parameters: times, Offset(W)
 
359
                        channel.addBeginLength = channel.addBeginCount = macroPtr[1];
 
360
                        channel.addBeginDelta = (int16)READ_BE_UINT16(&macroPtr[2]);
 
361
                        channel.sampleStart += channel.addBeginDelta;
 
362
                        Paula::setChannelSampleStart(channel.paulaChannel, getSamplePtr(channel.sampleStart));
 
363
                        continue;
 
364
 
 
365
                case 0x12:      // Add Length. Parameters: added Length(W)
 
366
                        channel.sampleLen += (int16)READ_BE_UINT16(&macroPtr[2]);
 
367
                        Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen);
 
368
                        continue;
 
369
 
 
370
                case 0x14:      // Wait key up. Parameters: wait cycles
 
371
                        if (channel.keyUp || channel.macroLoopCount == 0) {
 
372
                                channel.macroLoopCount = 0xFF;
 
373
                                continue;
 
374
                        } else if (channel.macroLoopCount == 0xFF)
 
375
                                channel.macroLoopCount = macroPtr[3];
 
376
                        --channel.macroLoopCount;
 
377
                        --channel.macroStep;
 
378
                        return;
 
379
 
 
380
                case 0x15:      // Subroutine. Parameters: MacroIndex, Macrostep(W)
 
381
                        channel.macroReturnOffset = channel.macroOffset;
 
382
                        channel.macroReturnStep = channel.macroStep;
 
383
 
 
384
                        channel.macroOffset = _resource->macroOffset[macroPtr[1] & (kMaxMacroOffsets - 1)];
 
385
                        channel.macroStep = READ_BE_UINT16(&macroPtr[2]);
 
386
                        // TODO: MI does some weird stuff there. Figure out which varioables need to be set
 
387
                        continue;
 
388
 
 
389
                case 0x16:      // Return from Sub.
 
390
                        channel.macroOffset = channel.macroReturnOffset;
 
391
                        channel.macroStep = channel.macroReturnStep;
 
392
                        continue;
 
393
 
 
394
                case 0x17:      // Set Period. Parameters: Period(W)
 
395
                        channel.refPeriod = READ_BE_UINT16(&macroPtr[2]);
 
396
                        if (!channel.portaDelta) {
 
397
                                channel.period = channel.refPeriod;
 
398
                                //Paula::setChannelPeriod(channel.paulaChannel, channel.period);
 
399
                        }
 
400
                        continue;
 
401
 
 
402
                case 0x18: {    // Sampleloop. Parameters: Offset from Samplestart(W)
 
403
                        // TODO: MI loads 24 bit, but thats useless?
 
404
                        const uint16 temp = /* ((int8)macroPtr[1] << 16) | */ READ_BE_UINT16(&macroPtr[2]);
 
405
                        if (macroPtr[1] || (temp & 1))
 
406
                                warning("Tfmx: Problematic value for sampleloop: %06X", (macroPtr[1] << 16) | temp);
 
407
                        channel.sampleStart += temp & 0xFFFE;
 
408
                        channel.sampleLen -= (temp / 2) /* & 0x7FFF */;
 
409
                        Paula::setChannelSampleStart(channel.paulaChannel, getSamplePtr(channel.sampleStart));
 
410
                        Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen);
 
411
                        continue;
 
412
                }
 
413
                case 0x19:      // Set One-Shot Sample
 
414
                        channel.addBeginLength = 0;
 
415
                        channel.sampleStart = 0;
 
416
                        channel.sampleLen = 1;
 
417
                        Paula::setChannelSampleStart(channel.paulaChannel, getSamplePtr(0));
 
418
                        Paula::setChannelSampleLen(channel.paulaChannel, 1);
 
419
                        continue;
 
420
 
 
421
                case 0x1A:      // Wait on DMA. Parameters: Cycles-1(W) to wait
 
422
                        channel.dmaIntCount = READ_BE_UINT16(&macroPtr[2]) + 1;
 
423
                        channel.macroRun = false;
 
424
                        Paula::setChannelDmaCount(channel.paulaChannel);
 
425
                        break;
 
426
 
 
427
/*              case 0x1B:      // Random play. Parameters: macro/speed/mode
 
428
                        warnMacroUnimplemented(macroPtr, 0);
 
429
                        continue;*/
 
430
 
 
431
                case 0x1C:      // Branch on Note. Parameters: note/macrostep(W)
 
432
                        if (channel.note > macroPtr[1])
 
433
                                channel.macroStep = READ_BE_UINT16(&macroPtr[2]);
 
434
                        continue;
 
435
 
 
436
                case 0x1D:      // Branch on Volume. Parameters: volume/macrostep(W)
 
437
                        if (channel.volume > macroPtr[1])
 
438
                                channel.macroStep = READ_BE_UINT16(&macroPtr[2]);
 
439
                        continue;
 
440
 
 
441
/*              case 0x1E:      // Addvol+note. Parameters: note/CONST./volume
 
442
                        warnMacroUnimplemented(macroPtr, 0);
 
443
                        continue;*/
 
444
 
 
445
                case 0x1F:      // AddPrevNote. Parameters: Note, Finetune(W)
 
446
                        setNoteMacro(channel, channel.prevNote + macroPtr[1], READ_BE_UINT16(&macroPtr[2]));
 
447
                        break;
 
448
 
 
449
                case 0x20:      // Signal. Parameters: signalnumber, value(W)
 
450
                        if (_playerCtx.numSignals > macroPtr[1])
 
451
                                _playerCtx.signal[macroPtr[1]] = READ_BE_UINT16(&macroPtr[2]);
 
452
                        continue;
 
453
 
 
454
                case 0x21:      // Play macro. Parameters: macro, chan, detune
 
455
                        noteCommand(channel.note, macroPtr[1], (channel.relVol << 4) | macroPtr[2], macroPtr[3]);
 
456
                        continue;
 
457
 
 
458
                // 0x22 - 0x29 are used by Gem`X
 
459
                // 0x30 - 0x34 are used by Carribean Disaster
 
460
 
 
461
                default:
 
462
                        debug(3, "Tfmx: Macro %02X not supported", macroPtr[0]);
 
463
                }
 
464
                if (!deferWait)
 
465
                        return;
 
466
        }
 
467
}
 
468
 
 
469
void Tfmx::advancePatterns() {
 
470
startPatterns:
 
471
        int runningPatterns = 0;
 
472
 
 
473
        for (int i = 0; i < kNumChannels; ++i) {
 
474
                PatternContext &pattern = _patternCtx[i];
 
475
                const uint8 pattCmd = pattern.command;
 
476
                if (pattCmd < 0x90) {   // execute Patternstep
 
477
                        ++runningPatterns;
 
478
                        if (!pattern.wait) {
 
479
                                // issue all Steps for this tick
 
480
                                if (patternRun(pattern)) {
 
481
                                        // we load the next Trackstep Command and then process all Channels again
 
482
                                        if (trackRun(true))
 
483
                                                goto startPatterns;
 
484
                                        else
 
485
                                                break;
 
486
                                }
 
487
 
 
488
                        } else
 
489
                                --pattern.wait;
 
490
 
 
491
                } else if (pattCmd == 0xFE) {   // Stop voice in pattern.expose
 
492
                        pattern.command = 0xFF;
 
493
                        ChannelContext &channel = _channelCtx[pattern.expose & (kNumVoices - 1)];
 
494
                        if (!channel.sfxLocked) {
 
495
                                haltMacroProgramm(channel);
 
496
                                Paula::disableChannel(channel.paulaChannel);
 
497
                        }
 
498
                } // else this pattern-Channel is stopped
 
499
        }
 
500
        if (_playerCtx.stopWithLastPattern && !runningPatterns) {
 
501
                stopPaula();
 
502
        }
 
503
}
 
504
 
 
505
bool Tfmx::patternRun(PatternContext &pattern) {
 
506
        for (;;) {
 
507
                const byte *const patternPtr = (const byte *)(getPatternPtr(pattern.offset) + pattern.step);
 
508
                ++pattern.step;
 
509
                const byte pattCmd = patternPtr[0];
 
510
 
 
511
                if (pattCmd < 0xF0) { // Playnote
 
512
                        bool doWait = false;
 
513
                        byte noteCmd = pattCmd + pattern.expose;
 
514
                        byte param3  = patternPtr[3];
 
515
                        if (pattCmd < 0xC0) {   // Note
 
516
                                if (pattCmd >= 0x80) {  // Wait
 
517
                                        pattern.wait = param3;
 
518
                                        param3 = 0;
 
519
                                        doWait = true;
 
520
                                }
 
521
                                noteCmd &= 0x3F;
 
522
                        }       // else Portamento
 
523
                        noteCommand(noteCmd, patternPtr[1], patternPtr[2], param3);
 
524
                        if (doWait)
 
525
                                return false;
 
526
 
 
527
                } else {        // Patterncommand
 
528
                        switch (pattCmd & 0xF) {
 
529
                        case 0:         // End Pattern + Next Trackstep
 
530
                                pattern.command = 0xFF;
 
531
                                --pattern.step;
 
532
                                return true;
 
533
 
 
534
                        case 1:         // Loop Pattern. Parameters: Loopcount, PatternStep(W)
 
535
                                if (pattern.loopCount != 0) {
 
536
                                        if (pattern.loopCount == 0xFF)
 
537
                                                pattern.loopCount = patternPtr[1];
 
538
                                        pattern.step = READ_BE_UINT16(&patternPtr[2]);
 
539
                                }
 
540
                                --pattern.loopCount;
 
541
                                continue;
 
542
 
 
543
                        case 2:         // Jump. Parameters: PatternIndex, PatternStep(W)
 
544
                                pattern.offset = _resource->patternOffset[patternPtr[1] & (kMaxPatternOffsets - 1)];
 
545
                                pattern.step = READ_BE_UINT16(&patternPtr[2]);
 
546
                                continue;
 
547
 
 
548
                        case 3:         // Wait. Paramters: ticks to wait
 
549
                                pattern.wait = patternPtr[1];
 
550
                                return false;
 
551
 
 
552
                        case 14:        // Stop custompattern
 
553
                                // TODO apparently toggles on/off pattern channel 7
 
554
                                debug(3, "Tfmx: Encountered 'Stop custompattern' command");
 
555
                                // FT
 
556
                        case 4:         // Stop this pattern
 
557
                                pattern.command = 0xFF;
 
558
                                --pattern.step;
 
559
                                // TODO: try figuring out if this was the last Channel?
 
560
                                return false;
 
561
 
 
562
                        case 5:         // Key Up Signal. Paramters: channel
 
563
                                if (!_channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLocked)
 
564
                                        _channelCtx[patternPtr[2] & (kNumVoices - 1)].keyUp = true;
 
565
                                continue;
 
566
 
 
567
                        case 6:         // Vibrato. Parameters: length, channel, rate
 
568
                        case 7:         // Envelope. Parameters: rate, tempo | channel, endVol
 
569
                                noteCommand(pattCmd, patternPtr[1], patternPtr[2], patternPtr[3]);
 
570
                                continue;
 
571
 
 
572
                        case 8:         // Subroutine. Parameters: pattern, patternstep(W)
 
573
                                pattern.savedOffset = pattern.offset;
 
574
                                pattern.savedStep = pattern.step;
 
575
 
 
576
                                pattern.offset = _resource->patternOffset[patternPtr[1] & (kMaxPatternOffsets - 1)];
 
577
                                pattern.step = READ_BE_UINT16(&patternPtr[2]);
 
578
                                continue;
 
579
 
 
580
                        case 9:         // Return from Subroutine
 
581
                                pattern.offset = pattern.savedOffset;
 
582
                                pattern.step = pattern.savedStep;
 
583
                                continue;
 
584
 
 
585
                        case 10:        // fade. Parameters: tempo, endVol
 
586
                                initFadeCommand((uint8)patternPtr[1], (int8)patternPtr[3]);
 
587
                                continue;
 
588
 
 
589
                        case 11:        // play pattern. Parameters: patternCmd, channel, expose
 
590
                                initPattern(_patternCtx[patternPtr[2] & (kNumChannels - 1)], patternPtr[1], patternPtr[3], _resource->patternOffset[patternPtr[1] & (kMaxPatternOffsets - 1)]);
 
591
                                continue;
 
592
 
 
593
                        case 12:        // Lock. Parameters: lockFlag, channel, lockTime
 
594
                                _channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLocked = (patternPtr[1] != 0);
 
595
                                _channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLockTime = patternPtr[3];
 
596
                                continue;
 
597
 
 
598
                        case 13:        // Cue. Parameters: signalnumber, value(W)
 
599
                                if (_playerCtx.numSignals > patternPtr[1])
 
600
                                        _playerCtx.signal[patternPtr[1]] = READ_BE_UINT16(&patternPtr[2]);
 
601
                                continue;
 
602
 
 
603
                        case 15:        // NOP
 
604
                                continue;
 
605
                        }
 
606
                }
 
607
        }
 
608
}
 
609
 
 
610
bool Tfmx::trackRun(const bool incStep) {
 
611
        assert(_playerCtx.song >= 0);
 
612
        if (incStep) {
 
613
                // TODO Optionally disable looping
 
614
                if (_trackCtx.posInd == _trackCtx.stopInd)
 
615
                        _trackCtx.posInd = _trackCtx.startInd;
 
616
                else
 
617
                        ++_trackCtx.posInd;
 
618
        }
 
619
        for (;;) {
 
620
                const uint16 *const trackData = getTrackPtr(_trackCtx.posInd);
 
621
 
 
622
                if (trackData[0] != FROM_BE_16(0xEFFE)) {
 
623
                        // 8 commands for Patterns
 
624
                        for (int i = 0; i < 8; ++i) {
 
625
                                const uint8 *patCmd = (const uint8 *)&trackData[i];
 
626
                                // First byte is pattern number
 
627
                                const uint8 patNum = patCmd[0];
 
628
                                // if highest bit is set then keep previous pattern
 
629
                                if (patNum < 0x80) {
 
630
                                        initPattern(_patternCtx[i], patNum, patCmd[1], _resource->patternOffset[patNum]);
 
631
                                } else {
 
632
                                        _patternCtx[i].command = patNum;
 
633
                                        _patternCtx[i].expose = (int8)patCmd[1];
 
634
                                }
 
635
                        }
 
636
                        return true;
 
637
 
 
638
                } else {
 
639
                        // 16 byte Trackstep Command
 
640
                        switch (READ_BE_UINT16(&trackData[1])) {
 
641
                        case 0: // Stop Player. No Parameters
 
642
                                stopPaula();
 
643
                                return false;
 
644
 
 
645
                        case 1: // Branch/Loop section of tracksteps. Parameters: branch target, loopcount
 
646
                                if (_trackCtx.loopCount != 0) {
 
647
                                        if (_trackCtx.loopCount < 0)
 
648
                                                _trackCtx.loopCount = READ_BE_UINT16(&trackData[3]);
 
649
                                        _trackCtx.posInd    = READ_BE_UINT16(&trackData[2]);
 
650
                                        continue;
 
651
                                }
 
652
                                --_trackCtx.loopCount;
 
653
                                break;
 
654
 
 
655
                        case 2: { // Set Tempo. Parameters: tempo, divisor
 
656
                                _playerCtx.patternCount = _playerCtx.patternSkip = READ_BE_UINT16(&trackData[2]); // tempo
 
657
                                const uint16 temp = READ_BE_UINT16(&trackData[3]); // divisor
 
658
 
 
659
                                if (!(temp & 0x8000) && (temp & 0x1FF))
 
660
                                        setInterruptFreqUnscaled(temp & 0x1FF);
 
661
                                break;
 
662
                        }
 
663
                        case 4: // Fade. Parameters: tempo, endVol
 
664
                                // load the LSB of the 16bit words
 
665
                                initFadeCommand(((const uint8 *)&trackData[2])[1], ((const int8 *)&trackData[3])[1]);
 
666
                                break;
 
667
 
 
668
                        case 3: // Unknown, stops player aswell
 
669
                        default:
 
670
                                debug(3, "Tfmx: Unknown Trackstep Command: %02X", READ_BE_UINT16(&trackData[1]));
 
671
                                // MI-Player handles this by stopping the player, we just continue
 
672
                        }
 
673
                }
 
674
 
 
675
                if (_trackCtx.posInd == _trackCtx.stopInd) {
 
676
                        warning("Tfmx: Reached invalid Song-Position");
 
677
                        return false;
 
678
                }
 
679
                ++_trackCtx.posInd;
 
680
        }
 
681
}
 
682
 
 
683
void Tfmx::noteCommand(const uint8 note, const uint8 param1, const uint8 param2, const uint8 param3) {
 
684
        ChannelContext &channel = _channelCtx[param2 & (kNumVoices - 1)];
 
685
 
 
686
        if (note == 0xFC) {     // Lock command
 
687
                channel.sfxLocked = (param1 != 0);
 
688
                channel.sfxLockTime = param3; // only 1 byte read!
 
689
 
 
690
        } else if (channel.sfxLocked) { // Channel still locked, do nothing
 
691
 
 
692
        } else if (note < 0xC0) {       // Play Note - Parameters: note, macro, relVol | channel, finetune
 
693
 
 
694
                channel.prevNote = channel.note;
 
695
                channel.note = note;
 
696
                // channel.macroIndex = param1 & (kMaxMacroOffsets - 1);
 
697
                channel.macroOffset = _resource->macroOffset[param1 & (kMaxMacroOffsets - 1)];
 
698
                channel.relVol = param2 >> 4;
 
699
                channel.fineTune = (int8)param3;
 
700
 
 
701
                // TODO: the point where the channel gets initialised varies with the games, needs more research.
 
702
                initMacroProgramm(channel);
 
703
                channel.keyUp = false; // key down = playing a Note
 
704
 
 
705
        } else if (note < 0xF0) {       // Portamento - Parameters: note, tempo, channel, rate
 
706
                channel.portaSkip = param1;
 
707
                channel.portaCount = 1;
 
708
                if (!channel.portaDelta)
 
709
                        channel.portaValue = channel.refPeriod;
 
710
                channel.portaDelta = param3;
 
711
 
 
712
                channel.note = note & 0x3F;
 
713
                channel.refPeriod = noteIntervalls[channel.note];
 
714
 
 
715
        } else switch (note) {  // Command
 
716
 
 
717
                case 0xF5:      // Key Up Signal
 
718
                        channel.keyUp = true;
 
719
                        break;
 
720
 
 
721
                case 0xF6:      // Vibratio - Parameters: length, channel, rate
 
722
                        channel.vibLength = param1 & 0xFE;
 
723
                        channel.vibCount = param1 / 2;
 
724
                        channel.vibDelta = param3;
 
725
                        channel.vibValue = 0;
 
726
                        break;
 
727
 
 
728
                case 0xF7:      // Envelope - Parameters: rate, tempo | channel, endVol
 
729
                        channel.envDelta = param1;
 
730
                        channel.envCount = channel.envSkip = (param2 >> 4) + 1;
 
731
                        channel.envEndVolume = param3;
 
732
                        break;
 
733
        }
 
734
}
 
735
 
 
736
void Tfmx::initMacroProgramm(ChannelContext &channel) {
 
737
        channel.macroStep = 0;
 
738
        channel.macroWait = 0;
 
739
        channel.macroRun = true;
 
740
        channel.macroSfxRun = 0;
 
741
        channel.macroLoopCount = 0xFF;
 
742
        channel.dmaIntCount = 0;
 
743
        channel.deferWait = false;
 
744
 
 
745
        channel.macroReturnOffset = 0;
 
746
        channel.macroReturnStep = 0;
 
747
}
 
748
 
 
749
void Tfmx::clearEffects(ChannelContext &channel) {
 
750
        channel.addBeginLength = 0;
 
751
        channel.envSkip = 0;
 
752
        channel.vibLength = 0;
 
753
        channel.portaDelta = 0;
 
754
}
 
755
 
 
756
void Tfmx::haltMacroProgramm(ChannelContext &channel) {
 
757
        channel.macroRun = false;
 
758
        channel.dmaIntCount = 0;
 
759
}
 
760
 
 
761
void Tfmx::unlockMacroChannel(ChannelContext &channel) {
 
762
        channel.customMacro = 0;
 
763
        channel.customMacroIndex = 0;
 
764
        channel.customMacroPrio = 0;
 
765
        channel.sfxLocked = false;
 
766
        channel.sfxLockTime = -1;
 
767
}
 
768
 
 
769
void Tfmx::initPattern(PatternContext &pattern, uint8 cmd, int8 expose, uint32 offset) {
 
770
        pattern.command = cmd;
 
771
        pattern.offset = offset;
 
772
        pattern.expose = expose;
 
773
        pattern.step = 0;
 
774
        pattern.wait = 0;
 
775
        pattern.loopCount = 0xFF;
 
776
 
 
777
        pattern.savedOffset = 0;
 
778
        pattern.savedStep = 0;
 
779
}
 
780
 
 
781
void Tfmx::stopSongImpl(bool stopAudio) {
 
782
         _playerCtx.song = -1;
 
783
        for (int i = 0; i < kNumChannels; ++i) {
 
784
                _patternCtx[i].command = 0xFF;
 
785
                _patternCtx[i].expose = 0;
 
786
        }
 
787
        if (stopAudio) {
 
788
                stopPaula();
 
789
                for (int i = 0; i < kNumVoices; ++i) {
 
790
                        clearEffects(_channelCtx[i]);
 
791
                        unlockMacroChannel(_channelCtx[i]);
 
792
                        haltMacroProgramm(_channelCtx[i]);
 
793
                        _channelCtx[i].note = 0;
 
794
                        _channelCtx[i].volume = 0;
 
795
                        _channelCtx[i].macroSfxRun = -1;
 
796
                        _channelCtx[i].vibValue = 0;
 
797
 
 
798
                        _channelCtx[i].sampleStart = 0;
 
799
                        _channelCtx[i].sampleLen = 2;
 
800
                        _channelCtx[i].refPeriod = 4;
 
801
                        _channelCtx[i].period = 4;
 
802
                        Paula::disableChannel(i);
 
803
                }
 
804
        }
 
805
}
 
806
 
 
807
void Tfmx::setNoteMacro(ChannelContext &channel, uint note, int fineTune) {
 
808
        const uint16 noteInt = noteIntervalls[note & 0x3F];
 
809
        const uint16 finetune = (uint16)(fineTune + channel.fineTune + (1 << 8));
 
810
        channel.refPeriod = ((uint32)noteInt * finetune >> 8);
 
811
        if (!channel.portaDelta)
 
812
                channel.period = channel.refPeriod;
 
813
}
 
814
 
 
815
void Tfmx::initFadeCommand(const uint8 fadeTempo, const int8 endVol) {
 
816
        _playerCtx.fadeCount = _playerCtx.fadeSkip = fadeTempo;
 
817
        _playerCtx.fadeEndVolume = endVol;
 
818
 
 
819
        if (fadeTempo) {
 
820
                const int diff = _playerCtx.fadeEndVolume - _playerCtx.volume;
 
821
                _playerCtx.fadeDelta = (diff != 0) ? ((diff > 0) ? 1 : -1) : 0;
 
822
        } else {
 
823
                _playerCtx.volume = endVol;
 
824
                _playerCtx.fadeDelta = 0;
 
825
        }
 
826
}
 
827
 
 
828
void Tfmx::setModuleData(Tfmx &otherPlayer) {
 
829
        setModuleData(otherPlayer._resource, otherPlayer._resourceSample.sampleData, otherPlayer._resourceSample.sampleLen, false);
 
830
}
 
831
 
 
832
bool Tfmx::load(Common::SeekableReadStream &musicData, Common::SeekableReadStream &sampleData, bool autoDelete) {
 
833
        const MdatResource *mdat = loadMdatFile(musicData);
 
834
        if (mdat) {
 
835
                uint32 sampleLen = 0;
 
836
                const int8 *sampleDat = loadSampleFile(sampleLen, sampleData);
 
837
                if (sampleDat) {
 
838
                        setModuleData(mdat, sampleDat, sampleLen, autoDelete);
 
839
                        return true;
 
840
                }
 
841
                delete[] mdat->mdatAlloc;
 
842
                delete mdat;
 
843
        }
 
844
        return false;
 
845
}
 
846
 
 
847
void Tfmx::freeResourceDataImpl() {
 
848
        if (_deleteResource) {
 
849
                if (_resource) {
 
850
                        delete[] _resource->mdatAlloc;
 
851
                        delete _resource;
 
852
                }
 
853
                delete[] _resourceSample.sampleData;
 
854
        }
 
855
        _resource = 0;
 
856
        _resourceSample.sampleData = 0;
 
857
        _resourceSample.sampleLen = 0;
 
858
        _deleteResource = false;
 
859
}
 
860
 
 
861
void Tfmx::setModuleData(const MdatResource *resource, const int8 *sampleData, uint32 sampleLen, bool autoDelete) {
 
862
        Common::StackLock lock(_mutex);
 
863
        stopSongImpl(true);
 
864
        freeResourceDataImpl();
 
865
        _resource = resource;
 
866
        _resourceSample.sampleData = sampleData;
 
867
        _resourceSample.sampleLen = sampleData ? sampleLen : 0;
 
868
        _deleteResource = autoDelete;
 
869
}
 
870
 
 
871
const int8 *Tfmx::loadSampleFile(uint32 &sampleLen, Common::SeekableReadStream &sampleStream) {
 
872
        sampleLen = 0;
 
873
 
 
874
        const int32 sampleSize = sampleStream.size();
 
875
        if (sampleSize < 4) {
 
876
                warning("Tfmx: Cant load Samplefile");
 
877
                return false;
 
878
        }
 
879
 
 
880
        int8 *sampleAlloc = new int8[sampleSize];
 
881
        if (!sampleAlloc) {
 
882
                warning("Tfmx: Could not allocate Memory: %dKB", sampleSize / 1024);
 
883
                return 0;
 
884
        }
 
885
 
 
886
        if (sampleStream.read(sampleAlloc, sampleSize) == (uint32)sampleSize) {
 
887
                sampleAlloc[0] = sampleAlloc[1] = sampleAlloc[2] = sampleAlloc[3] = 0;
 
888
                sampleLen = sampleSize;
 
889
        } else {
 
890
                delete[] sampleAlloc;
 
891
                warning("Tfmx: Encountered IO-Error");
 
892
                return 0;
 
893
        }
 
894
        return sampleAlloc;
 
895
}
 
896
 
 
897
const Tfmx::MdatResource *Tfmx::loadMdatFile(Common::SeekableReadStream &musicData) {
 
898
        bool hasHeader = false;
 
899
        const int32 mdatSize = musicData.size();
 
900
        if (mdatSize >= 0x200) {
 
901
                byte buf[16] = { 0 };
 
902
                // 0x0000: 10 Bytes Header "TFMX-SONG "
 
903
                musicData.read(buf, 10);
 
904
                hasHeader = memcmp(buf, "TFMX-SONG ", 10) == 0;
 
905
        }
 
906
 
 
907
        if (!hasHeader) {
 
908
                warning("Tfmx: File is not a Tfmx Module");
 
909
                return 0;
 
910
        }
 
911
 
 
912
        MdatResource *resource = new MdatResource;
 
913
 
 
914
        resource->mdatAlloc = 0;
 
915
        resource->mdatData = 0;
 
916
        resource->mdatLen = 0;
 
917
 
 
918
        // 0x000A: int16 flags
 
919
        resource->headerFlags = musicData.readUint16BE();
 
920
        // 0x000C: int32 ?
 
921
        // 0x0010: 6*40 Textfield
 
922
        musicData.skip(4 + 6 * 40);
 
923
 
 
924
        /* 0x0100: Songstart x 32*/
 
925
        for (int i = 0; i < kNumSubsongs; ++i)
 
926
                resource->subsong[i].songstart = musicData.readUint16BE();
 
927
        /* 0x0140: Songend x 32*/
 
928
        for (int i = 0; i < kNumSubsongs; ++i)
 
929
                resource->subsong[i].songend = musicData.readUint16BE();
 
930
        /* 0x0180: Tempo x 32*/
 
931
        for (int i = 0; i < kNumSubsongs; ++i)
 
932
                resource->subsong[i].tempo  = musicData.readUint16BE();
 
933
 
 
934
        /* 0x01c0: unused ? */
 
935
        musicData.skip(16);
 
936
 
 
937
        /* 0x01d0: trackstep, pattern data p, macro data p */
 
938
        const uint32 offTrackstep = musicData.readUint32BE();
 
939
        uint32 offPatternP, offMacroP;
 
940
 
 
941
        // This is how MI`s TFMX-Player tests for unpacked Modules.
 
942
        if (offTrackstep == 0) { // unpacked File
 
943
                resource->trackstepOffset = 0x600 + 0x200;
 
944
                offPatternP             = 0x200 + 0x200;
 
945
                offMacroP               = 0x400 + 0x200;
 
946
        } else { // packed File
 
947
                resource->trackstepOffset = offTrackstep;
 
948
                offPatternP = musicData.readUint32BE();
 
949
                offMacroP = musicData.readUint32BE();
 
950
        }
 
951
 
 
952
        // End of basic header, check if everything worked ok
 
953
        if (musicData.err()) {
 
954
                warning("Tfmx: Encountered IO-Error");
 
955
                delete resource;
 
956
                return 0;
 
957
        }
 
958
 
 
959
        // TODO: if a File is packed it could have for Ex only 2 Patterns/Macros
 
960
        // the following loops could then read beyond EOF.
 
961
        // To correctly handle this it would be necessary to sort the pointers and
 
962
        // figure out the number of Macros/Patterns
 
963
        // We could also analyze pointers if they are correct offsets,
 
964
        // so that accesses can be unchecked later
 
965
 
 
966
        // Read in pattern starting offsets
 
967
        musicData.seek(offPatternP);
 
968
        for (int i = 0; i < kMaxPatternOffsets; ++i)
 
969
                resource->patternOffset[i] = musicData.readUint32BE();
 
970
 
 
971
        // use last PatternOffset (stored at 0x5FC in mdat) if unpacked File
 
972
        // or fixed offset 0x200 if packed
 
973
        resource->sfxTableOffset = offTrackstep ? 0x200 : resource->patternOffset[127];
 
974
 
 
975
        // Read in macro starting offsets
 
976
        musicData.seek(offMacroP);
 
977
        for (int i = 0; i < kMaxMacroOffsets; ++i)
 
978
                resource->macroOffset[i] = musicData.readUint32BE();
 
979
 
 
980
        // Read in mdat-file
 
981
        // TODO: we can skip everything thats already stored in the resource-structure.
 
982
        const int32 mdatOffset = offTrackstep ? 0x200 : 0x600;  // 0x200 is very conservative
 
983
        const uint32 allocSize = (uint32)mdatSize - mdatOffset;
 
984
 
 
985
        byte *mdatAlloc = new byte[allocSize];
 
986
        if (!mdatAlloc) {
 
987
                warning("Tfmx: Could not allocate Memory: %dKB", allocSize / 1024);
 
988
                delete resource;
 
989
                return 0;
 
990
        }
 
991
        musicData.seek(mdatOffset);
 
992
        if (musicData.read(mdatAlloc, allocSize) == allocSize) {
 
993
                resource->mdatAlloc = mdatAlloc;
 
994
                resource->mdatData = mdatAlloc - mdatOffset;
 
995
                resource->mdatLen = mdatSize;
 
996
        } else {
 
997
                delete[] mdatAlloc;
 
998
                warning("Tfmx: Encountered IO-Error");
 
999
                delete resource;
 
1000
                return 0;
 
1001
        }
 
1002
 
 
1003
        return resource;
 
1004
}
 
1005
 
 
1006
void Tfmx::doMacro(int note, int macro, int relVol, int finetune, int channelNo) {
 
1007
        assert(0 <= macro && macro < kMaxMacroOffsets);
 
1008
        assert(0 <= note && note < 0xC0);
 
1009
        Common::StackLock lock(_mutex);
 
1010
 
 
1011
        if (!hasResources())
 
1012
                return;
 
1013
        channelNo &= (kNumVoices - 1);
 
1014
        ChannelContext &channel = _channelCtx[channelNo];
 
1015
        unlockMacroChannel(channel);
 
1016
 
 
1017
        noteCommand((uint8)note, (uint8)macro, (uint8)((relVol << 4) | channelNo), (uint8)finetune);
 
1018
        startPaula();
 
1019
}
 
1020
 
 
1021
void Tfmx::stopMacroEffect(int channel) {
 
1022
        assert(0 <= channel && channel < kNumVoices);
 
1023
        Common::StackLock lock(_mutex);
 
1024
        unlockMacroChannel(_channelCtx[channel]);
 
1025
        haltMacroProgramm(_channelCtx[channel]);
 
1026
        Paula::disableChannel(_channelCtx[channel].paulaChannel);
 
1027
}
 
1028
 
 
1029
void Tfmx::doSong(int songPos, bool stopAudio) {
 
1030
        assert(0 <= songPos && songPos < kNumSubsongs);
 
1031
        Common::StackLock lock(_mutex);
 
1032
 
 
1033
        stopSongImpl(stopAudio);
 
1034
 
 
1035
        if (!hasResources())
 
1036
                return;
 
1037
 
 
1038
        _trackCtx.loopCount = -1;
 
1039
        _trackCtx.startInd = _trackCtx.posInd = _resource->subsong[songPos].songstart;
 
1040
        _trackCtx.stopInd = _resource->subsong[songPos].songend;
 
1041
        _playerCtx.song = (int8)songPos;
 
1042
 
 
1043
        const bool palFlag = (_resource->headerFlags & 2) != 0;
 
1044
        const uint16 tempo = _resource->subsong[songPos].tempo;
 
1045
        uint16 ciaIntervall;
 
1046
        if (tempo >= 0x10) {
 
1047
                ciaIntervall = (uint16)(kCiaBaseInterval / tempo);
 
1048
                _playerCtx.patternSkip = 0;
 
1049
        } else {
 
1050
                ciaIntervall = palFlag ? (uint16)kPalDefaultCiaVal : (uint16)kNtscDefaultCiaVal;
 
1051
                _playerCtx.patternSkip = tempo;
 
1052
        }
 
1053
        setInterruptFreqUnscaled(ciaIntervall);
 
1054
        Paula::setAudioFilter(true);
 
1055
 
 
1056
        _playerCtx.patternCount = 0;
 
1057
        if (trackRun())
 
1058
                startPaula();
 
1059
}
 
1060
 
 
1061
int Tfmx::doSfx(uint16 sfxIndex, bool unlockChannel) {
 
1062
        assert(sfxIndex < 128);
 
1063
        Common::StackLock lock(_mutex);
 
1064
 
 
1065
        if (!hasResources())
 
1066
                return -1;
 
1067
        const byte *sfxEntry = getSfxPtr(sfxIndex);
 
1068
        if (sfxEntry[0] == 0xFB) {
 
1069
                warning("Tfmx: custom patterns are not supported");
 
1070
                // custompattern
 
1071
                /* const uint8 patCmd = sfxEntry[2];
 
1072
                const int8 patExp = (int8)sfxEntry[3]; */
 
1073
        } else {
 
1074
                // custommacro
 
1075
                const byte channelNo = ((_playerCtx.song >= 0) ? sfxEntry[2] : sfxEntry[4]) & (kNumVoices - 1);
 
1076
                const byte priority = sfxEntry[5] & 0x7F;
 
1077
 
 
1078
                ChannelContext &channel = _channelCtx[channelNo];
 
1079
                if (unlockChannel)
 
1080
                        unlockMacroChannel(channel);
 
1081
 
 
1082
                const int16 sfxLocktime = channel.sfxLockTime;
 
1083
                if (priority >= channel.customMacroPrio || sfxLocktime < 0) {
 
1084
                        if (sfxIndex != channel.customMacroIndex || sfxLocktime < 0 || (sfxEntry[5] < 0x80)) {
 
1085
                                channel.customMacro = READ_UINT32(sfxEntry); // intentionally not "endian-correct"
 
1086
                                channel.customMacroPrio = priority;
 
1087
                                channel.customMacroIndex = (uint8)sfxIndex;
 
1088
                                debug(3, "Tfmx: running Macro %08X on channel %i - priority: %02X", TO_BE_32(channel.customMacro), channelNo, priority);
 
1089
                                return channelNo;
 
1090
                        }
 
1091
                }
 
1092
        }
 
1093
        return -1;
 
1094
}
 
1095
 
 
1096
}       // End of namespace Audio
 
1097
 
 
1098
// some debugging functions
 
1099
namespace {
 
1100
#if !defined(NDEBUG) && 0
 
1101
void displayMacroStep(const void *const vptr) {
 
1102
        const char *tableMacros[] = {
 
1103
                "DMAoff+Resetxx/xx/xx flag/addset/vol   ",
 
1104
                "DMAon (start sample at selected begin) ",
 
1105
                "SetBegin    xxxxxx   sample-startadress",
 
1106
                "SetLen      ..xxxx   sample-length     ",
 
1107
                "Wait        ..xxxx   count (VBI''s)    ",
 
1108
                "Loop        xx/xxxx  count/step        ",
 
1109
                "Cont        xx/xxxx  macro-number/step ",
 
1110
                "-------------STOP----------------------",
 
1111
                "AddNote     xx/xxxx  note/detune       ",
 
1112
                "SetNote     xx/xxxx  note/detune       ",
 
1113
                "Reset   Vibrato-Portamento-Envelope    ",
 
1114
                "Portamento  xx/../xx count/speed       ",
 
1115
                "Vibrato     xx/../xx speed/intensity   ",
 
1116
                "AddVolume   ....xx   volume 00-3F      ",
 
1117
                "SetVolume   ....xx   volume 00-3F      ",
 
1118
                "Envelope    xx/xx/xx speed/count/endvol",
 
1119
                "Loop key up xx/xxxx  count/step        ",
 
1120
                "AddBegin    xx/xxxx  count/add to start",
 
1121
                "AddLen      ..xxxx   add to sample-len ",
 
1122
                "DMAoff stop sample but no clear        ",
 
1123
                "Wait key up ....xx   count (VBI''s)    ",
 
1124
                "Go submacro xx/xxxx  macro-number/step ",
 
1125
                "--------Return to old macro------------",
 
1126
                "Setperiod   ..xxxx   DMA period        ",
 
1127
                "Sampleloop  ..xxxx   relative adress   ",
 
1128
                "-------Set one shot sample-------------",
 
1129
                "Wait on DMA ..xxxx   count (Wavecycles)",
 
1130
                "Random play xx/xx/xx macro/speed/mode  ",
 
1131
                "Splitkey    xx/xxxx  key/macrostep     ",
 
1132
                "Splitvolume xx/xxxx  volume/macrostep  ",
 
1133
                "Addvol+note xx/fe/xx note/CONST./volume",
 
1134
                "SetPrevNote xx/xxxx  note/detune       ",
 
1135
                "Signal      xx/xxxx  signalnumber/value",
 
1136
                "Play macro  xx/.x/xx macro/chan/detune ",
 
1137
                "SID setbeg  xxxxxx   sample-startadress",
 
1138
                "SID setlen  xx/xxxx  buflen/sourcelen  ",
 
1139
                "SID op3 ofs xxxxxx   offset            ",
 
1140
                "SID op3 frq xx/xxxx  speed/amplitude   ",
 
1141
                "SID op2 ofs xxxxxx   offset            ",
 
1142
                "SID op2 frq xx/xxxx  speed/amplitude   ",
 
1143
                "SID op1     xx/xx/xx speed/amplitude/TC",
 
1144
                "SID stop    xx....   flag (1=clear all)"
 
1145
        };
 
1146
 
 
1147
        const byte *const macroData = (const byte *const)vptr;
 
1148
        if (macroData[0] < ARRAYSIZE(tableMacros))
 
1149
                debug("%s %02X%02X%02X", tableMacros[macroData[0]], macroData[1], macroData[2], macroData[3]);
 
1150
        else
 
1151
                debug("Unkown Macro #%02X %02X%02X%02X", macroData[0], macroData[1], macroData[2], macroData[3]);
 
1152
}
 
1153
 
 
1154
void displayPatternstep(const void *const vptr) {
 
1155
        const char *tablePatterns[] = {
 
1156
                "End --Next track  step--",
 
1157
                "Loop[count     / step.w]",
 
1158
                "Cont[patternno./ step.w]",
 
1159
                "Wait[count 00-FF--------",
 
1160
                "Stop--Stop this pattern-",
 
1161
                "Kup^-Set key up/channel]",
 
1162
                "Vibr[speed     / rate.b]",
 
1163
                "Enve[speed /endvolume.b]",
 
1164
                "GsPt[patternno./ step.w]",
 
1165
                "RoPt-Return old pattern-",
 
1166
                "Fade[speed /endvolume.b]",
 
1167
                "PPat[patt./track+transp]",
 
1168
                "Lock---------ch./time.b]",
 
1169
                "Cue [number.b/  value.w]",
 
1170
                "Stop-Stop custompattern-",
 
1171
                "NOP!-no operation-------"
 
1172
        };
 
1173
 
 
1174
        const byte *const patData = (const byte *const)vptr;
 
1175
        const byte command = patData[0];
 
1176
        if (command < 0xF0) { // Playnote
 
1177
                const byte flags = command >> 6; // 0-1 means note+detune, 2 means wait, 3 means portamento?
 
1178
                const char *flagsSt[] = { "Note ", "Note ", "Wait ", "Porta" };
 
1179
                debug("%s %02X%02X%02X%02X", flagsSt[flags], patData[0], patData[1], patData[2], patData[3]);
 
1180
        } else
 
1181
                debug("%s %02X%02X%02X",tablePatterns[command & 0xF], patData[1], patData[2], patData[3]);
 
1182
}
 
1183
#else
 
1184
void displayMacroStep(const void *const vptr, int chan, int index) {}
 
1185
void displayPatternstep(const void *const vptr) {}
 
1186
#endif
 
1187
}       // End of namespace
 
1188
 
 
1189
#endif // #if defined(SOUND_MODS_TFMX_H)