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 */
22
23 #include "sci/sci.h"
24
25 #include "common/file.h"
26 #include "common/system.h"
27 #include "common/textconsole.h"
28
29 #include "audio/softsynth/fmtowns_pc98/towns_audio.h"
30
31 #include "sci/resource/resource.h"
32 #include "sci/sound/drivers/mididriver.h"
33
34 namespace Sci {
35
36 class MidiDriver_FMTowns;
37
38 class TownsChannel {
39 public:
40 TownsChannel(MidiDriver_FMTowns *driver, uint8 id);
~TownsChannel()41 ~TownsChannel() {}
42
43 void noteOff();
44 void noteOn(uint8 note, uint8 velo);
45 void pitchBend(int16 val);
46 void updateVolume();
47 void updateDuration();
48
49 uint8 _assign;
50 uint8 _note;
51 uint8 _sustain;
52 uint16 _duration;
53
54 private:
55 uint8 _id;
56 uint8 _velo;
57 uint8 _program;
58
59 MidiDriver_FMTowns *_drv;
60 };
61
62 class TownsMidiPart {
63 friend class MidiDriver_FMTowns;
64 public:
65 TownsMidiPart(MidiDriver_FMTowns *driver, uint8 id);
~TownsMidiPart()66 ~TownsMidiPart() {}
67
68 void noteOff(uint8 note);
69 void noteOn(uint8 note, uint8 velo);
70 void controlChangeVolume(uint8 vol);
71 void controlChangeSustain(uint8 sus);
72 void controlChangePolyphony(uint8 numChan);
73 void controlChangeAllNotesOff();
74 void programChange(uint8 prg);
75 void pitchBend(int16 val);
76
77 void addChannels(int num);
78 void dropChannels(int num);
79
80 uint8 currentProgram() const;
81
82 private:
83 int allocateChannel();
84
85 uint8 _id;
86 uint8 _program;
87 uint8 _volume;
88 uint8 _sustain;
89 uint8 _chanMissing;
90 int16 _pitchBend;
91 uint8 _outChan;
92
93 MidiDriver_FMTowns *_drv;
94 };
95
96 class MidiDriver_FMTowns : public MidiDriver, public TownsAudioInterfacePluginDriver {
97 friend class TownsChannel;
98 friend class TownsMidiPart;
99 public:
100 MidiDriver_FMTowns(Audio::Mixer *mixer, SciVersion version);
101 ~MidiDriver_FMTowns() override;
102
103 int open() override;
104 void loadInstruments(const SciSpan<const uint8> &data);
isOpen() const105 bool isOpen() const override { return _isOpen; }
106 void close() override;
107
108 void send(uint32 b) override;
109
110 uint32 property(int prop, uint32 param) override;
111 void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) override;
112
113 void setSoundOn(bool toggle);
114
115 uint32 getBaseTempo() override;
allocateChannel()116 MidiChannel *allocateChannel() override { return 0; }
getPercussionChannel()117 MidiChannel *getPercussionChannel() override { return 0; }
118
119 void timerCallback(int timerId) override;
120
121 private:
122 int getChannelVolume(uint8 midiPart);
123 void addMissingChannels();
124
125 void updateParser();
126 void updateChannels();
127
128 Common::TimerManager::TimerProc _timerProc;
129 void *_timerProcPara;
130
131 TownsMidiPart **_parts;
132 TownsChannel **_out;
133
134 uint8 _masterVolume;
135
136 bool _soundOn;
137
138 bool _isOpen;
139 bool _ready;
140
141 const uint16 _baseTempo;
142 SciVersion _version;
143
144 TownsAudioInterface *_intf;
145 };
146
147 class MidiPlayer_FMTowns : public MidiPlayer {
148 public:
149 MidiPlayer_FMTowns(SciVersion version);
150 ~MidiPlayer_FMTowns() override;
151
152 int open(ResourceManager *resMan) override;
153
154 bool hasRhythmChannel() const override;
155 byte getPlayId() const override;
156 int getPolyphony() const override;
157 void playSwitch(bool play) override;
158
159 private:
160 MidiDriver_FMTowns *_townsDriver;
161 };
162
TownsChannel(MidiDriver_FMTowns * driver,uint8 id)163 TownsChannel::TownsChannel(MidiDriver_FMTowns *driver, uint8 id) : _drv(driver), _id(id), _assign(0xff), _note(0xff), _velo(0), _sustain(0), _duration(0), _program(0xff) {
164 }
165
noteOn(uint8 note,uint8 velo)166 void TownsChannel::noteOn(uint8 note, uint8 velo) {
167 _duration = 0;
168
169 if (_drv->_version != SCI_VERSION_1_EARLY) {
170 if (_program != _drv->_parts[_assign]->currentProgram() && _drv->_soundOn) {
171 _program = _drv->_parts[_assign]->currentProgram();
172 _drv->_intf->callback(4, _id, _program);
173 }
174 }
175
176 _note = note;
177 _velo = velo;
178 _drv->_intf->callback(1, _id, _note, _velo);
179 }
180
noteOff()181 void TownsChannel::noteOff() {
182 if (_sustain)
183 return;
184
185 _drv->_intf->callback(2, _id);
186 _note = 0xff;
187 _duration = 0;
188 }
189
pitchBend(int16 val)190 void TownsChannel::pitchBend(int16 val) {
191 _drv->_intf->callback(7, _id, val);
192 }
193
updateVolume()194 void TownsChannel::updateVolume() {
195 if (_assign > 15 && _drv->_version != SCI_VERSION_1_EARLY)
196 return;
197 _drv->_intf->callback(8, _id, _drv->getChannelVolume((_drv->_version == SCI_VERSION_1_EARLY) ? 0 : _assign));
198 }
199
updateDuration()200 void TownsChannel::updateDuration() {
201 if (_note != 0xff)
202 _duration++;
203 }
204
TownsMidiPart(MidiDriver_FMTowns * driver,uint8 id)205 TownsMidiPart::TownsMidiPart(MidiDriver_FMTowns *driver, uint8 id) : _drv(driver), _id(id), _program(0), _volume(0x3f), _sustain(0), _chanMissing(0), _pitchBend(0x2000), _outChan(0) {
206 }
207
noteOff(uint8 note)208 void TownsMidiPart::noteOff(uint8 note) {
209 for (int i = 0; i < 6; i++) {
210 if ((_drv->_out[i]->_assign != _id && _drv->_version != SCI_VERSION_1_EARLY) || _drv->_out[i]->_note != note)
211 continue;
212 if (_sustain)
213 _drv->_out[i]->_sustain = 1;
214 else
215 _drv->_out[i]->noteOff();
216 return;
217 }
218 }
219
noteOn(uint8 note,uint8 velo)220 void TownsMidiPart::noteOn(uint8 note, uint8 velo) {
221 if (note < 12 || note > 107)
222 return;
223
224 if (velo == 0) {
225 noteOff(note);
226 return;
227 }
228
229 if (_drv->_version != SCI_VERSION_1_EARLY)
230 velo >>= 1;
231
232 for (int i = 0; i < 6; i++) {
233 if ((_drv->_out[i]->_assign != _id && _drv->_version != SCI_VERSION_1_EARLY) || _drv->_out[i]->_note != note)
234 continue;
235 _drv->_out[i]->_sustain = 0;
236 _drv->_out[i]->noteOff();
237 _drv->_out[i]->noteOn(note, velo);
238 return;
239 }
240
241 int chan = allocateChannel();
242 if (chan != -1)
243 _drv->_out[chan]->noteOn(note, velo);
244 }
245
controlChangeVolume(uint8 vol)246 void TownsMidiPart::controlChangeVolume(uint8 vol) {
247 if (_drv->_version == SCI_VERSION_1_EARLY)
248 return;
249
250 _volume = vol >> 1;
251 for (int i = 0; i < 6; i++) {
252 if (_drv->_out[i]->_assign == _id)
253 _drv->_out[i]->updateVolume();
254 }
255 }
256
controlChangeSustain(uint8 sus)257 void TownsMidiPart::controlChangeSustain(uint8 sus) {
258 if (_drv->_version == SCI_VERSION_1_EARLY)
259 return;
260
261 _sustain = sus;
262 if (_sustain)
263 return;
264
265 for (int i = 0; i < 6; i++) {
266 if (_drv->_out[i]->_assign == _id && _drv->_out[i]->_sustain) {
267 _drv->_out[i]->_sustain = 0;
268 _drv->_out[i]->noteOff();
269 }
270 }
271 }
272
controlChangePolyphony(uint8 numChan)273 void TownsMidiPart::controlChangePolyphony(uint8 numChan) {
274 if (_drv->_version == SCI_VERSION_1_EARLY)
275 return;
276
277 uint8 numAssigned = 0;
278 for (int i = 0; i < 6; i++) {
279 if (_drv->_out[i]->_assign == _id)
280 numAssigned++;
281 }
282
283 numAssigned += _chanMissing;
284 if (numAssigned < numChan) {
285 addChannels(numChan - numAssigned);
286 } else if (numAssigned > numChan) {
287 dropChannels(numAssigned - numChan);
288 _drv->addMissingChannels();
289 }
290 }
291
controlChangeAllNotesOff()292 void TownsMidiPart::controlChangeAllNotesOff() {
293 for (int i = 0; i < 6; i++) {
294 if ((_drv->_out[i]->_assign == _id || _drv->_version == SCI_VERSION_1_EARLY) && _drv->_out[i]->_note != 0xff)
295 _drv->_out[i]->noteOff();
296 }
297 }
298
programChange(uint8 prg)299 void TownsMidiPart::programChange(uint8 prg) {
300 _program = prg;
301 }
302
pitchBend(int16 val)303 void TownsMidiPart::pitchBend(int16 val) {
304 _pitchBend = val;
305 val -= 0x2000;
306 for (int i = 0; i < 6; i++) {
307 // Strangely, the early version driver applies the setting to channel 0 only.
308 if (_drv->_out[i]->_assign == _id || (_drv->_version == SCI_VERSION_1_EARLY && i == 0))
309 _drv->_out[i]->pitchBend(val);
310 }
311 }
312
addChannels(int num)313 void TownsMidiPart::addChannels(int num) {
314 for (int i = 0; i < 6; i++) {
315 if (_drv->_out[i]->_assign != 0xff)
316 continue;
317
318 _drv->_out[i]->_assign = _id;
319 _drv->_out[i]->updateVolume();
320
321 if (_drv->_out[i]->_note != 0xff)
322 _drv->_out[i]->noteOff();
323
324 if (!--num)
325 break;
326 }
327
328 _chanMissing += num;
329 programChange(_program);
330 pitchBend(_pitchBend);
331 controlChangeVolume(_volume << 1);
332 }
333
dropChannels(int num)334 void TownsMidiPart::dropChannels(int num) {
335 if (_chanMissing == num) {
336 _chanMissing = 0;
337 return;
338 } else if (_chanMissing > num) {
339 _chanMissing -= num;
340 return;
341 }
342
343 num -= _chanMissing;
344 _chanMissing = 0;
345
346 for (int i = 0; i < 6; i++) {
347 if (_drv->_out[i]->_assign != _id || _drv->_out[i]->_note != 0xff)
348 continue;
349 _drv->_out[i]->_assign = 0xff;
350 if (!--num)
351 return;
352 }
353
354 for (int i = 0; i < 6; i++) {
355 if (_drv->_out[i]->_assign != _id)
356 continue;
357 _drv->_out[i]->_sustain = 0;
358 _drv->_out[i]->noteOff();
359 _drv->_out[i]->_assign = 0xff;
360 if (!--num)
361 return;
362 }
363 }
364
currentProgram() const365 uint8 TownsMidiPart::currentProgram() const {
366 return _program;
367 }
368
allocateChannel()369 int TownsMidiPart::allocateChannel() {
370 int chan = _outChan;
371 int ovrChan = 0;
372 int ld = 0;
373 bool found = false;
374
375 for (bool loop = true; loop; ) {
376 if (++chan == 6)
377 chan = 0;
378
379 if (chan == _outChan)
380 loop = false;
381
382 if (_id == _drv->_out[chan]->_assign || _drv->_version == SCI_VERSION_1_EARLY) {
383 if (_drv->_out[chan]->_note == 0xff) {
384 found = true;
385 break;
386 }
387
388 if (_drv->_out[chan]->_duration >= ld) {
389 ld = _drv->_out[chan]->_duration;
390 ovrChan = chan;
391 }
392 }
393 }
394
395 if (!found) {
396 if (!ld)
397 return -1;
398 chan = ovrChan;
399 _drv->_out[chan]->_sustain = 0;
400 _drv->_out[chan]->noteOff();
401 }
402
403 _outChan = chan;
404 return chan;
405 }
406
MidiDriver_FMTowns(Audio::Mixer * mixer,SciVersion version)407 MidiDriver_FMTowns::MidiDriver_FMTowns(Audio::Mixer *mixer, SciVersion version) : _version(version), _timerProc(0), _timerProcPara(0), _baseTempo(10080), _ready(false), _isOpen(false), _masterVolume(0x0f), _soundOn(true) {
408 _intf = new TownsAudioInterface(mixer, this, true);
409 _out = new TownsChannel*[6];
410 for (int i = 0; i < 6; i++)
411 _out[i] = new TownsChannel(this, i);
412 _parts = new TownsMidiPart*[16];
413 for (int i = 0; i < 16; i++)
414 _parts[i] = new TownsMidiPart(this, i);
415 }
416
~MidiDriver_FMTowns()417 MidiDriver_FMTowns::~MidiDriver_FMTowns() {
418 delete _intf;
419
420 if (_parts) {
421 for (int i = 0; i < 16; i++) {
422 delete _parts[i];
423 _parts[i] = 0;
424 }
425 delete[] _parts;
426 _parts = 0;
427 }
428
429 if (_out) {
430 for (int i = 0; i < 6; i++) {
431 delete _out[i];
432 _out[i] = 0;
433 }
434 delete[] _out;
435 _out = 0;
436 }
437 }
438
open()439 int MidiDriver_FMTowns::open() {
440 if (_isOpen)
441 return MERR_ALREADY_OPEN;
442
443 if (!_ready) {
444 if (!_intf->init())
445 return MERR_CANNOT_CONNECT;
446
447 _intf->callback(0);
448
449 _intf->callback(21, 255, 1);
450 _intf->callback(21, 0, 1);
451 _intf->callback(22, 255, 221);
452
453 _intf->callback(33, 8);
454 _intf->setSoundEffectChanMask(~0x3f);
455
456 _ready = true;
457 }
458
459 _isOpen = true;
460
461 return 0;
462 }
463
loadInstruments(const SciSpan<const uint8> & data)464 void MidiDriver_FMTowns::loadInstruments(const SciSpan<const uint8> &data) {
465 enum {
466 fmDataSize = 48
467 };
468
469 if (data.size()) {
470 SciSpan<const uint8> instrumentData = data.subspan(6);
471 for (int i = 0; i < 128; i++, instrumentData += fmDataSize) {
472 _intf->callback(5, 0, i, instrumentData.getUnsafeDataAt(0, fmDataSize));
473 }
474 }
475
476 _intf->callback(70, 3);
477 property(MIDI_PROP_MASTER_VOLUME, _masterVolume);
478 }
479
close()480 void MidiDriver_FMTowns::close() {
481 _isOpen = false;
482 }
483
send(uint32 b)484 void MidiDriver_FMTowns::send(uint32 b) {
485 if (!_isOpen)
486 return;
487
488 byte para2 = (b >> 16) & 0xFF;
489 byte para1 = (b >> 8) & 0xFF;
490 byte cmd = b & 0xF0;
491
492 TownsMidiPart *chan = _parts[b & 0x0F];
493
494 switch (cmd) {
495 case 0x80:
496 chan->noteOff(para1);
497 break;
498 case 0x90:
499 chan->noteOn(para1, para2);
500 break;
501 case 0xb0:
502 switch (para1) {
503 case 7:
504 chan->controlChangeVolume(para2);
505 break;
506 case 64:
507 chan->controlChangeSustain(para2);
508 break;
509 case SCI_MIDI_SET_POLYPHONY:
510 chan->controlChangePolyphony(para2);
511 break;
512 case SCI_MIDI_CHANNEL_NOTES_OFF:
513 chan->controlChangeAllNotesOff();
514 break;
515 default:
516 break;
517 }
518 break;
519 case 0xc0:
520 chan->programChange(para1);
521 break;
522 case 0xe0:
523 chan->pitchBend(para1 | (para2 << 7));
524 break;
525 default:
526 break;
527 }
528 }
529
property(int prop,uint32 param)530 uint32 MidiDriver_FMTowns::property(int prop, uint32 param) {
531 switch(prop) {
532 case MIDI_PROP_MASTER_VOLUME:
533 if (param != 0xffff) {
534 _masterVolume = param;
535 for (int i = 0; i < 6; i++)
536 _out[i]->updateVolume();
537 }
538 return _masterVolume;
539 default:
540 break;
541 }
542 return 0;
543 }
544
setTimerCallback(void * timer_param,Common::TimerManager::TimerProc timer_proc)545 void MidiDriver_FMTowns::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
546 _timerProc = timer_proc;
547 _timerProcPara = timer_param;
548 }
549
setSoundOn(bool toggle)550 void MidiDriver_FMTowns::setSoundOn(bool toggle) {
551 _soundOn = toggle;
552 }
553
getBaseTempo()554 uint32 MidiDriver_FMTowns::getBaseTempo() {
555 return _baseTempo;
556 }
557
timerCallback(int timerId)558 void MidiDriver_FMTowns::timerCallback(int timerId) {
559 if (!_isOpen)
560 return;
561
562 switch (timerId) {
563 case 1:
564 updateParser();
565 updateChannels();
566 break;
567 default:
568 break;
569 }
570 }
571
getChannelVolume(uint8 midiPart)572 int MidiDriver_FMTowns::getChannelVolume(uint8 midiPart) {
573 static const uint8 volumeTable[] = { 0x00, 0x0D, 0x1B, 0x28, 0x36, 0x43, 0x51, 0x5F, 0x63, 0x67, 0x6B, 0x6F, 0x73, 0x77, 0x7B, 0x7F };
574 int tableIndex = (_version == SCI_VERSION_1_EARLY) ? _masterVolume : (_parts[midiPart]->_volume * (_masterVolume + 1)) >> 6;
575 assert(tableIndex < 16);
576 return volumeTable[tableIndex];
577 }
578
addMissingChannels()579 void MidiDriver_FMTowns::addMissingChannels() {
580 uint8 avlChan = 0;
581 for (int i = 0; i < 6; i++) {
582 if (_out[i]->_assign == 0xff)
583 avlChan++;
584 }
585
586 if (!avlChan)
587 return;
588
589 for (int i = 0; i < 16; i++) {
590 if (!_parts[i]->_chanMissing)
591 continue;
592
593 if (_parts[i]->_chanMissing < avlChan) {
594 avlChan -= _parts[i]->_chanMissing;
595 uint8 m = _parts[i]->_chanMissing;
596 _parts[i]->_chanMissing = 0;
597 _parts[i]->addChannels(m);
598 } else {
599 _parts[i]->_chanMissing -= avlChan;
600 _parts[i]->addChannels(avlChan);
601 return;
602 }
603 }
604 }
605
updateParser()606 void MidiDriver_FMTowns::updateParser() {
607 if (_timerProc)
608 _timerProc(_timerProcPara);
609 }
610
updateChannels()611 void MidiDriver_FMTowns::updateChannels() {
612 for (int i = 0; i < 6; i++)
613 _out[i]->updateDuration();
614 }
615
MidiPlayer_FMTowns(SciVersion version)616 MidiPlayer_FMTowns::MidiPlayer_FMTowns(SciVersion version) : MidiPlayer(version) {
617 _driver = _townsDriver = new MidiDriver_FMTowns(g_system->getMixer(), version);
618 }
619
~MidiPlayer_FMTowns()620 MidiPlayer_FMTowns::~MidiPlayer_FMTowns() {
621 delete _driver;
622 }
623
open(ResourceManager * resMan)624 int MidiPlayer_FMTowns::open(ResourceManager *resMan) {
625 int result = MidiDriver::MERR_DEVICE_NOT_AVAILABLE;
626 if (_townsDriver) {
627 result = _townsDriver->open();
628 if (!result && _version == SCI_VERSION_1_LATE)
629 _townsDriver->loadInstruments(*resMan->findResource(ResourceId(kResourceTypePatch, 8), false));
630 }
631 return result;
632 }
633
hasRhythmChannel() const634 bool MidiPlayer_FMTowns::hasRhythmChannel() const {
635 return false;
636 }
637
getPlayId() const638 byte MidiPlayer_FMTowns::getPlayId() const {
639 return (_version == SCI_VERSION_1_EARLY) ? 0x00 : 0x16;
640 }
641
getPolyphony() const642 int MidiPlayer_FMTowns::getPolyphony() const {
643 // WORKAROUND:
644 // I set the return value to 16 for SCI_VERSION_1_EARLY here, which fixes music playback in Mixed Up Mothergoose.
645 // This has been broken since the introduction of SciMusic::remapChannels() and the corresponding code.
646 // The original code of Mixed Up Mothergoose code doesn't have the remapping and doesn't seem to check the polyphony
647 // setting ever. So the value of 1 was probably incorrect.
648 return (_version == SCI_VERSION_1_EARLY) ? 16 : 6;
649 }
650
playSwitch(bool play)651 void MidiPlayer_FMTowns::playSwitch(bool play) {
652 if (_townsDriver)
653 _townsDriver->setSoundOn(play);
654 }
655
MidiPlayer_FMTowns_create(SciVersion _soundVersion)656 MidiPlayer *MidiPlayer_FMTowns_create(SciVersion _soundVersion) {
657 return new MidiPlayer_FMTowns(_soundVersion);
658 }
659
660 } // End of namespace Sci
661
662