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.
25 #if defined(MACOSX) || defined(macintosh)
27 // HACK to disable deprecated warnings under Mac OS X 10.5.
28 // Apple depracted the complete QuickTime Music/MIDI API.
29 // Apps are supposed to use CoreAudio & CoreMIDI. We do support
30 // those, but while QT Midi support is still around, there is no
31 // reason to disable this driver. If they really ditch the API in 10.6,
32 // we can still release binaries with this driver disabled/removed.
33 #include <AvailabilityMacros.h>
34 #undef DEPRECATED_ATTRIBUTE
35 #define DEPRECATED_ATTRIBUTE
39 #include "common/endian.h"
40 #include "common/util.h"
41 #include "sound/musicplugin.h"
42 #include "sound/mpu401.h"
45 #include <QuickTime/QuickTimeComponents.h>
46 #include <QuickTime/QuickTimeMusic.h>
48 #include <QuickTimeComponents.h>
49 #include <QuickTimeMusic.h>
53 // FIXME: the following disables reverb support in the QuickTime / CoreAudio
54 // midi backends. For some reasons, reverb will suck away a *lot* of CPU time.
55 // Until we know for sure what is causing this and if there is a better way to
56 // fix the problem, we just disable all reverb for these backends.
57 #define COREAUDIO_REVERB_HACK
60 /* QuickTime MIDI driver
61 * Original QuickTime support by Florent Boudet <flobo@ifrance.com>
62 * Modified by Max Horn
64 class MidiDriver_QT
: public MidiDriver_MPU401
{
71 void setPitchBendRange (byte channel
, uint range
);
74 NoteAllocator qtNoteAllocator
;
75 NoteChannel qtNoteChannel
[16];
76 NoteRequest simpleNoteRequest
;
78 // Pitch bend tracking. Necessary since QTMA handles
79 // pitch bending so differently from MPU401.
80 uint16 _pitchbend
[16];
81 byte _pitchbend_range
[16];
86 MidiDriver_QT::MidiDriver_QT() {
88 for (int i
= 0; i
< 16; i
++)
92 int MidiDriver_QT::open() {
93 ComponentResult qtErr
= noErr
;
95 if (qtNoteAllocator
!= 0)
96 return MERR_ALREADY_OPEN
;
98 qtNoteAllocator
= OpenDefaultComponent(kNoteAllocatorComponentType
, 0);
99 if (qtNoteAllocator
== 0)
102 simpleNoteRequest
.info
.flags
= 0;
103 WRITE_BE_UINT16(& simpleNoteRequest
.info
.polyphony
, 11); // simultaneous tones
104 WRITE_BE_UINT32(& simpleNoteRequest
.info
.typicalPolyphony
, 0x00010000);
106 qtErr
= NAStuffToneDescription(qtNoteAllocator
, 1, &simpleNoteRequest
.tone
);
110 for (int i
= 0; i
< 16; i
++) {
111 qtErr
= NANewNoteChannel(qtNoteAllocator
, &simpleNoteRequest
, &(qtNoteChannel
[i
]));
112 if ((qtErr
!= noErr
) || (qtNoteChannel
[i
] == 0))
115 qtErr
= NAResetNoteChannel(qtNoteAllocator
, qtNoteChannel
[i
]);
119 // Channel 10 (i.e. index 9) is the drum channel. Set it up as such.
120 // All other channels default to piano.
121 qtErr
= NASetInstrumentNumber(qtNoteAllocator
, qtNoteChannel
[i
], (i
== 9 ? kFirstDrumkit
: kFirstGMInstrument
) + 1);
128 error("Init QT failed %x %x %d", (int)qtNoteAllocator
, (int)qtNoteChannel
, (int)qtErr
);
132 return MERR_DEVICE_NOT_AVAILABLE
;
135 void MidiDriver_QT::close() {
136 MidiDriver_MPU401::close();
140 void MidiDriver_QT::send(uint32 b
) {
141 MusicMIDIPacket midPacket
;
142 unsigned char *midiCmd
= midPacket
.data
;
143 midPacket
.length
= 3;
144 midiCmd
[3] = (b
& 0xFF000000) >> 24;
145 midiCmd
[2] = (b
& 0x00FF0000) >> 16;
146 midiCmd
[1] = (b
& 0x0000FF00) >> 8;
147 midiCmd
[0] = (b
& 0x000000FF);
149 unsigned char chanID
= midiCmd
[0] & 0x0F;
150 switch (midiCmd
[0] & 0xF0) {
151 case 0x80: // Note off
152 NAPlayNote(qtNoteAllocator
, qtNoteChannel
[chanID
], midiCmd
[1], 0);
155 case 0x90: // Note on
156 NAPlayNote(qtNoteAllocator
, qtNoteChannel
[chanID
], midiCmd
[1], midiCmd
[2]);
160 switch (midiCmd
[1]) {
161 case 0x01: // Modulation
162 NASetController(qtNoteAllocator
, qtNoteChannel
[chanID
], kControllerModulationWheel
, midiCmd
[2] << 8);
166 NASetController(qtNoteAllocator
, qtNoteChannel
[chanID
], kControllerVolume
, midiCmd
[2] << 8);
170 NASetController(qtNoteAllocator
, qtNoteChannel
[chanID
], kControllerPan
, (midiCmd
[2] << 1) + 256);
173 case 0x40: // Sustain on/off
174 NASetController(qtNoteAllocator
, qtNoteChannel
[chanID
], kControllerSustain
, midiCmd
[2]);
177 case 0x5b: // ext effect depth
178 #if !defined(COREAUDIO_REVERB_HACK)
179 NASetController(qtNoteAllocator
, qtNoteChannel
[chanID
], kControllerReverb
, midiCmd
[2] << 8);
183 case 0x5d: // chorus depth
184 NASetController(qtNoteAllocator
, qtNoteChannel
[chanID
], kControllerChorus
, midiCmd
[2] << 8);
187 case 0x7b: // mode message all notes off
188 // FIXME: For unknown reasons, the following code causes weird
189 // troubles. In particular, in the Sam&Max intro, all channel are
190 // sent this event. As a result, not only does the music stop - it
191 // also never resumes!!! This is very odd.
192 /* for (int i = 0; i < 128; i++)
193 NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], i, 0);
200 // pitch bend changes - ignore those for now
203 case 0x12: // Occurs in Scumm games
204 case 0x77: // Occurs in Simon2
205 case 0x79: // Occurs in Simon1
206 // What are these ?!? Ignore it for now
210 warning("Unknown MIDI effect: %08x", (int)b
);
215 case 0xC0: // Program change
216 NASetInstrumentNumber(qtNoteAllocator
, qtNoteChannel
[chanID
], midiCmd
[1] + (chanID
== 9 ? kFirstDrumkit
: kFirstGMInstrument
));
219 case 0xE0:{ // Pitch bend
220 // QuickTime specifies pitchbend in semitones, using 8.8 fixed point values;
221 // but iMuse sends us the pitch bend data as 0-16383. which has to be mapped
222 // to +/- 12 semitones. Based on this, we first center the input data, then
223 // multiply it by a factor. If all was right, the factor would be 3/8, but for
224 // mysterious reasons the actual factor we have to use is more like 1/32 or 3/64.
225 // Maybe the QT docs are right, and
226 _pitchbend
[chanID
] = ((uint16
) midiCmd
[1] | (uint16
) (midiCmd
[2] << 7));
227 long theBend
= ((long) _pitchbend
[chanID
] - 0x2000) * _pitchbend_range
[chanID
] / 32;
229 NASetController(qtNoteAllocator
, qtNoteChannel
[chanID
], kControllerPitchBend
, theBend
);
234 error("Unknown Command: %08x", (int)b
);
235 NASendMIDI(qtNoteAllocator
, qtNoteChannel
[chanID
], &midPacket
);
240 void MidiDriver_QT::setPitchBendRange (byte channel
, uint range
) {
241 if (_pitchbend_range
[channel
] == range
)
243 _pitchbend_range
[channel
] = range
;
245 long theBend
= _pitchbend
[channel
];
246 theBend
= (theBend
- 0x2000) * range
/ 32;
247 NASetController(qtNoteAllocator
, qtNoteChannel
[channel
], kControllerPitchBend
, theBend
);
250 void MidiDriver_QT::dispose() {
251 for (int i
= 0; i
< 16; i
++) {
252 if (qtNoteChannel
[i
] != 0)
253 NADisposeNoteChannel(qtNoteAllocator
, qtNoteChannel
[i
]);
254 qtNoteChannel
[i
] = 0;
257 if (qtNoteAllocator
!= 0) {
258 CloseComponent(qtNoteAllocator
);
266 class QuickTimeMusicPlugin
: public MusicPluginObject
{
268 const char *getName() const {
272 const char *getId() const {
276 MusicDevices
getDevices() const;
277 Common::Error
createInstance(Audio::Mixer
*mixer
, MidiDriver
**mididriver
) const;
280 MusicDevices
QuickTimeMusicPlugin::getDevices() const {
281 MusicDevices devices
;
282 // TODO: Return a different music type depending on the configuration
283 // TODO: List the available devices
284 devices
.push_back(MusicDevice(this, "", MT_GM
));
288 Common::Error
QuickTimeMusicPlugin::createInstance(Audio::Mixer
*mixer
, MidiDriver
**mididriver
) const {
289 *mididriver
= new MidiDriver_QT();
291 return Common::kNoError
;
294 MidiDriver
*MidiDriver_QT_create(Audio::Mixer
*mixer
) {
295 MidiDriver
*mididriver
;
297 QuickTimeMusicPlugin p
;
298 p
.createInstance(mixer
, &mididriver
);
303 //#if PLUGIN_ENABLED_DYNAMIC(QUICKTIME)
304 //REGISTER_PLUGIN_DYNAMIC(QUICKTIME, PLUGIN_TYPE_MUSIC, QuickTimeMusicPlugin);
306 REGISTER_PLUGIN_STATIC(QUICKTIME
, PLUGIN_TYPE_MUSIC
, QuickTimeMusicPlugin
);
309 #endif // MACOSX || macintosh