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