2011-04-16 14:08:33 +02:00
/* ScummVM - Graphic Adventure Engine
2010-01-21 19:15:34 +00:00
*
2011-04-16 14:08:33 +02:00
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
2010-01-21 19:15:34 +00:00
* file distributed with this source distribution .
*
* 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 .
*/
2011-04-17 01:00:10 +02:00
# include "common/scummsys.h"
2011-06-09 11:17:15 +02:00
# include "common/system.h"
2010-01-21 19:15:34 +00:00
# ifdef USE_MT32EMU
2011-04-14 12:41:26 +02:00
# include "audio/softsynth/mt32/mt32emu.h"
2010-01-21 19:15:34 +00:00
2011-04-14 12:41:26 +02:00
# include "audio/softsynth/emumidi.h"
# include "audio/musicplugin.h"
# include "audio/mpu401.h"
2010-01-21 19:15:34 +00:00
# include "common/config-manager.h"
# include "common/debug.h"
2011-05-01 17:49:40 +02:00
# include "common/error.h"
2010-01-21 19:15:34 +00:00
# include "common/events.h"
# include "common/file.h"
# include "common/system.h"
# include "common/util.h"
# include "common/archive.h"
2011-05-01 17:49:40 +02:00
# include "common/textconsole.h"
2011-04-11 11:41:07 +02:00
# include "common/translation.h"
2010-01-21 19:15:34 +00:00
# include "graphics/fontman.h"
# include "graphics/surface.h"
2011-05-01 17:49:40 +02:00
# include "graphics/pixelformat.h"
# include "graphics/font.h"
2010-01-21 19:15:34 +00:00
class MidiChannel_MT32 : public MidiChannel_MPU401 {
void effectLevel ( byte value ) { }
void chorusLevel ( byte value ) { }
} ;
class MidiDriver_MT32 : public MidiDriver_Emulated {
private :
MidiChannel_MT32 _midiChannels [ 16 ] ;
uint16 _channelMask ;
MT32Emu : : Synth * _synth ;
int _outputRate ;
protected :
void generateSamples ( int16 * buf , int len ) ;
public :
2011-06-09 11:17:15 +02:00
bool _initializing ;
2010-01-21 19:15:34 +00:00
MidiDriver_MT32 ( Audio : : Mixer * mixer ) ;
virtual ~ MidiDriver_MT32 ( ) ;
int open ( ) ;
void close ( ) ;
void send ( uint32 b ) ;
void setPitchBendRange ( byte channel , uint range ) ;
void sysEx ( const byte * msg , uint16 length ) ;
uint32 property ( int prop , uint32 param ) ;
MidiChannel * allocateChannel ( ) ;
MidiChannel * getPercussionChannel ( ) ;
// AudioStream API
bool isStereo ( ) const { return true ; }
int getRate ( ) const { return _outputRate ; }
} ;
class MT32File : public MT32Emu : : File {
Common : : File _in ;
Common : : DumpFile _out ;
public :
bool open ( const char * filename , OpenMode mode ) {
if ( mode = = OpenMode_read )
return _in . open ( filename ) ;
else
return _out . open ( filename ) ;
}
void close ( ) {
_in . close ( ) ;
_out . close ( ) ;
}
size_t read ( void * in , size_t size ) {
return _in . read ( in , size ) ;
}
bool readBit8u ( MT32Emu : : Bit8u * in ) {
byte b = _in . readByte ( ) ;
if ( _in . eos ( ) )
return false ;
* in = b ;
return true ;
}
size_t write ( const void * in , size_t size ) {
return _out . write ( in , size ) ;
}
bool writeBit8u ( MT32Emu : : Bit8u out ) {
_out . writeByte ( out ) ;
return ! _out . err ( ) ;
}
bool isEOF ( ) {
return _in . isOpen ( ) & & _in . eos ( ) ;
}
} ;
static int eatSystemEvents ( ) {
Common : : Event event ;
Common : : EventManager * eventMan = g_system - > getEventManager ( ) ;
while ( eventMan - > pollEvent ( event ) ) {
switch ( event . type ) {
case Common : : EVENT_QUIT :
return 1 ;
default :
break ;
}
}
return 0 ;
}
static void drawProgress ( float progress ) {
2011-04-25 05:19:33 +08:00
// TODO implement in Residual
/*const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kOSDFont));
2011-04-10 21:59:04 +02:00
Graphics : : Surface * screen = g_system - > lockScreen ( ) ;
assert ( screen ) ;
assert ( screen - > pixels ) ;
Graphics : : PixelFormat screenFormat = g_system - > getScreenFormat ( ) ;
int16 w = g_system - > getWidth ( ) / 7 * 5 ;
int16 h = font . getFontHeight ( ) ;
int16 x = g_system - > getWidth ( ) / 7 ;
int16 y = g_system - > getHeight ( ) / 2 - h / 2 ;
Common : : Rect r ( x , y , x + w , y + h ) ;
uint32 col ;
2011-04-14 12:41:26 +02:00
2011-04-10 21:59:04 +02:00
if ( screenFormat . bytesPerPixel > 1 )
col = screenFormat . RGBToColor ( 0 , 171 , 0 ) ;
else
col = 1 ;
screen - > frameRect ( r , col ) ;
2010-01-21 19:15:34 +00:00
r . grow ( - 1 ) ;
2011-04-10 21:59:04 +02:00
r . setWidth ( uint16 ( progress * w ) ) ;
if ( screenFormat . bytesPerPixel > 1 )
col = screenFormat . RGBToColor ( 171 , 0 , 0 ) ;
else
col = 2 ;
2011-04-11 02:28:51 +02:00
2011-04-25 05:19:33 +08:00
screen - > fillRect ( r , col ) ;
g_system - > unlockScreen ( ) ;
g_system - > updateScreen ( ) ;
*/
2010-01-21 19:15:34 +00:00
}
static void drawMessage ( int offset , const Common : : String & text ) {
2011-04-25 05:19:33 +08:00
// TODO implement in Residual
/* const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kOSDFont));
2011-04-10 21:59:04 +02:00
Graphics : : Surface * screen = g_system - > lockScreen ( ) ;
assert ( screen ) ;
assert ( screen - > pixels ) ;
Graphics : : PixelFormat screenFormat = g_system - > getScreenFormat ( ) ;
uint16 h = font . getFontHeight ( ) ;
uint16 y = g_system - > getHeight ( ) / 2 - h / 2 + offset * ( h + 1 ) ;
uint32 col ;
2011-04-11 15:40:01 +02:00
2011-04-10 21:59:04 +02:00
if ( screenFormat . bytesPerPixel > 1 )
col = screenFormat . RGBToColor ( 0 , 0 , 0 ) ;
else
col = 0 ;
Common : : Rect r ( 0 , y , screen - > w , y + h ) ;
screen - > fillRect ( r , col ) ;
if ( screenFormat . bytesPerPixel > 1 )
col = screenFormat . RGBToColor ( 0 , 171 , 0 ) ;
else
col = 1 ;
font . drawString ( screen , text , 0 , y , screen - > w , col , Graphics : : kTextAlignCenter ) ;
2011-04-25 05:19:33 +08:00
g_system - > unlockScreen ( ) ;
g_system - > updateScreen ( ) ;
*/
2010-01-21 19:15:34 +00:00
}
static MT32Emu : : File * MT32_OpenFile ( void * userData , const char * filename , MT32Emu : : File : : OpenMode mode ) {
MT32File * file = new MT32File ( ) ;
if ( ! file - > open ( filename , mode ) ) {
delete file ;
return NULL ;
}
return file ;
}
static void MT32_PrintDebug ( void * userData , const char * fmt , va_list list ) {
2011-06-09 11:17:15 +02:00
if ( ( ( MidiDriver_MT32 * ) userData ) - > _initializing ) {
2011-04-10 21:59:04 +02:00
char buf [ 512 ] ;
2010-01-21 19:15:34 +00:00
vsnprintf ( buf , 512 , fmt , list ) ;
buf [ 70 ] = 0 ; // Truncate to a reasonable length
2011-04-10 21:59:04 +02:00
2010-01-21 19:15:34 +00:00
drawMessage ( 1 , buf ) ;
}
2011-04-11 02:28:51 +02:00
2010-01-21 19:15:34 +00:00
//vdebug(0, fmt, list); // FIXME: Use a higher debug level
}
static int MT32_Report ( void * userData , MT32Emu : : ReportType type , const void * reportData ) {
switch ( type ) {
case MT32Emu : : ReportType_lcdMessage :
2011-04-14 12:41:26 +02:00
g_system - > displayMessageOnOSD ( ( const char * ) reportData ) ;
2010-01-21 19:15:34 +00:00
break ;
case MT32Emu : : ReportType_errorControlROM :
error ( " Failed to load MT32_CONTROL.ROM " ) ;
break ;
case MT32Emu : : ReportType_errorPCMROM :
error ( " Failed to load MT32_PCM.ROM " ) ;
break ;
case MT32Emu : : ReportType_progressInit :
2011-06-09 11:17:15 +02:00
if ( ( ( MidiDriver_MT32 * ) userData ) - > _initializing ) {
2010-01-21 19:15:34 +00:00
drawProgress ( * ( ( const float * ) reportData ) ) ;
return eatSystemEvents ( ) ;
}
break ;
case MT32Emu : : ReportType_availableSSE :
debug ( 1 , " MT32emu: SSE is available " ) ;
break ;
case MT32Emu : : ReportType_usingSSE :
debug ( 1 , " MT32emu: using SSE " ) ;
break ;
case MT32Emu : : ReportType_available3DNow :
debug ( 1 , " MT32emu: 3DNow! is available " ) ;
break ;
case MT32Emu : : ReportType_using3DNow :
debug ( 1 , " MT32emu: using 3DNow! " ) ;
break ;
default :
break ;
}
return 0 ;
}
////////////////////////////////////////
//
// MidiDriver_MT32
//
////////////////////////////////////////
MidiDriver_MT32 : : MidiDriver_MT32 ( Audio : : Mixer * mixer ) : MidiDriver_Emulated ( mixer ) {
_channelMask = 0xFFFF ; // Permit all 16 channels by default
uint i ;
for ( i = 0 ; i < ARRAYSIZE ( _midiChannels ) ; + + i ) {
_midiChannels [ i ] . init ( this , i ) ;
}
_synth = NULL ;
// A higher baseFreq reduces the length used in generateSamples(),
// and means that the timer callback will be called more often.
// That results in more accurate timing.
_baseFreq = 10000 ;
// Unfortunately bugs in the emulator cause inaccurate tuning
// at rates other than 32KHz, thus we produce data at 32KHz and
// rely on Mixer to convert.
_outputRate = 32000 ; //_mixer->getOutputRate();
2011-06-09 11:17:15 +02:00
_initializing = false ;
2010-01-21 19:15:34 +00:00
}
MidiDriver_MT32 : : ~ MidiDriver_MT32 ( ) {
delete _synth ;
}
int MidiDriver_MT32 : : open ( ) {
MT32Emu : : SynthProperties prop ;
if ( _isOpen )
return MERR_ALREADY_OPEN ;
MidiDriver_Emulated : : open ( ) ;
memset ( & prop , 0 , sizeof ( prop ) ) ;
prop . sampleRate = getRate ( ) ;
prop . useReverb = true ;
prop . useDefaultReverb = false ;
prop . reverbType = 0 ;
prop . reverbTime = 5 ;
prop . reverbLevel = 3 ;
prop . userData = this ;
prop . printDebug = MT32_PrintDebug ;
prop . report = MT32_Report ;
prop . openFile = MT32_OpenFile ;
2011-04-10 21:59:04 +02:00
2010-01-21 19:15:34 +00:00
_synth = new MT32Emu : : Synth ( ) ;
2011-04-25 05:19:33 +08:00
// TODO implement in Residual
Graphics : : PixelFormat screenFormat ; /*= g_system->getScreenFormat();*/
2011-04-10 21:59:04 +02:00
if ( screenFormat . bytesPerPixel = = 1 ) {
const byte dummy_palette [ ] = {
2011-04-14 12:41:26 +02:00
0 , 0 , 0 , // background
0 , 171 , 0 , // border, font
171 , 0 , 0 // fill
2011-04-10 21:59:04 +02:00
} ;
// TODO implement in Residual
//g_system->setPalette(dummy_palette, 0, 3);
}
2011-06-09 11:17:15 +02:00
_initializing = true ;
drawMessage ( - 1 , _s ( " Initializing MT-32 Emulator " ) ) ;
2010-01-21 19:15:34 +00:00
if ( ! _synth - > open ( prop ) )
return MERR_DEVICE_NOT_AVAILABLE ;
2011-06-09 11:17:15 +02:00
_initializing = false ;
2011-04-10 21:59:04 +02:00
2011-04-10 15:45:37 +02:00
// TODO implement in Residual
2011-04-10 21:59:04 +02:00
/* if (screenFormat.bytesPerPixel > 1)
g_system - > fillScreen ( screenFormat . RGBToColor ( 0 , 0 , 0 ) ) ;
else
g_system - > fillScreen ( 0 ) ;
g_system - > updateScreen ( ) ; */
2011-04-14 12:41:26 +02:00
_mixer - > playStream ( Audio : : Mixer : : kSFXSoundType , & _mixerSoundHandle , this , - 1 , Audio : : Mixer : : kMaxChannelVolume , 0 , DisposeAfterUse : : NO , true ) ;
2011-04-10 21:59:04 +02:00
2010-01-21 19:15:34 +00:00
return 0 ;
}
void MidiDriver_MT32 : : send ( uint32 b ) {
_synth - > playMsg ( b ) ;
}
void MidiDriver_MT32 : : setPitchBendRange ( byte channel , uint range ) {
if ( range > 24 ) {
2011-05-08 15:38:26 +02:00
warning ( " setPitchBendRange() called with range > 24: %d " , range ) ;
2010-01-21 19:15:34 +00:00
}
byte benderRangeSysex [ 9 ] ;
benderRangeSysex [ 0 ] = 0x41 ; // Roland
benderRangeSysex [ 1 ] = channel ;
benderRangeSysex [ 2 ] = 0x16 ; // MT-32
benderRangeSysex [ 3 ] = 0x12 ; // Write
benderRangeSysex [ 4 ] = 0x00 ;
benderRangeSysex [ 5 ] = 0x00 ;
benderRangeSysex [ 6 ] = 0x04 ;
benderRangeSysex [ 7 ] = ( byte ) range ;
benderRangeSysex [ 8 ] = MT32Emu : : Synth : : calcSysexChecksum ( & benderRangeSysex [ 4 ] , 4 , 0 ) ;
sysEx ( benderRangeSysex , 9 ) ;
}
void MidiDriver_MT32 : : sysEx ( const byte * msg , uint16 length ) {
if ( msg [ 0 ] = = 0xf0 ) {
_synth - > playSysex ( msg , length ) ;
} else {
_synth - > playSysexWithoutFraming ( msg , length ) ;
}
}
void MidiDriver_MT32 : : close ( ) {
if ( ! _isOpen )
return ;
_isOpen = false ;
// Detach the player callback handler
setTimerCallback ( NULL , NULL ) ;
// Detach the mixer callback handler
2011-04-14 12:41:26 +02:00
_mixer - > stopHandle ( _mixerSoundHandle ) ;
2010-01-21 19:15:34 +00:00
_synth - > close ( ) ;
delete _synth ;
_synth = NULL ;
}
void MidiDriver_MT32 : : generateSamples ( int16 * data , int len ) {
_synth - > render ( data , len ) ;
}
uint32 MidiDriver_MT32 : : property ( int prop , uint32 param ) {
switch ( prop ) {
case PROP_CHANNEL_MASK :
_channelMask = param & 0xFFFF ;
return 1 ;
}
return 0 ;
}
MidiChannel * MidiDriver_MT32 : : allocateChannel ( ) {
MidiChannel_MT32 * chan ;
uint i ;
for ( i = 0 ; i < ARRAYSIZE ( _midiChannels ) ; + + i ) {
if ( i = = 9 | | ! ( _channelMask & ( 1 < < i ) ) )
continue ;
chan = & _midiChannels [ i ] ;
if ( chan - > allocate ( ) ) {
return chan ;
}
}
return NULL ;
}
MidiChannel * MidiDriver_MT32 : : getPercussionChannel ( ) {
return & _midiChannels [ 9 ] ;
}
// This code should be used when calling the timer callback from the mixer thread is undesirable.
// Note that it results in less accurate timing.
#if 0
class MidiEvent_MT32 {
public :
MidiEvent_MT32 * _next ;
uint32 _msg ; // 0xFFFFFFFF indicates a sysex message
byte * _data ;
uint32 _len ;
MidiEvent_MT32 ( uint32 msg , byte * data , uint32 len ) {
_msg = msg ;
if ( len > 0 ) {
_data = new byte [ len ] ;
memcpy ( _data , data , len ) ;
}
_len = len ;
_next = NULL ;
}
MidiEvent_MT32 ( ) {
if ( _len > 0 )
delete _data ;
}
} ;
class MidiDriver_ThreadedMT32 : public MidiDriver_MT32 {
private :
OSystem : : Mutex _eventMutex ;
MidiEvent_MT32 * _events ;
TimerManager : : TimerProc _timer_proc ;
void pushMidiEvent ( MidiEvent_MT32 * event ) ;
MidiEvent_MT32 * popMidiEvent ( ) ;
protected :
void send ( uint32 b ) ;
void sysEx ( const byte * msg , uint16 length ) ;
public :
MidiDriver_ThreadedMT32 ( Audio : : Mixer * mixer ) ;
void onTimer ( ) ;
void close ( ) ;
void setTimerCallback ( void * timer_param , TimerManager : : TimerProc timer_proc ) ;
} ;
MidiDriver_ThreadedMT32 : : MidiDriver_ThreadedMT32 ( Audio : : Mixer * mixer ) : MidiDriver_MT32 ( mixer ) {
_events = NULL ;
_timer_proc = NULL ;
}
void MidiDriver_ThreadedMT32 : : close ( ) {
MidiDriver_MT32 : : close ( ) ;
while ( ( popMidiEvent ( ) ! = NULL ) ) {
// Just eat any leftover events
}
}
void MidiDriver_ThreadedMT32 : : setTimerCallback ( void * timer_param , TimerManager : : TimerProc timer_proc ) {
if ( ! _timer_proc | | ! timer_proc ) {
if ( _timer_proc )
_vm - > _timer - > removeTimerProc ( _timer_proc ) ;
_timer_proc = timer_proc ;
if ( timer_proc )
_vm - > _timer - > installTimerProc ( timer_proc , getBaseTempo ( ) , timer_param ) ;
}
}
void MidiDriver_ThreadedMT32 : : pushMidiEvent ( MidiEvent_MT32 * event ) {
Common : : StackLock lock ( _eventMutex ) ;
if ( _events = = NULL ) {
_events = event ;
} else {
MidiEvent_MT32 * last = _events ;
while ( last - > _next ! = NULL )
last = last - > _next ;
last - > _next = event ;
}
}
MidiEvent_MT32 * MidiDriver_ThreadedMT32 : : popMidiEvent ( ) {
Common : : StackLock lock ( _eventMutex ) ;
MidiEvent_MT32 * event ;
event = _events ;
if ( event ! = NULL )
_events = event - > _next ;
return event ;
}
void MidiDriver_ThreadedMT32 : : send ( uint32 b ) {
MidiEvent_MT32 * event = new MidiEvent_MT32 ( b , NULL , 0 ) ;
pushMidiEvent ( event ) ;
}
void MidiDriver_ThreadedMT32 : : sysEx ( const byte * msg , uint16 length ) {
MidiEvent_MT32 * event = new MidiEvent_MT32 ( 0xFFFFFFFF , msg , length ) ;
pushMidiEvent ( event ) ;
}
void MidiDriver_ThreadedMT32 : : onTimer ( ) {
MidiEvent_MT32 * event ;
while ( ( event = popMidiEvent ( ) ) ! = NULL ) {
if ( event - > _msg = = 0xFFFFFFFF ) {
MidiDriver_MT32 : : sysEx ( event - > _data , event - > _len ) ;
} else {
MidiDriver_MT32 : : send ( event - > _msg ) ;
}
delete event ;
}
}
# endif
// Plugin interface
class MT32EmuMusicPlugin : public MusicPluginObject {
public :
const char * getName ( ) const {
2011-04-11 11:41:07 +02:00
return _s ( " MT-32 Emulator " ) ;
2010-01-21 19:15:34 +00:00
}
const char * getId ( ) const {
return " mt32 " ;
}
MusicDevices getDevices ( ) const ;
2011-06-09 11:17:15 +02:00
bool checkDevice ( MidiDriver : : DeviceHandle ) const ;
2011-04-11 11:41:07 +02:00
Common : : Error createInstance ( MidiDriver * * mididriver , MidiDriver : : DeviceHandle = 0 ) const ;
2010-01-21 19:15:34 +00:00
} ;
MusicDevices MT32EmuMusicPlugin : : getDevices ( ) const {
MusicDevices devices ;
devices . push_back ( MusicDevice ( this , " " , MT_MT32 ) ) ;
return devices ;
}
2011-06-09 11:17:15 +02:00
bool MT32EmuMusicPlugin : : checkDevice ( MidiDriver : : DeviceHandle ) const {
if ( ! ( ( Common : : File : : exists ( " MT32_CONTROL.ROM " ) & & Common : : File : : exists ( " MT32_PCM.ROM " ) ) | |
( Common : : File : : exists ( " CM32L_CONTROL.ROM " ) & & Common : : File : : exists ( " CM32L_PCM.ROM " ) ) ) ) {
warning ( " The MT-32 emulator requires one of the two following file sets (not bundled with ScummVM): \n Either 'MT32_CONTROL.ROM' and 'MT32_PCM.ROM' or 'CM32L_CONTROL.ROM' and 'CM32L_PCM.ROM' " ) ;
return false ;
}
return true ;
}
2011-04-11 11:41:07 +02:00
Common : : Error MT32EmuMusicPlugin : : createInstance ( MidiDriver * * mididriver , MidiDriver : : DeviceHandle ) const {
2010-01-21 19:15:34 +00:00
if ( ConfMan . hasKey ( " extrapath " ) )
SearchMan . addDirectory ( " extrapath " , ConfMan . get ( " extrapath " ) ) ;
2011-04-11 11:41:07 +02:00
* mididriver = new MidiDriver_MT32 ( g_system - > getMixer ( ) ) ;
2010-01-21 19:15:34 +00:00
2011-04-11 11:41:07 +02:00
return Common : : kNoError ;
2010-01-21 19:15:34 +00:00
}
//#if PLUGIN_ENABLED_DYNAMIC(MT32)
//REGISTER_PLUGIN_DYNAMIC(MT32, PLUGIN_TYPE_MUSIC, MT32EmuMusicPlugin);
//#else
REGISTER_PLUGIN_STATIC ( MT32 , PLUGIN_TYPE_MUSIC , MT32EmuMusicPlugin ) ;
//#endif
# endif