Speech bubbles can point down right.
[scummvm-innocent.git] / sound / softsynth / mt32.cpp
blobd662be989ee41c3e735efbe6ed454325778a59df
1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $URL$
22 * $Id$
25 #include "common/scummsys.h"
27 #ifdef USE_MT32EMU
29 #include "sound/softsynth/mt32/mt32emu.h"
31 #include "sound/softsynth/emumidi.h"
32 #include "sound/musicplugin.h"
33 #include "sound/mpu401.h"
35 #include "common/config-manager.h"
36 #include "common/debug.h"
37 #include "common/events.h"
38 #include "common/file.h"
39 #include "common/system.h"
40 #include "common/util.h"
42 #include "graphics/fontman.h"
43 #include "graphics/surface.h"
45 class MidiChannel_MT32 : public MidiChannel_MPU401 {
46 void effectLevel(byte value) { }
47 void chorusLevel(byte value) { }
50 class MidiDriver_MT32 : public MidiDriver_Emulated {
51 private:
52 Audio::SoundHandle _handle;
53 MidiChannel_MT32 _midiChannels[16];
54 uint16 _channelMask;
55 MT32Emu::Synth *_synth;
57 int _outputRate;
59 protected:
60 void generateSamples(int16 *buf, int len);
62 public:
63 bool _initialising;
65 MidiDriver_MT32(Audio::Mixer *mixer);
66 virtual ~MidiDriver_MT32();
68 int open();
69 void close();
70 void send(uint32 b);
71 void setPitchBendRange (byte channel, uint range);
72 void sysEx(const byte *msg, uint16 length);
74 uint32 property(int prop, uint32 param);
75 MidiChannel *allocateChannel();
76 MidiChannel *getPercussionChannel();
78 // AudioStream API
79 bool isStereo() const { return true; }
80 int getRate() const { return _outputRate; }
83 class MT32File : public MT32Emu::File {
84 Common::File _in;
85 Common::DumpFile _out;
86 public:
87 bool open(const char *filename, OpenMode mode) {
88 if (mode == OpenMode_read)
89 return _in.open(filename);
90 else
91 return _out.open(filename);
93 void close() {
94 _in.close();
95 _out.close();
97 size_t read(void *in, size_t size) {
98 return _in.read(in, size);
100 bool readBit8u(MT32Emu::Bit8u *in) {
101 byte b = _in.readByte();
102 if (_in.eos())
103 return false;
104 *in = b;
105 return true;
107 size_t write(const void *in, size_t size) {
108 return _out.write(in, size);
110 bool writeBit8u(MT32Emu::Bit8u out) {
111 _out.writeByte(out);
112 return !_out.err();
114 bool isEOF() {
115 return _in.isOpen() && _in.eos();
119 static int eatSystemEvents() {
120 Common::Event event;
121 Common::EventManager *eventMan = g_system->getEventManager();
122 while (eventMan->pollEvent(event)) {
123 switch (event.type) {
124 case Common::EVENT_QUIT:
125 return 1;
126 default:
127 break;
130 return 0;
133 static void drawProgress(float progress) {
134 const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kOSDFont));
135 Graphics::Surface surf;
136 uint32 borderColor = 0x2;
137 uint32 fillColor = 0x4;
138 surf.w = g_system->getWidth() / 7 * 5;
139 surf.h = font.getFontHeight();
140 int x = g_system->getWidth() / 7;
141 int y = g_system->getHeight() / 2 - surf.h / 2;
142 surf.pitch = surf.w;
143 surf.bytesPerPixel = 1;
144 surf.pixels = calloc(surf.w, surf.h);
145 Common::Rect r(surf.w, surf.h);
146 surf.frameRect(r, borderColor);
147 r.grow(-1);
148 r.right = r.left + (uint16)(r.width() * progress);
149 surf.fillRect(r, fillColor);
150 g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, x, y, surf.w, surf.h);
151 g_system->updateScreen();
152 free(surf.pixels);
155 static void drawMessage(int offset, const Common::String &text) {
156 const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kOSDFont));
157 Graphics::Surface surf;
158 uint32 color = 0x2;
159 surf.w = g_system->getWidth();
160 surf.h = font.getFontHeight();
161 surf.pitch = surf.w;
162 surf.bytesPerPixel = 1;
163 surf.pixels = calloc(surf.w, surf.h);
164 font.drawString(&surf, text, 0, 0, surf.w, color, Graphics::kTextAlignCenter);
165 int y = g_system->getHeight() / 2 - font.getFontHeight() / 2 + offset * (font.getFontHeight() + 1);
166 g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, 0, y, surf.w, surf.h);
167 g_system->updateScreen();
168 free(surf.pixels);
171 static MT32Emu::File *MT32_OpenFile(void *userData, const char *filename, MT32Emu::File::OpenMode mode) {
172 MT32File *file = new MT32File();
173 if (!file->open(filename, mode)) {
174 delete file;
175 return NULL;
177 return file;
180 static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) {
181 char buf[512];
182 if (((MidiDriver_MT32 *)userData)->_initialising) {
183 vsnprintf(buf, 512, fmt, list);
184 buf[70] = 0; // Truncate to a reasonable length
185 drawMessage(1, buf);
187 //vdebug(0, fmt, list); // FIXME: Use a higher debug level
190 static int MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) {
191 switch(type) {
192 case MT32Emu::ReportType_lcdMessage:
193 g_system->displayMessageOnOSD((const char *)reportData);
194 break;
195 case MT32Emu::ReportType_errorControlROM:
196 error("Failed to load MT32_CONTROL.ROM");
197 break;
198 case MT32Emu::ReportType_errorPCMROM:
199 error("Failed to load MT32_PCM.ROM");
200 break;
201 case MT32Emu::ReportType_progressInit:
202 if (((MidiDriver_MT32 *)userData)->_initialising) {
203 drawProgress(*((const float *)reportData));
204 return eatSystemEvents();
206 break;
207 case MT32Emu::ReportType_availableSSE:
208 debug(1, "MT32emu: SSE is available");
209 break;
210 case MT32Emu::ReportType_usingSSE:
211 debug(1, "MT32emu: using SSE");
212 break;
213 case MT32Emu::ReportType_available3DNow:
214 debug(1, "MT32emu: 3DNow! is available");
215 break;
216 case MT32Emu::ReportType_using3DNow:
217 debug(1, "MT32emu: using 3DNow!");
218 break;
219 default:
220 break;
222 return 0;
225 ////////////////////////////////////////
227 // MidiDriver_MT32
229 ////////////////////////////////////////
231 MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) {
232 _channelMask = 0xFFFF; // Permit all 16 channels by default
233 uint i;
234 for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
235 _midiChannels[i].init(this, i);
237 _synth = NULL;
238 // A higher baseFreq reduces the length used in generateSamples(),
239 // and means that the timer callback will be called more often.
240 // That results in more accurate timing.
241 _baseFreq = 10000;
242 // Unfortunately bugs in the emulator cause inaccurate tuning
243 // at rates other than 32KHz, thus we produce data at 32KHz and
244 // rely on Mixer to convert.
245 _outputRate = 32000; //_mixer->getOutputRate();
246 _initialising = false;
249 MidiDriver_MT32::~MidiDriver_MT32() {
250 if (_synth != NULL)
251 delete _synth;
254 int MidiDriver_MT32::open() {
255 MT32Emu::SynthProperties prop;
257 if (_isOpen)
258 return MERR_ALREADY_OPEN;
260 MidiDriver_Emulated::open();
262 memset(&prop, 0, sizeof(prop));
263 prop.sampleRate = getRate();
264 prop.useReverb = true;
265 prop.useDefaultReverb = false;
266 prop.reverbType = 0;
267 prop.reverbTime = 5;
268 prop.reverbLevel = 3;
269 prop.userData = this;
270 prop.printDebug = MT32_PrintDebug;
271 prop.report = MT32_Report;
272 prop.openFile = MT32_OpenFile;
273 _synth = new MT32Emu::Synth();
274 _initialising = true;
275 const byte dummy_palette[] = {
276 0, 0, 0, 0,
277 0, 0, 171, 0,
278 0, 171, 0, 0,
279 0, 171, 171, 0,
280 171, 0, 0, 0
283 g_system->setPalette(dummy_palette, 0, 5);
284 drawMessage(-1, "Initialising MT-32 Emulator");
285 if (!_synth->open(prop))
286 return MERR_DEVICE_NOT_AVAILABLE;
287 _initialising = false;
288 g_system->fillScreen(0);
289 g_system->updateScreen();
290 _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_handle, this, -1, 255, 0, false, true);
291 return 0;
294 void MidiDriver_MT32::send(uint32 b) {
295 _synth->playMsg(b);
298 void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) {
299 if (range > 24) {
300 printf("setPitchBendRange() called with range > 24: %d", range);
302 byte benderRangeSysex[9];
303 benderRangeSysex[0] = 0x41; // Roland
304 benderRangeSysex[1] = channel;
305 benderRangeSysex[2] = 0x16; // MT-32
306 benderRangeSysex[3] = 0x12; // Write
307 benderRangeSysex[4] = 0x00;
308 benderRangeSysex[5] = 0x00;
309 benderRangeSysex[6] = 0x04;
310 benderRangeSysex[7] = (byte)range;
311 benderRangeSysex[8] = MT32Emu::Synth::calcSysexChecksum(&benderRangeSysex[4], 4, 0);
312 sysEx(benderRangeSysex, 9);
315 void MidiDriver_MT32::sysEx(const byte *msg, uint16 length) {
316 if (msg[0] == 0xf0) {
317 _synth->playSysex(msg, length);
318 } else {
319 _synth->playSysexWithoutFraming(msg, length);
323 void MidiDriver_MT32::close() {
324 if (!_isOpen)
325 return;
326 _isOpen = false;
328 // Detach the player callback handler
329 setTimerCallback(NULL, NULL);
330 // Detach the mixer callback handler
331 _mixer->stopHandle(_handle);
333 _synth->close();
334 delete _synth;
335 _synth = NULL;
338 void MidiDriver_MT32::generateSamples(int16 *data, int len) {
339 _synth->render(data, len);
342 uint32 MidiDriver_MT32::property(int prop, uint32 param) {
343 switch (prop) {
344 case PROP_CHANNEL_MASK:
345 _channelMask = param & 0xFFFF;
346 return 1;
349 return 0;
352 MidiChannel *MidiDriver_MT32::allocateChannel() {
353 MidiChannel_MT32 *chan;
354 uint i;
356 for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
357 if (i == 9 || !(_channelMask & (1 << i)))
358 continue;
359 chan = &_midiChannels[i];
360 if (chan->allocate()) {
361 return chan;
364 return NULL;
367 MidiChannel *MidiDriver_MT32::getPercussionChannel() {
368 return &_midiChannels[9];
371 // This code should be used when calling the timer callback from the mixer thread is undesirable.
372 // Note that it results in less accurate timing.
373 #if 0
374 class MidiEvent_MT32 {
375 public:
376 MidiEvent_MT32 *_next;
377 uint32 _msg; // 0xFFFFFFFF indicates a sysex message
378 byte *_data;
379 uint32 _len;
381 MidiEvent_MT32(uint32 msg, byte *data, uint32 len) {
382 _msg = msg;
383 if (len > 0) {
384 _data = new byte[len];
385 memcpy(_data, data, len);
387 _len = len;
388 _next = NULL;
391 MidiEvent_MT32() {
392 if (_len > 0)
393 delete _data;
397 class MidiDriver_ThreadedMT32 : public MidiDriver_MT32 {
398 private:
399 OSystem::Mutex _eventMutex;
400 MidiEvent_MT32 *_events;
401 TimerManager::TimerProc _timer_proc;
403 void pushMidiEvent(MidiEvent_MT32 *event);
404 MidiEvent_MT32 *popMidiEvent();
406 protected:
407 void send(uint32 b);
408 void sysEx(const byte *msg, uint16 length);
410 public:
411 MidiDriver_ThreadedMT32(Audio::Mixer *mixer);
413 void onTimer();
414 void close();
415 void setTimerCallback(void *timer_param, TimerManager::TimerProc timer_proc);
419 MidiDriver_ThreadedMT32::MidiDriver_ThreadedMT32(Audio::Mixer *mixer) : MidiDriver_MT32(mixer) {
420 _events = NULL;
421 _timer_proc = NULL;
424 void MidiDriver_ThreadedMT32::close() {
425 MidiDriver_MT32::close();
426 while ((popMidiEvent() != NULL)) {
427 // Just eat any leftover events
431 void MidiDriver_ThreadedMT32::setTimerCallback(void *timer_param, TimerManager::TimerProc timer_proc) {
432 if (!_timer_proc || !timer_proc) {
433 if (_timer_proc)
434 _vm->_timer->removeTimerProc(_timer_proc);
435 _timer_proc = timer_proc;
436 if (timer_proc)
437 _vm->_timer->installTimerProc(timer_proc, getBaseTempo(), timer_param);
441 void MidiDriver_ThreadedMT32::pushMidiEvent(MidiEvent_MT32 *event) {
442 Common::StackLock lock(_eventMutex);
443 if (_events == NULL) {
444 _events = event;
445 } else {
446 MidiEvent_MT32 *last = _events;
447 while (last->_next != NULL)
448 last = last->_next;
449 last->_next = event;
453 MidiEvent_MT32 *MidiDriver_ThreadedMT32::popMidiEvent() {
454 Common::StackLock lock(_eventMutex);
455 MidiEvent_MT32 *event;
456 event = _events;
457 if (event != NULL)
458 _events = event->_next;
459 return event;
462 void MidiDriver_ThreadedMT32::send(uint32 b) {
463 MidiEvent_MT32 *event = new MidiEvent_MT32(b, NULL, 0);
464 pushMidiEvent(event);
467 void MidiDriver_ThreadedMT32::sysEx(const byte *msg, uint16 length) {
468 MidiEvent_MT32 *event = new MidiEvent_MT32(0xFFFFFFFF, msg, length);
469 pushMidiEvent(event);
472 void MidiDriver_ThreadedMT32::onTimer() {
473 MidiEvent_MT32 *event;
474 while ((event = popMidiEvent()) != NULL) {
475 if (event->_msg == 0xFFFFFFFF) {
476 MidiDriver_MT32::sysEx(event->_data, event->_len);
477 } else {
478 MidiDriver_MT32::send(event->_msg);
480 delete event;
483 #endif
486 // Plugin interface
488 class MT32EmuMusicPlugin : public MusicPluginObject {
489 public:
490 const char *getName() const {
491 return "MT-32 Emulator";
494 const char *getId() const {
495 return "mt32";
498 MusicDevices getDevices() const;
499 Common::Error createInstance(Audio::Mixer *mixer, MidiDriver **mididriver) const;
502 MusicDevices MT32EmuMusicPlugin::getDevices() const {
503 MusicDevices devices;
504 devices.push_back(MusicDevice(this, "", MT_MT32));
505 return devices;
508 Common::Error MT32EmuMusicPlugin::createInstance(Audio::Mixer *mixer, MidiDriver **mididriver) const {
509 *mididriver = new MidiDriver_MT32(mixer);
511 return Common::kNoError;
514 MidiDriver *MidiDriver_MT32_create(Audio::Mixer *mixer) {
515 // HACK: It will stay here until engine plugin loader overhaul
516 if (ConfMan.hasKey("extrapath"))
517 Common::File::addDefaultDirectory(ConfMan.get("extrapath"));
519 MidiDriver *mididriver;
521 MT32EmuMusicPlugin p;
522 p.createInstance(mixer, &mididriver);
524 return mididriver;
527 //#if PLUGIN_ENABLED_DYNAMIC(MT32)
528 //REGISTER_PLUGIN_DYNAMIC(MT32, PLUGIN_TYPE_MUSIC, MT32EmuMusicPlugin);
529 //#else
530 REGISTER_PLUGIN_STATIC(MT32, PLUGIN_TYPE_MUSIC, MT32EmuMusicPlugin);
531 //#endif
533 #endif