mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-17 05:59:58 +00:00

version of patch 1448395 by Aubin and some of my own changes. Notably: * Two elements of a struct in backends/midi/quicktime.cpp needed to be forced to BE. * NASM set to build Mach-O binaries * configure now checks for Altivec support on Darwin hosts, instead of assuming it. Altivec support can also be disabled (not 100% sure if t his is actually needed, but might be handy for crosscompiling). * The aliasing of the symbols contained in the .asm files done on Windows were also needed on MacOS. Note that while the asm code is properly compiled and linked in, HQ2/3x support will still not work properly on Intel macs if NASM is enabled. svn-id: r21280
254 lines
7.4 KiB
C++
254 lines
7.4 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2001-2006 The ScummVM project
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*/
|
|
|
|
#if defined(MACOSX) || defined(macintosh)
|
|
|
|
|
|
#include "sound/mpu401.h"
|
|
#include "common/util.h"
|
|
|
|
#if defined(MACOSX)
|
|
#include <QuickTime/QuickTimeComponents.h>
|
|
#include <QuickTime/QuickTimeMusic.h>
|
|
#else
|
|
#include <QuickTimeComponents.h>
|
|
#include <QuickTimeMusic.h>
|
|
#endif
|
|
|
|
|
|
// FIXME - the following disables reverb support in the QuickTime / CoreAudio
|
|
// midi backends. For some reasons, reverb will suck away a *lot* of CPU time.
|
|
// Until we know for sure what is causing this and if there is a better way to
|
|
// fix the problem, we just disable all reverb for these backends.
|
|
#define COREAUDIO_REVERB_HACK
|
|
|
|
|
|
/* QuickTime MIDI driver
|
|
* Original QuickTime support by Florent Boudet <flobo@ifrance.com>
|
|
* Modified by Max Horn
|
|
*/
|
|
class MidiDriver_QT : public MidiDriver_MPU401 {
|
|
public:
|
|
MidiDriver_QT();
|
|
|
|
int open();
|
|
void close();
|
|
void send(uint32 b);
|
|
void setPitchBendRange (byte channel, uint range);
|
|
|
|
private:
|
|
NoteAllocator qtNoteAllocator;
|
|
NoteChannel qtNoteChannel[16];
|
|
NoteRequest simpleNoteRequest;
|
|
|
|
// Pitch bend tracking. Necessary since QTMA handles
|
|
// pitch bending so differently from MPU401.
|
|
uint16 _pitchbend [16];
|
|
byte _pitchbend_range [16];
|
|
|
|
void dispose();
|
|
};
|
|
|
|
MidiDriver_QT::MidiDriver_QT() {
|
|
qtNoteAllocator = 0;
|
|
for (int i = 0; i < 16; i++)
|
|
qtNoteChannel[i] = 0;
|
|
}
|
|
|
|
int MidiDriver_QT::open() {
|
|
ComponentResult qtErr = noErr;
|
|
|
|
if (qtNoteAllocator != 0)
|
|
return MERR_ALREADY_OPEN;
|
|
|
|
qtNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType, 0);
|
|
if (qtNoteAllocator == 0)
|
|
goto bail;
|
|
|
|
simpleNoteRequest.info.flags = 0;
|
|
WRITE_BE_UINT16(& simpleNoteRequest.info.polyphony, 11); // simultaneous tones
|
|
WRITE_BE_UINT16(& simpleNoteRequest.info.typicalPolyphony, 0x00010000);
|
|
|
|
qtErr = NAStuffToneDescription(qtNoteAllocator, 1, &simpleNoteRequest.tone);
|
|
if (qtErr != noErr)
|
|
goto bail;
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
qtErr = NANewNoteChannel(qtNoteAllocator, &simpleNoteRequest, &(qtNoteChannel[i]));
|
|
if ((qtErr != noErr) || (qtNoteChannel[i] == 0))
|
|
goto bail;
|
|
|
|
qtErr = NAResetNoteChannel(qtNoteAllocator, qtNoteChannel[i]);
|
|
if (qtErr != noErr)
|
|
goto bail;
|
|
|
|
// Channel 10 (i.e. index 9) is the drum channel. Set it up as such.
|
|
// All other channels default to piano.
|
|
qtErr = NASetInstrumentNumber(qtNoteAllocator, qtNoteChannel[i], (i == 9 ? kFirstDrumkit : kFirstGMInstrument) + 1);
|
|
if (qtErr != noErr)
|
|
goto bail;
|
|
}
|
|
return 0;
|
|
|
|
bail:
|
|
error("Init QT failed %x %x %d\n", (int)qtNoteAllocator, (int)qtNoteChannel, (int)qtErr);
|
|
|
|
dispose();
|
|
|
|
return MERR_DEVICE_NOT_AVAILABLE;
|
|
}
|
|
|
|
void MidiDriver_QT::close()
|
|
{
|
|
MidiDriver_MPU401::close();
|
|
dispose();
|
|
}
|
|
|
|
void MidiDriver_QT::send(uint32 b) {
|
|
MusicMIDIPacket midPacket;
|
|
unsigned char *midiCmd = midPacket.data;
|
|
midPacket.length = 3;
|
|
midiCmd[3] = (b & 0xFF000000) >> 24;
|
|
midiCmd[2] = (b & 0x00FF0000) >> 16;
|
|
midiCmd[1] = (b & 0x0000FF00) >> 8;
|
|
midiCmd[0] = (b & 0x000000FF);
|
|
|
|
unsigned char chanID = midiCmd[0] & 0x0F;
|
|
switch (midiCmd[0] & 0xF0) {
|
|
case 0x80: // Note off
|
|
NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1], 0);
|
|
break;
|
|
|
|
case 0x90: // Note on
|
|
NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1], midiCmd[2]);
|
|
break;
|
|
|
|
case 0xB0: // Effect
|
|
switch (midiCmd[1]) {
|
|
case 0x01: // Modulation
|
|
NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerModulationWheel, midiCmd[2] << 8);
|
|
break;
|
|
|
|
case 0x07: // Volume
|
|
NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerVolume, midiCmd[2] << 8);
|
|
break;
|
|
|
|
case 0x0A: // Pan
|
|
NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerPan, (midiCmd[2] << 1) + 256);
|
|
break;
|
|
|
|
case 0x40: // Sustain on/off
|
|
NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerSustain, midiCmd[2]);
|
|
break;
|
|
|
|
case 0x5b: // ext effect depth
|
|
#if !defined(COREAUDIO_REVERB_HACK)
|
|
NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerReverb, midiCmd[2] << 8);
|
|
#endif
|
|
break;
|
|
|
|
case 0x5d: // chorus depth
|
|
NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerChorus, midiCmd[2] << 8);
|
|
break;
|
|
|
|
case 0x7b: // mode message all notes off
|
|
// FIXME: For unknown reasons, the following code causes weird
|
|
// troubles. In particular, in the Sam&Max intro, all channel are
|
|
// sent this event. As a result, not only does the music stop - it
|
|
// also never resumes!!! This is very odd.
|
|
/* for (int i = 0; i < 128; i++)
|
|
NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], i, 0);
|
|
*/
|
|
break;
|
|
case 0x64:
|
|
case 0x65:
|
|
case 0x06:
|
|
case 0x26:
|
|
// pitch bend changes - ignore those for now
|
|
break;
|
|
|
|
case 0x12: // Occurs in Scumm games
|
|
case 0x77: // Occurs in Simon2
|
|
case 0x79: // Occurs in Simon1
|
|
// What are these ?!? Ignore it for now
|
|
break;
|
|
|
|
default:
|
|
warning("Unknown MIDI effect: %08x", (int)b);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0xC0: // Program change
|
|
NASetInstrumentNumber(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1] + (chanID == 9 ? kFirstDrumkit : kFirstGMInstrument));
|
|
break;
|
|
|
|
case 0xE0:{ // Pitch bend
|
|
// QuickTime specifies pitchbend in semitones, using 8.8 fixed point values;
|
|
// but iMuse sends us the pitch bend data as 0-16383. which has to be mapped
|
|
// to +/- 12 semitones. Based on this, we first center the input data, then
|
|
// multiply it by a factor. If all was right, the factor would be 3/8, but for
|
|
// mysterious reasons the actual factor we have to use is more like 1/32 or 3/64.
|
|
// Maybe the QT docs are right, and
|
|
_pitchbend[chanID] = ((uint16) midiCmd[1] | (uint16) (midiCmd[2] << 7));
|
|
long theBend = ((long) _pitchbend[chanID] - 0x2000) * _pitchbend_range[chanID] / 32;
|
|
|
|
NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerPitchBend, theBend);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error("Unknown Command: %08x", (int)b);
|
|
NASendMIDI(qtNoteAllocator, qtNoteChannel[chanID], &midPacket);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MidiDriver_QT::setPitchBendRange (byte channel, uint range) {
|
|
if (_pitchbend_range[channel] == range)
|
|
return;
|
|
_pitchbend_range[channel] = range;
|
|
|
|
long theBend = _pitchbend[channel];
|
|
theBend = (theBend - 0x2000) * range / 32;
|
|
NASetController(qtNoteAllocator, qtNoteChannel[channel], kControllerPitchBend, theBend);
|
|
}
|
|
|
|
void MidiDriver_QT::dispose()
|
|
{
|
|
for (int i = 0; i < 16; i++) {
|
|
if (qtNoteChannel[i] != 0)
|
|
NADisposeNoteChannel(qtNoteAllocator, qtNoteChannel[i]);
|
|
qtNoteChannel[i] = 0;
|
|
}
|
|
|
|
if (qtNoteAllocator != 0) {
|
|
CloseComponent(qtNoteAllocator);
|
|
qtNoteAllocator = 0;
|
|
}
|
|
}
|
|
|
|
MidiDriver *MidiDriver_QT_create() {
|
|
return new MidiDriver_QT();
|
|
}
|
|
|
|
#endif // MACOSX || macintosh
|