mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-03 07:11:49 +00:00
parent
9cb88f1d46
commit
9d0b746aaa
62
README
62
README
@ -30,11 +30,12 @@ Table of Contents:
|
||||
* 6.1 Autosaves
|
||||
7.0) Music and Sound
|
||||
* 7.1 Adlib emulation
|
||||
* 7.2 MIDI emulation
|
||||
* 7.3 Native MIDI support
|
||||
* 7.4 UNIX native & ALSA sequencer support
|
||||
* 7.5 Using compressed audiofiles (MP3, Ogg Vorbis, Flac)
|
||||
* 7.6 Output sample rate
|
||||
* 7.2 MT-32 emulation
|
||||
* 7.3 MIDI emulation
|
||||
* 7.4 Native MIDI support
|
||||
* 7.5 UNIX native & ALSA sequencer support
|
||||
* 7.6 Using compressed audiofiles (MP3, Ogg Vorbis, Flac)
|
||||
* 7.7 Output sample rate
|
||||
8.0) Configuration Files
|
||||
9.0) Compiling
|
||||
X.X) Credits
|
||||
@ -659,6 +660,7 @@ manual configuration. If you ARE using MIDI, you have several different
|
||||
choices of output, depending on your operating system and configuration.
|
||||
|
||||
adlib - Uses internal Adlib Emulation (default)
|
||||
mt32 - Uses internal MT-32 Emulation
|
||||
pcjr - Uses internal PCjr Emulation
|
||||
pcspk - Uses internal PC Speaker Emulation
|
||||
towns - Uses FM-TOWNS YM2612 Emulation
|
||||
@ -682,25 +684,38 @@ By default an Adlib card will be emulated and ScummVM will output the music
|
||||
as sampled waves. This is the default mode for most games, and offers the
|
||||
best compatibility between machines and games.
|
||||
|
||||
7.2) Playing sound with MT-32 emulation:
|
||||
---- -----------------------------------
|
||||
Some games which contain MIDI music data also have improved tracks designed
|
||||
for MT-32 sound module. ScummVM can now emulate this card, however you should
|
||||
provide original MT-32 ROMs to make it work. Put the roms in game directory or
|
||||
directory specified by extrapath.
|
||||
|
||||
7.2) Playing sound with MIDI emulation:
|
||||
You don't need to specify --native-mt32 with this driver, as it automatically
|
||||
gets turned on.
|
||||
|
||||
NOTE: You need to have enough processor power to use this emulator as it uses
|
||||
heavy floating-point computations.
|
||||
|
||||
|
||||
7.3) Playing sound with MIDI emulation:
|
||||
---- ----------------------------------
|
||||
Some games (such as Sam and Max) only contain MIDI music data. This once
|
||||
prevented music for these games from working on platforms that do not support
|
||||
MIDI, or soundcards that do not provide MIDI drivers (e.g, many soundcards will
|
||||
not play MIDI under Linux). ScummVM can now emulate MIDI mode using sampled
|
||||
waves and Adlib emulation using the -eadlib option. However, if you are capable
|
||||
of using native MIDI, we recommend using one of the MIDI modes below for best
|
||||
sound.
|
||||
waves and Adlib or MT-32 emulation using the -eadlib or -emt32 options respectively.
|
||||
However, if you are capable of using native MIDI, we recommend using one of the MIDI
|
||||
modes below for best sound.
|
||||
|
||||
7.3) Playing sound with Native MIDI:
|
||||
7.4) Playing sound with Native MIDI:
|
||||
---- -------------------------------
|
||||
Use the appropriate -e<mode> command line option from the list above to
|
||||
select your preferred MIDI device. For example, if you wish to use the
|
||||
Windows MIDI driver, use the -ewindows option.
|
||||
|
||||
|
||||
7.4.0) Playing sound with Sequencer MIDI: [UNIX ONLY]
|
||||
7.5.0) Playing sound with Sequencer MIDI: [UNIX ONLY]
|
||||
------ ----------------------------------
|
||||
If your soundcard driver supports a sequencer, you may set the environment
|
||||
variable "SCUMMVM_MIDI" to your sequencer device - e.g., /dev/sequencer
|
||||
@ -713,7 +728,7 @@ performance and quality than Adlib emulation. However, for those systems where
|
||||
sequencer support does not work, you can always fall back on Adlib emulation.
|
||||
|
||||
|
||||
7.4.1) Playing sound with ALSA sequencer: [UNIX ONLY]
|
||||
7.5.1) Playing sound with ALSA sequencer: [UNIX ONLY]
|
||||
------ ----------------------------------
|
||||
If you have installed the ALSA driver with the sequencer support, then
|
||||
set the environment variable SCUMMVM_PORT or the config file parameter
|
||||
@ -762,7 +777,7 @@ Asking FluidSynth to become an ALSA sequencer (using SoundFonts):
|
||||
Once either TiMidity or FluidSynth are running, use the 'aconnect -o -l'
|
||||
command as described earlier in this section.
|
||||
|
||||
7.5.0) Using MP3 files for CD audio:
|
||||
7.6.0) Using MP3 files for CD audio:
|
||||
------ -----------------------------
|
||||
Use LAME or some other MP3 encoder to rip the cd audio tracks to files. Name
|
||||
the files track1.mp3 track2.mp3 etc. ScummVM must be compiled with MAD support
|
||||
@ -773,7 +788,7 @@ following LAME command line:
|
||||
lame -t -q 0 -b 96 track1.wav track1.mp3
|
||||
|
||||
|
||||
7.5.1) Using Ogg Vorbis files for CD audio:
|
||||
7.6.1) Using Ogg Vorbis files for CD audio:
|
||||
------ ------------------------------------
|
||||
Use oggenc or some other vorbis encoder to encode the audio tracks to files.
|
||||
Name the files track1.ogg track2.ogg etc. ScummVM must be compiled with vorbis
|
||||
@ -784,7 +799,7 @@ command line with the value after q specifying the desired quality from 0 to 10:
|
||||
oggenc -q 5 track1.wav
|
||||
|
||||
|
||||
7.5.2) Using Flac files for CD audio:
|
||||
7.6.2) Using Flac files for CD audio:
|
||||
------ ------------------------------------
|
||||
Use flac or some other flac encoder to encode the audio tracks to files.
|
||||
Name the files track1.flac track2.flac etc. In your filesystem only allows
|
||||
@ -799,7 +814,7 @@ Remember that the quality is always the same, varying encoder options will only
|
||||
affect the encoding time and resulting filesize.
|
||||
|
||||
|
||||
7.5.3) Compressing MONSTER.SOU with MP3:
|
||||
7.6.3) Compressing MONSTER.SOU with MP3:
|
||||
------ ---------------------------------
|
||||
You need LAME, and our extract util from the scummvm-tools package to perform
|
||||
this task, and ScummVM must be compiled with MAD support.
|
||||
@ -810,7 +825,7 @@ Eventually you will have a much smaller monster.so3 file, copy this file
|
||||
to your game directory. You can safely remove the monster.sou file.
|
||||
|
||||
|
||||
7.5.4) Compressing MONSTER.SOU with Ogg Vorbis:
|
||||
7.6.4) Compressing MONSTER.SOU with Ogg Vorbis:
|
||||
------ ----------------------------------------
|
||||
As above, but ScummVM must be compiled with OGG support. Run:
|
||||
|
||||
@ -821,7 +836,7 @@ game directory. Ogg encoding may take a considerable longer amount of time
|
||||
than MP3, so have a good book handy.
|
||||
|
||||
|
||||
7.5.5) Compressing MONSTER.SOU with Flac:
|
||||
7.6.5) Compressing MONSTER.SOU with Flac:
|
||||
------ ----------------------------------------
|
||||
As above, but ScummVM must be compiled with Flac support. Run:
|
||||
|
||||
@ -835,7 +850,7 @@ filesize - 1152 seems to be a good value for those kind of soundfiles. Be sure
|
||||
to read the encoder documentation before you use other values.
|
||||
|
||||
|
||||
7.5.6) Compressing sfx/speech in Simon the Sorcerer 1 and 2
|
||||
7.6.6) Compressing sfx/speech in Simon the Sorcerer 1 and 2
|
||||
------ ----------------------------------------------------
|
||||
Use our simon2mp3 util from the scummvm-tools package to perform this task.
|
||||
You can choose between multiple target formats, but note that you can only use
|
||||
@ -861,7 +876,7 @@ For Flac add --flac and optional parameters, i.e.
|
||||
Eventually you will have a much smaller *.mp3, *.ogg or *.fla file, copy this
|
||||
file to your game dir. You can safely remove the old file.
|
||||
|
||||
7.5.7) Compressing speech/music in Broken Sword 1
|
||||
7.6.7) Compressing speech/music in Broken Sword 1
|
||||
------ ------------------------------------------
|
||||
The sword1mp3 tool from the scummvm-tools package can encode music and speech
|
||||
to MP3 as well as Ogg Vorbis.
|
||||
@ -876,7 +891,7 @@ of MP3.
|
||||
Use "sword1mp3 --help" to get a full list of the options.
|
||||
|
||||
|
||||
7.5.8) Compressing speech/music in Broken Sword 2
|
||||
7.6.8) Compressing speech/music in Broken Sword 2
|
||||
------ ------------------------------------------
|
||||
Use our sword2mp3 util from the scummvm-tools package to perform this task.
|
||||
You can choose between multiple target formats, but note that you can only use
|
||||
@ -900,7 +915,7 @@ Broken Sword 2. It will not work with any of the other *.clu files, nor will it
|
||||
work with the speech files from Broken Sword 1.
|
||||
|
||||
|
||||
7.6) Output sample rate:
|
||||
7.7) Output sample rate:
|
||||
---- -------------------
|
||||
|
||||
The output sample rate tells ScummVM how many sound samples to play per channel
|
||||
@ -1181,9 +1196,12 @@ X.X) Credits:
|
||||
|
||||
Special thanks to:
|
||||
Sander Buskens - For his work on the initial reversing of Monkey2
|
||||
Canadacow - For his MT-32 emulator
|
||||
Kevin Carnes - For Scumm16, the basis of ScummVM older gfx codec
|
||||
Jezar - For his freeverb filter implementation
|
||||
Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports
|
||||
Jimmi Thogersen - For ScummRev, and much obscure code/documentation
|
||||
Tristan - For his Linux port of MT-32 emulator
|
||||
|
||||
Tony Warriner and everyone at Revolution Software Ltd. for sharing
|
||||
with us the source of some of their brilliant games, allowing us to
|
||||
|
1
backends/midi/mt32/.cvsignore
Normal file
1
backends/midi/mt32/.cvsignore
Normal file
@ -0,0 +1 @@
|
||||
.deps
|
356
backends/midi/mt32/freeverb.cpp
Normal file
356
backends/midi/mt32/freeverb.cpp
Normal file
@ -0,0 +1,356 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2004 The ScummVM project
|
||||
* Copyright (C) 2000 Jezar at Dreampoint
|
||||
*
|
||||
* This code is public domain
|
||||
*
|
||||
* Parts of this code are:
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* $Header$
|
||||
*
|
||||
*/
|
||||
|
||||
// Comb filter implementation
|
||||
//
|
||||
// Written by
|
||||
// http://www.dreampoint.co.uk
|
||||
// This code is public domain
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "backends/midi/mt32/freeverb.h"
|
||||
|
||||
comb::comb()
|
||||
{
|
||||
filterstore = 0;
|
||||
bufidx = 0;
|
||||
}
|
||||
|
||||
void comb::setbuffer(float *buf, int size)
|
||||
{
|
||||
buffer = buf;
|
||||
bufsize = size;
|
||||
}
|
||||
|
||||
void comb::mute()
|
||||
{
|
||||
for (int i=0; i<bufsize; i++)
|
||||
buffer[i]=0;
|
||||
}
|
||||
|
||||
void comb::setdamp(float val)
|
||||
{
|
||||
damp1 = val;
|
||||
damp2 = 1-val;
|
||||
}
|
||||
|
||||
float comb::getdamp()
|
||||
{
|
||||
return damp1;
|
||||
}
|
||||
|
||||
void comb::setfeedback(float val)
|
||||
{
|
||||
feedback = val;
|
||||
}
|
||||
|
||||
float comb::getfeedback()
|
||||
{
|
||||
return feedback;
|
||||
}
|
||||
|
||||
// Allpass filter implementation
|
||||
|
||||
allpass::allpass()
|
||||
{
|
||||
bufidx = 0;
|
||||
}
|
||||
|
||||
void allpass::setbuffer(float *buf, int size)
|
||||
{
|
||||
buffer = buf;
|
||||
bufsize = size;
|
||||
}
|
||||
|
||||
void allpass::mute()
|
||||
{
|
||||
for (int i=0; i<bufsize; i++)
|
||||
buffer[i]=0;
|
||||
}
|
||||
|
||||
void allpass::setfeedback(float val)
|
||||
{
|
||||
feedback = val;
|
||||
}
|
||||
|
||||
float allpass::getfeedback()
|
||||
{
|
||||
return feedback;
|
||||
}
|
||||
|
||||
// Reverb model implementation
|
||||
|
||||
revmodel::revmodel()
|
||||
{
|
||||
// Tie the components to their buffers
|
||||
combL[0].setbuffer(bufcombL1,combtuningL1);
|
||||
combR[0].setbuffer(bufcombR1,combtuningR1);
|
||||
combL[1].setbuffer(bufcombL2,combtuningL2);
|
||||
combR[1].setbuffer(bufcombR2,combtuningR2);
|
||||
combL[2].setbuffer(bufcombL3,combtuningL3);
|
||||
combR[2].setbuffer(bufcombR3,combtuningR3);
|
||||
combL[3].setbuffer(bufcombL4,combtuningL4);
|
||||
combR[3].setbuffer(bufcombR4,combtuningR4);
|
||||
combL[4].setbuffer(bufcombL5,combtuningL5);
|
||||
combR[4].setbuffer(bufcombR5,combtuningR5);
|
||||
combL[5].setbuffer(bufcombL6,combtuningL6);
|
||||
combR[5].setbuffer(bufcombR6,combtuningR6);
|
||||
combL[6].setbuffer(bufcombL7,combtuningL7);
|
||||
combR[6].setbuffer(bufcombR7,combtuningR7);
|
||||
combL[7].setbuffer(bufcombL8,combtuningL8);
|
||||
combR[7].setbuffer(bufcombR8,combtuningR8);
|
||||
allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
|
||||
allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
|
||||
allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
|
||||
allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
|
||||
allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
|
||||
allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
|
||||
allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
|
||||
allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
|
||||
|
||||
// Set default values
|
||||
allpassL[0].setfeedback(0.5f);
|
||||
allpassR[0].setfeedback(0.5f);
|
||||
allpassL[1].setfeedback(0.5f);
|
||||
allpassR[1].setfeedback(0.5f);
|
||||
allpassL[2].setfeedback(0.5f);
|
||||
allpassR[2].setfeedback(0.5f);
|
||||
allpassL[3].setfeedback(0.5f);
|
||||
allpassR[3].setfeedback(0.5f);
|
||||
setwet(initialwet);
|
||||
setroomsize(initialroom);
|
||||
setdry(initialdry);
|
||||
setdamp(initialdamp);
|
||||
setwidth(initialwidth);
|
||||
setmode(initialmode);
|
||||
|
||||
// Buffer will be full of rubbish - so we MUST mute them
|
||||
mute();
|
||||
}
|
||||
|
||||
void revmodel::mute()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (getmode() >= freezemode)
|
||||
return;
|
||||
|
||||
for (i=0;i<numcombs;i++)
|
||||
{
|
||||
combL[i].mute();
|
||||
combR[i].mute();
|
||||
}
|
||||
for (i=0;i<numallpasses;i++)
|
||||
{
|
||||
allpassL[i].mute();
|
||||
allpassR[i].mute();
|
||||
}
|
||||
}
|
||||
|
||||
void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip)
|
||||
{
|
||||
float outL,outR,input;
|
||||
|
||||
while(numsamples-- > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
outL = outR = 0;
|
||||
input = (*inputL + *inputR) * gain;
|
||||
|
||||
// Accumulate comb filters in parallel
|
||||
for(i=0; i<numcombs; i++)
|
||||
{
|
||||
outL += combL[i].process(input);
|
||||
outR += combR[i].process(input);
|
||||
}
|
||||
|
||||
// Feed through allpasses in series
|
||||
for(i=0; i<numallpasses; i++)
|
||||
{
|
||||
outL = allpassL[i].process(outL);
|
||||
outR = allpassR[i].process(outR);
|
||||
}
|
||||
|
||||
// Calculate output REPLACING anything already there
|
||||
*outputL = outL*wet1 + outR*wet2 + *inputL*dry;
|
||||
*outputR = outR*wet1 + outL*wet2 + *inputR*dry;
|
||||
|
||||
// Increment sample pointers, allowing for interleave (if any)
|
||||
inputL += skip;
|
||||
inputR += skip;
|
||||
outputL += skip;
|
||||
outputR += skip;
|
||||
}
|
||||
}
|
||||
|
||||
void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip)
|
||||
{
|
||||
float outL,outR,input;
|
||||
|
||||
while(numsamples-- > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
outL = outR = 0;
|
||||
input = (*inputL + *inputR) * gain;
|
||||
|
||||
// Accumulate comb filters in parallel
|
||||
for(i=0; i<numcombs; i++)
|
||||
{
|
||||
outL += combL[i].process(input);
|
||||
outR += combR[i].process(input);
|
||||
}
|
||||
|
||||
// Feed through allpasses in series
|
||||
for(i=0; i<numallpasses; i++)
|
||||
{
|
||||
outL = allpassL[i].process(outL);
|
||||
outR = allpassR[i].process(outR);
|
||||
}
|
||||
|
||||
// Calculate output MIXING with anything already there
|
||||
*outputL += outL*wet1 + outR*wet2 + *inputL*dry;
|
||||
*outputR += outR*wet1 + outL*wet2 + *inputR*dry;
|
||||
|
||||
// Increment sample pointers, allowing for interleave (if any)
|
||||
inputL += skip;
|
||||
inputR += skip;
|
||||
outputL += skip;
|
||||
outputR += skip;
|
||||
}
|
||||
}
|
||||
|
||||
void revmodel::update()
|
||||
{
|
||||
// Recalculate internal values after parameter change
|
||||
|
||||
int i;
|
||||
|
||||
wet1 = wet*(width/2 + 0.5f);
|
||||
wet2 = wet*((1-width)/2);
|
||||
|
||||
if (mode >= freezemode)
|
||||
{
|
||||
roomsize1 = 1;
|
||||
damp1 = 0;
|
||||
gain = muted;
|
||||
}
|
||||
else
|
||||
{
|
||||
roomsize1 = roomsize;
|
||||
damp1 = damp;
|
||||
gain = fixedgain;
|
||||
}
|
||||
|
||||
for(i=0; i<numcombs; i++)
|
||||
{
|
||||
combL[i].setfeedback(roomsize1);
|
||||
combR[i].setfeedback(roomsize1);
|
||||
}
|
||||
|
||||
for(i=0; i<numcombs; i++)
|
||||
{
|
||||
combL[i].setdamp(damp1);
|
||||
combR[i].setdamp(damp1);
|
||||
}
|
||||
}
|
||||
|
||||
// The following get/set functions are not inlined, because
|
||||
// speed is never an issue when calling them, and also
|
||||
// because as you develop the reverb model, you may
|
||||
// wish to take dynamic action when they are called.
|
||||
|
||||
void revmodel::setroomsize(float value)
|
||||
{
|
||||
roomsize = (value*scaleroom) + offsetroom;
|
||||
update();
|
||||
}
|
||||
|
||||
float revmodel::getroomsize()
|
||||
{
|
||||
return (roomsize-offsetroom)/scaleroom;
|
||||
}
|
||||
|
||||
void revmodel::setdamp(float value)
|
||||
{
|
||||
damp = value*scaledamp;
|
||||
update();
|
||||
}
|
||||
|
||||
float revmodel::getdamp()
|
||||
{
|
||||
return damp/scaledamp;
|
||||
}
|
||||
|
||||
void revmodel::setwet(float value)
|
||||
{
|
||||
wet = value*scalewet;
|
||||
update();
|
||||
}
|
||||
|
||||
float revmodel::getwet()
|
||||
{
|
||||
return wet/scalewet;
|
||||
}
|
||||
|
||||
void revmodel::setdry(float value)
|
||||
{
|
||||
dry = value*scaledry;
|
||||
}
|
||||
|
||||
float revmodel::getdry()
|
||||
{
|
||||
return dry/scaledry;
|
||||
}
|
||||
|
||||
void revmodel::setwidth(float value)
|
||||
{
|
||||
width = value;
|
||||
update();
|
||||
}
|
||||
|
||||
float revmodel::getwidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
void revmodel::setmode(float value)
|
||||
{
|
||||
mode = value;
|
||||
update();
|
||||
}
|
||||
|
||||
float revmodel::getmode()
|
||||
{
|
||||
if (mode >= freezemode)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
//ends
|
||||
|
246
backends/midi/mt32/freeverb.h
Normal file
246
backends/midi/mt32/freeverb.h
Normal file
@ -0,0 +1,246 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2004 The ScummVM project
|
||||
* Copyright (C) 2000 Jezar at Dreampoint
|
||||
*
|
||||
* This code is public domain
|
||||
*
|
||||
* Parts of this code are:
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* $Header$
|
||||
*
|
||||
*/
|
||||
|
||||
// Macro for killing denormalled numbers
|
||||
//
|
||||
// Written by Jezar at Dreampoint, June 2000
|
||||
// http://www.dreampoint.co.uk
|
||||
// Based on IS_DENORMAL macro by Jon Watte
|
||||
// This code is public domain
|
||||
|
||||
#ifndef _freeverb_h_
|
||||
#define _freeverb_h_
|
||||
|
||||
#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
|
||||
|
||||
// Comb filter class declaration
|
||||
|
||||
class comb
|
||||
{
|
||||
public:
|
||||
comb();
|
||||
void setbuffer(float *buf, int size);
|
||||
inline float process(float inp);
|
||||
void mute();
|
||||
void setdamp(float val);
|
||||
float getdamp();
|
||||
void setfeedback(float val);
|
||||
float getfeedback();
|
||||
private:
|
||||
float feedback;
|
||||
float filterstore;
|
||||
float damp1;
|
||||
float damp2;
|
||||
float *buffer;
|
||||
int bufsize;
|
||||
int bufidx;
|
||||
};
|
||||
|
||||
|
||||
// Big to inline - but crucial for speed
|
||||
|
||||
inline float comb::process(float input)
|
||||
{
|
||||
float output;
|
||||
|
||||
output = buffer[bufidx];
|
||||
undenormalise(output);
|
||||
|
||||
filterstore = (output*damp2) + (filterstore*damp1);
|
||||
undenormalise(filterstore);
|
||||
|
||||
buffer[bufidx] = input + (filterstore*feedback);
|
||||
|
||||
if(++bufidx>=bufsize) bufidx = 0;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Allpass filter declaration
|
||||
|
||||
class allpass
|
||||
{
|
||||
public:
|
||||
allpass();
|
||||
void setbuffer(float *buf, int size);
|
||||
inline float process(float inp);
|
||||
void mute();
|
||||
void setfeedback(float val);
|
||||
float getfeedback();
|
||||
// private:
|
||||
float feedback;
|
||||
float *buffer;
|
||||
int bufsize;
|
||||
int bufidx;
|
||||
};
|
||||
|
||||
|
||||
// Big to inline - but crucial for speed
|
||||
|
||||
inline float allpass::process(float input)
|
||||
{
|
||||
float output;
|
||||
float bufout;
|
||||
|
||||
bufout = buffer[bufidx];
|
||||
undenormalise(bufout);
|
||||
|
||||
output = -input + bufout;
|
||||
buffer[bufidx] = input + (bufout*feedback);
|
||||
|
||||
if(++bufidx>=bufsize) bufidx = 0;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
// Reverb model tuning values
|
||||
|
||||
const int numcombs = 8;
|
||||
const int numallpasses = 4;
|
||||
const float muted = 0;
|
||||
const float fixedgain = 0.015f;
|
||||
const float scalewet = 3;
|
||||
const float scaledry = 2;
|
||||
const float scaledamp = 0.4f;
|
||||
const float scaleroom = 0.28f;
|
||||
const float offsetroom = 0.7f;
|
||||
const float initialroom = 0.5f;
|
||||
const float initialdamp = 0.5f;
|
||||
const float initialwet = 1/scalewet;
|
||||
const float initialdry = 0;
|
||||
const float initialwidth = 1;
|
||||
const float initialmode = 0;
|
||||
const float freezemode = 0.5f;
|
||||
const int stereospread = 23;
|
||||
|
||||
// These values assume 44.1KHz sample rate
|
||||
// they will probably be OK for 48KHz sample rate
|
||||
// but would need scaling for 96KHz (or other) sample rates.
|
||||
// The values were obtained by listening tests.
|
||||
const int combtuningL1 = 1116;
|
||||
const int combtuningR1 = 1116+stereospread;
|
||||
const int combtuningL2 = 1188;
|
||||
const int combtuningR2 = 1188+stereospread;
|
||||
const int combtuningL3 = 1277;
|
||||
const int combtuningR3 = 1277+stereospread;
|
||||
const int combtuningL4 = 1356;
|
||||
const int combtuningR4 = 1356+stereospread;
|
||||
const int combtuningL5 = 1422;
|
||||
const int combtuningR5 = 1422+stereospread;
|
||||
const int combtuningL6 = 1491;
|
||||
const int combtuningR6 = 1491+stereospread;
|
||||
const int combtuningL7 = 1557;
|
||||
const int combtuningR7 = 1557+stereospread;
|
||||
const int combtuningL8 = 1617;
|
||||
const int combtuningR8 = 1617+stereospread;
|
||||
const int allpasstuningL1 = 556;
|
||||
const int allpasstuningR1 = 556+stereospread;
|
||||
const int allpasstuningL2 = 441;
|
||||
const int allpasstuningR2 = 441+stereospread;
|
||||
const int allpasstuningL3 = 341;
|
||||
const int allpasstuningR3 = 341+stereospread;
|
||||
const int allpasstuningL4 = 225;
|
||||
const int allpasstuningR4 = 225+stereospread;
|
||||
|
||||
|
||||
// Reverb model declaration
|
||||
|
||||
class revmodel
|
||||
{
|
||||
public:
|
||||
revmodel();
|
||||
void mute();
|
||||
void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
|
||||
void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
|
||||
void setroomsize(float value);
|
||||
float getroomsize();
|
||||
void setdamp(float value);
|
||||
float getdamp();
|
||||
void setwet(float value);
|
||||
float getwet();
|
||||
void setdry(float value);
|
||||
float getdry();
|
||||
void setwidth(float value);
|
||||
float getwidth();
|
||||
void setmode(float value);
|
||||
float getmode();
|
||||
private:
|
||||
void update();
|
||||
private:
|
||||
float gain;
|
||||
float roomsize,roomsize1;
|
||||
float damp,damp1;
|
||||
float wet,wet1,wet2;
|
||||
float dry;
|
||||
float width;
|
||||
float mode;
|
||||
|
||||
// The following are all declared inline
|
||||
// to remove the need for dynamic allocation
|
||||
// with its subsequent error-checking messiness
|
||||
|
||||
// Comb filters
|
||||
comb combL[numcombs];
|
||||
comb combR[numcombs];
|
||||
|
||||
// Allpass filters
|
||||
allpass allpassL[numallpasses];
|
||||
allpass allpassR[numallpasses];
|
||||
|
||||
// Buffers for the combs
|
||||
float bufcombL1[combtuningL1];
|
||||
float bufcombR1[combtuningR1];
|
||||
float bufcombL2[combtuningL2];
|
||||
float bufcombR2[combtuningR2];
|
||||
float bufcombL3[combtuningL3];
|
||||
float bufcombR3[combtuningR3];
|
||||
float bufcombL4[combtuningL4];
|
||||
float bufcombR4[combtuningR4];
|
||||
float bufcombL5[combtuningL5];
|
||||
float bufcombR5[combtuningR5];
|
||||
float bufcombL6[combtuningL6];
|
||||
float bufcombR6[combtuningR6];
|
||||
float bufcombL7[combtuningL7];
|
||||
float bufcombR7[combtuningR7];
|
||||
float bufcombL8[combtuningL8];
|
||||
float bufcombR8[combtuningR8];
|
||||
|
||||
// Buffers for the allpasses
|
||||
float bufallpassL1[allpasstuningL1];
|
||||
float bufallpassR1[allpasstuningR1];
|
||||
float bufallpassL2[allpasstuningL2];
|
||||
float bufallpassR2[allpasstuningR2];
|
||||
float bufallpassL3[allpasstuningL3];
|
||||
float bufallpassR3[allpasstuningR3];
|
||||
float bufallpassL4[allpasstuningL4];
|
||||
float bufallpassR4[allpasstuningR4];
|
||||
};
|
||||
|
||||
#endif//_freeverb_h_
|
||||
|
||||
//ends
|
||||
|
135
backends/midi/mt32/mt32.cpp
Normal file
135
backends/midi/mt32/mt32.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2001-2004 The ScummVM project
|
||||
*
|
||||
* YM2612 tone generation code written by Tomoaki Hayasaka.
|
||||
* Used under the terms of the GNU General Public License.
|
||||
* Adpated to ScummVM by Jamieson Christian.
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* $Header$
|
||||
*/
|
||||
|
||||
#include "backends/midi/emumidi.h"
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/file.h"
|
||||
|
||||
#include "backends/midi/mt32/synth.h"
|
||||
|
||||
class MidiDriver_MT32 : public MidiDriver_Emulated {
|
||||
private:
|
||||
CSynthMT32 *_synth;
|
||||
|
||||
const char *rom_path;
|
||||
|
||||
protected:
|
||||
void generate_samples(int16 *buf, int len);
|
||||
|
||||
public:
|
||||
MidiDriver_MT32(SoundMixer *mixer, const char *path);
|
||||
virtual ~MidiDriver_MT32();
|
||||
|
||||
int open();
|
||||
void close();
|
||||
void send(uint32 b);
|
||||
uint32 property(int prop, uint32 param) { return 0; }
|
||||
|
||||
void setPitchBendRange(byte channel, uint range) { }
|
||||
void sysEx(byte *msg, uint16 length);
|
||||
|
||||
MidiChannel *allocateChannel() { return 0; }
|
||||
MidiChannel *getPercussionChannel() { return 0; }
|
||||
|
||||
|
||||
// AudioStream API
|
||||
bool isStereo() const { return true; }
|
||||
int getRate() const { return 32000; }
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// MidiDriver_MT32
|
||||
//
|
||||
////////////////////////////////////////
|
||||
|
||||
|
||||
MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer, const char *path)
|
||||
: MidiDriver_Emulated(mixer) {
|
||||
_synth = new CSynthMT32();
|
||||
rom_path = path;
|
||||
File::addDefaultDirectory(path);
|
||||
}
|
||||
|
||||
MidiDriver_MT32::~MidiDriver_MT32() {
|
||||
delete _synth;
|
||||
}
|
||||
|
||||
int MidiDriver_MT32::open() {
|
||||
SynthProperties prop;
|
||||
|
||||
if (_isOpen)
|
||||
return MERR_ALREADY_OPEN;
|
||||
|
||||
MidiDriver_Emulated::open();
|
||||
|
||||
prop.SampleRate = getRate(); // 32000;
|
||||
prop.UseReverb = true;
|
||||
prop.UseDefault = true;
|
||||
//prop.RevType = 0;
|
||||
//prop.RevTime = 5;
|
||||
//prop.RevLevel = 3;
|
||||
|
||||
_synth->ClassicOpen(rom_path, prop);
|
||||
|
||||
_mixer->setupPremix(this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_MT32::send(uint32 b) {
|
||||
_synth->PlayMsg(b);
|
||||
}
|
||||
|
||||
void MidiDriver_MT32::sysEx(byte *msg, uint16 length) {
|
||||
_synth->PlaySysex(msg, length);
|
||||
}
|
||||
|
||||
void MidiDriver_MT32::close() {
|
||||
if (!_isOpen)
|
||||
return;
|
||||
_isOpen = false;
|
||||
|
||||
// Detach the premix callback handler
|
||||
_mixer->setupPremix(0);
|
||||
|
||||
_synth->Close();
|
||||
}
|
||||
|
||||
void MidiDriver_MT32::generate_samples(int16 *data, int len) {
|
||||
_synth->MT32_CallBack((Bit8u *)data, len, _mixer->getMusicVolume());
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// MidiDriver_MT32 factory
|
||||
//
|
||||
////////////////////////////////////////
|
||||
|
||||
MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer, const char *path) {
|
||||
return new MidiDriver_MT32(mixer, path);
|
||||
}
|
591
backends/midi/mt32/partial.cpp
Normal file
591
backends/midi/mt32/partial.cpp
Normal file
@ -0,0 +1,591 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2004 The ScummVM project
|
||||
* Based on Tristan's conversion of Canadacow's code
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* $Header$
|
||||
*
|
||||
*/
|
||||
|
||||
// Implementation of the MT-32 partial class
|
||||
|
||||
#include "backends/midi/mt32/synth.h"
|
||||
#include "backends/midi/mt32/partial.h"
|
||||
|
||||
INLINE void CPartialMT32::generateSamples(Bit16s * partialBuf, long length) {
|
||||
if (!isActive) return;
|
||||
if (alreadyOutputed) return;
|
||||
|
||||
|
||||
alreadyOutputed = true;
|
||||
|
||||
// Generate samples
|
||||
|
||||
int r;
|
||||
int i;
|
||||
Bit32s envval, ampval, filtval;
|
||||
soundaddr *pOff = &partCache->partialOff;
|
||||
int noteval = partCache->keyedval;
|
||||
for(i=0;i<length;i++) {
|
||||
Bit32s ptemp = 0;
|
||||
|
||||
if(partCache->envs[AMPENV].sustaining) {
|
||||
ampval = partCache->ampEnvCache;
|
||||
} else {
|
||||
if(partCache->envs[AMPENV].count<=0) {
|
||||
|
||||
ampval = getAmpEnvelope(partCache,tmppoly);
|
||||
isActive = partCache->playPartial;
|
||||
//TODO: check what is going on here
|
||||
// if (ampval < 0) ampval = 0;
|
||||
//printf("%d %d\n", (int)ampval, (int)isActive);
|
||||
if(!isActive) {
|
||||
tmppoly->partActive[timbreNum] = false;
|
||||
tmppoly->isActive = tmppoly->partActive[0] || tmppoly->partActive[1] || tmppoly->partActive[2] || tmppoly->partActive[3];
|
||||
}
|
||||
if(ampval>=128) ampval = 127;
|
||||
|
||||
ampval = amptable[ampval];
|
||||
int tmpvel = tmppoly->vel;
|
||||
if(tcache->ampenvdir==1) tmpvel = 127-tmpvel;
|
||||
ampval = (ampval * ampveltable[tmpvel][(int)tcache->ampEnv.velosens]) >> 8;
|
||||
//if(partCache->envs[AMPENV].sustaining)
|
||||
partCache->ampEnvCache = ampval;
|
||||
} else {
|
||||
ampval = partCache->ampEnvCache;
|
||||
}
|
||||
--partCache->envs[AMPENV].count;
|
||||
}
|
||||
|
||||
int delta = 0x10707;
|
||||
|
||||
// Calculate Pitch envelope
|
||||
int lfoat = 0x1000;
|
||||
int pdep;
|
||||
if(partCache->pitchsustain) {
|
||||
// Calculate LFO position
|
||||
// LFO does not kick in completely until pitch envelope sustains
|
||||
|
||||
if(tcache->lfodepth>0) {
|
||||
partCache->lfopos++;
|
||||
|
||||
if(partCache->lfopos>=tcache->lfoperiod) partCache->lfopos = 0;
|
||||
int lfoatm = (partCache->lfopos << 16) / tcache->lfoperiod;
|
||||
|
||||
int lfoatr = sintable[lfoatm];
|
||||
|
||||
lfoat = lfoptable[tcache->lfodepth][lfoatr];
|
||||
}
|
||||
pdep = partCache->pitchEnvCache;
|
||||
|
||||
|
||||
} else {
|
||||
envval = getPitchEnvelope(partCache,tmppoly);
|
||||
int pd=tcache->pitchEnv.depth;
|
||||
pdep = penvtable[pd][envval];
|
||||
if(partCache->pitchsustain) partCache->pitchEnvCache = pdep;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Get waveform - either PCM or synthesized sawtooth or square
|
||||
|
||||
|
||||
if (tcache->PCMPartial) {
|
||||
// PCM partial
|
||||
|
||||
if(!partCache->PCMDone) {
|
||||
|
||||
int addr,len,tmppcm;
|
||||
partialTable *tPCM = &tcache->convPCM;
|
||||
|
||||
if(tPCM->aggSound==-1) {
|
||||
delta = wavtabler[tPCM->pcmnum][noteval];
|
||||
addr = tPCM->addr;
|
||||
len = tPCM->len;
|
||||
|
||||
} else {
|
||||
tmppcm = LoopPatterns[tPCM->aggSound][partCache->looppos];
|
||||
addr = PCM[tmppcm].addr;
|
||||
len = PCM[tmppcm].len;
|
||||
delta = looptabler[tPCM->aggSound][partCache->looppos][noteval];
|
||||
}
|
||||
|
||||
if(ampval>0) {
|
||||
int ra,rb,dist;
|
||||
int taddr;
|
||||
if(delta<0x10000) {
|
||||
// Linear sound interpolation
|
||||
|
||||
taddr = addr + pOff->pcmoffs.pcmplace;
|
||||
ra = romfile[taddr];
|
||||
rb = romfile[taddr+1];
|
||||
|
||||
dist = rb-ra;
|
||||
r = (ra + ((dist * (Bit32s)(pOff->pcmoffs.pcmoffset>>8)) >>8));
|
||||
|
||||
} else {
|
||||
|
||||
//r = romfile[addr + pOff->pcmoffs.pcmplace];
|
||||
// Sound decimation
|
||||
// The right way to do it is to use a lowpass filter on the waveform before selecting
|
||||
// a point. This is too slow. The following appoximates this as fast as possible
|
||||
int idelta = delta >> 16;
|
||||
int ix;
|
||||
taddr = addr + pOff->pcmoffs.pcmplace;
|
||||
ra = 0;
|
||||
for(ix=0;ix<idelta;ix++) {
|
||||
ra += romfile[taddr++];
|
||||
}
|
||||
r = ra / idelta;
|
||||
|
||||
|
||||
|
||||
}
|
||||
} else r = 0;
|
||||
|
||||
ptemp = r;
|
||||
|
||||
if ((pOff->pcmoffs.pcmplace) >= len) {
|
||||
if(tPCM->aggSound==-1) {
|
||||
if(tPCM->loop) {
|
||||
pOff->pcmabs = 0;
|
||||
} else {
|
||||
partCache->PCMDone = true;
|
||||
partCache->playPartial = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
partCache->looppos++;
|
||||
if(LoopPatterns[tPCM->aggSound][partCache->looppos]==-1) partCache->looppos=0;
|
||||
pOff->pcmabs = 0;
|
||||
}
|
||||
//LOG_MSG("tPCM %d loops %d done %d playPart %d", tPCM->pcmnum, tPCM->loop, partCache->PCMDone, partCache->playPartial);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
// Synthesis partial
|
||||
int divis, hdivis, ofs, ofs3, toff;
|
||||
int minorplace;
|
||||
|
||||
int wf = tcache->waveform ;
|
||||
|
||||
divis = divtable[noteval]>>15;
|
||||
|
||||
if(pOff->pcmoffs.pcmplace>=divis) pOff->pcmoffs.pcmplace = (Bit16u)(pOff->pcmoffs.pcmplace-divis);
|
||||
|
||||
toff = pOff->pcmoffs.pcmplace;
|
||||
minorplace = pOff->pcmoffs.pcmoffset >> 14;
|
||||
|
||||
int pa, pb;
|
||||
|
||||
if(ampval>0) {
|
||||
|
||||
filtval = getFiltEnvelope((Bit16s)ptemp,partCache,tmppoly);
|
||||
|
||||
//LOG_MSG("Filtval: %d", filtval);
|
||||
|
||||
if(wf==0) {
|
||||
// Square waveform. Made by combining two pregenerated bandlimited
|
||||
// sawtooth waveforms
|
||||
// Pulse width is not yet correct
|
||||
|
||||
hdivis = divis >> 1;
|
||||
int divmark = smalldivtable[noteval];
|
||||
//int pw = (tcache->pulsewidth * pulsemod[filtval]) >> 8;
|
||||
|
||||
|
||||
ofs = toff % (hdivis);
|
||||
|
||||
ofs3 = toff + ((divmark*pulsetable[partCache->pulsewidth])>>16);
|
||||
ofs3 = ofs3 % (hdivis);
|
||||
|
||||
pa = waveforms[1][noteval][(ofs<<2)+minorplace];
|
||||
pb = waveforms[0][noteval][(ofs3<<2)+minorplace];
|
||||
//ptemp = pa+pb+pulseoffset[tcache->pulsewidth];
|
||||
ptemp = (pa+pb)*4;
|
||||
|
||||
// Non-bandlimited squarewave
|
||||
/*
|
||||
ofs = (divis*pulsetable[tcache->pulsewidth])>>8;
|
||||
if(toff < ofs) {
|
||||
ptemp = 1 * WGAMP;
|
||||
} else {
|
||||
ptemp = -1 * WGAMP;
|
||||
}*/
|
||||
|
||||
|
||||
} else {
|
||||
// Sawtooth. Made by combining the full cosine and half cosine according
|
||||
// to how it looks on the MT-32. What it really does it takes the
|
||||
// square wave and multiplies it by a full cosine
|
||||
// TODO: This area here crashes DosBox due to read overflow
|
||||
int offsetpos = (toff<<2)+minorplace;
|
||||
//int a = 0;
|
||||
if(toff < sawtable[noteval][partCache->pulsewidth]) {
|
||||
while(offsetpos>waveformsize[2][noteval]) {
|
||||
offsetpos-=waveformsize[2][noteval];
|
||||
}
|
||||
ptemp = waveforms[2][noteval][offsetpos];
|
||||
} else {
|
||||
while(offsetpos>waveformsize[3][noteval]) {
|
||||
offsetpos-=waveformsize[3][noteval];
|
||||
}
|
||||
ptemp = waveforms[3][noteval][offsetpos];
|
||||
}
|
||||
ptemp = ptemp *4;
|
||||
|
||||
// ptemp = (int)(sin((double)toff / 100.0) * 100.0);
|
||||
//ptemp = pa;
|
||||
|
||||
// This is the correct way
|
||||
// Seems slow to me (though bandlimited) -- doesn't seem to
|
||||
// sound any better though
|
||||
/*
|
||||
hdivis = divis >> 1;
|
||||
int divmark = smalldivtable[noteval];
|
||||
//int pw = (tcache->pulsewidth * pulsemod[filtval]) >> 8;
|
||||
|
||||
|
||||
ofs = toff % (hdivis);
|
||||
|
||||
ofs3 = toff + ((divmark*pulsetable[tcache->pulsewidth])>>16);
|
||||
ofs3 = ofs3 % (hdivis);
|
||||
|
||||
pa = waveforms[0][noteval][ofs];
|
||||
pb = waveforms[1][noteval][ofs3];
|
||||
ptemp = ((pa+pb) * waveforms[3][noteval][toff]) / WGAMP;
|
||||
ptemp = ptemp *4;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//Very exact filter
|
||||
//ptemp[t] = (int)iir_filter((float)ptemp[t],&partCache->history[t],filtcoeff[filtval][tcache->filtEnv.resonance]);
|
||||
if(filtval>((FILTERGRAN*15)/16)) filtval = ((FILTERGRAN*15)/16);
|
||||
ptemp = (Bit32s)(usefilter)((float)ptemp,&partCache->history[0],filtcoeff[filtval][(int)tcache->filtEnv.resonance], tcache->filtEnv.resonance);
|
||||
} else ptemp = 0;
|
||||
|
||||
//ptemp[t] = Moog1(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance);
|
||||
//ptemp[t] = Moog2(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance);
|
||||
//ptemp[t] = simpleLowpass(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance);
|
||||
|
||||
// Use this to mute analogue synthesis
|
||||
// ptemp = 0;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Build delta for position of next sample
|
||||
/*
|
||||
delta = (delta * tcache->fineshift)>>12;
|
||||
delta = (delta * pdep)>>12;
|
||||
delta = (delta * lfoat)>>12;
|
||||
if(tcache->useBender) delta = (delta * *tmppoly->bendptr)>>12;
|
||||
*/
|
||||
|
||||
// Fix delta code
|
||||
__int64 tdelta = (__int64)delta;
|
||||
tdelta = (tdelta * tcache->fineshift)>>12;
|
||||
tdelta = (tdelta * pdep)>>12;
|
||||
tdelta = (tdelta * lfoat)>>12;
|
||||
if(tcache->useBender) tdelta = (tdelta * *tmppoly->bendptr)>>12;
|
||||
|
||||
// Add calculated delta to our waveform offset
|
||||
pOff->pcmabs+=(int)tdelta;
|
||||
|
||||
// Put volume envelope over generated sample
|
||||
ptemp = (ptemp * ampval) >> 9;
|
||||
ptemp = (ptemp * *tmppoly->volumeptr) >> 7;
|
||||
|
||||
partCache->envs[AMPENV].envpos++;
|
||||
partCache->envs[PITCHENV].envpos++;
|
||||
partCache->envs[FILTENV].envpos++;
|
||||
|
||||
*partialBuf++ = (Bit16s)ptemp;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
INLINE void CPartialMT32::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
|
||||
// Early exit if no need to mix
|
||||
if(tibrePair==NULL) return;
|
||||
|
||||
#if USE_MMX == 0
|
||||
int i;
|
||||
for(i=0;i<len;i++) {
|
||||
Bit32s tmp1 = buf1[i];
|
||||
Bit32s tmp2 = buf2[i];
|
||||
tmp1 += tmp2;
|
||||
buf1[i] = (Bit16s)tmp1;
|
||||
}
|
||||
#else
|
||||
len = (len>>2)+4;
|
||||
#ifdef I_ASM
|
||||
__asm {
|
||||
mov ecx, len
|
||||
mov esi, buf1
|
||||
mov edi, buf2
|
||||
|
||||
mixloop1:
|
||||
movq mm1, [edi]
|
||||
movq mm2, [esi]
|
||||
paddw mm1,mm2
|
||||
movq [esi],mm1
|
||||
add edi,8
|
||||
add esi,8
|
||||
|
||||
dec ecx
|
||||
cmp ecx,0
|
||||
jg mixloop1
|
||||
emms
|
||||
}
|
||||
#else
|
||||
atti386_mixBuffers(buf1, buf2, len);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
INLINE void CPartialMT32::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
|
||||
#if USE_MMX != 2
|
||||
int i;
|
||||
for(i=0;i<len;i++) {
|
||||
float a, b;
|
||||
a = ((float)buf1[i]) / 8192.0;
|
||||
b = ((float)buf2[i]) / 8192.0;
|
||||
a = (a * b) + a;
|
||||
if(a>1.0) a = 1.0;
|
||||
if(a<-1.0) a = -1.0;
|
||||
buf1[i] = (Bit16s)(a * 8192.0);
|
||||
|
||||
//buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i];
|
||||
}
|
||||
#else
|
||||
len = (len>>2)+4;
|
||||
#ifdef I_ASM
|
||||
__asm {
|
||||
mov ecx, len
|
||||
mov esi, buf1
|
||||
mov edi, buf2
|
||||
|
||||
mixloop2:
|
||||
movq mm1, [esi]
|
||||
movq mm2, [edi]
|
||||
movq mm3, mm1
|
||||
pmulhw mm1, mm2
|
||||
paddw mm1,mm3
|
||||
movq [esi],mm1
|
||||
add edi,8
|
||||
add esi,8
|
||||
|
||||
dec ecx
|
||||
cmp ecx,0
|
||||
jg mixloop2
|
||||
emms
|
||||
}
|
||||
#else
|
||||
atti386_mixBuffersRingMix(buf1, buf2, len);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
INLINE void CPartialMT32::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
|
||||
#if USE_MMX != 2
|
||||
int i;
|
||||
for(i=0;i<len;i++) {
|
||||
float a, b;
|
||||
a = ((float)buf1[i]) / 8192.0;
|
||||
b = ((float)buf2[i]) / 8192.0;
|
||||
a *= b;
|
||||
if(a>1.0) a = 1.0;
|
||||
if(a<-1.0) a = -1.0;
|
||||
buf1[i] = (Bit16s)(a * 8192.0);
|
||||
//buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10);
|
||||
}
|
||||
#else
|
||||
len = (len>>2)+4;
|
||||
#ifdef I_ASM
|
||||
__asm {
|
||||
mov ecx, len
|
||||
mov esi, buf1
|
||||
mov edi, buf2
|
||||
|
||||
mixloop3:
|
||||
movq mm1, [esi]
|
||||
movq mm2, [edi]
|
||||
pmulhw mm1, mm2
|
||||
movq [esi],mm1
|
||||
add edi,8
|
||||
add esi,8
|
||||
|
||||
dec ecx
|
||||
cmp ecx,0
|
||||
jg mixloop3
|
||||
emms
|
||||
}
|
||||
#else
|
||||
atti386_mixBuffersRing(buf1, buf2, len);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
INLINE void CPartialMT32::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) {
|
||||
int i,m;
|
||||
m=0;
|
||||
for(i=0;i<len;i++) {
|
||||
*outBuf++ = (*buf1);
|
||||
buf1++;
|
||||
*outBuf++ = (*buf2);
|
||||
buf2++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CPartialMT32::produceOutput(Bit16s * partialBuf, long length) {
|
||||
if (!isActive) return false;
|
||||
if (alreadyOutputed) return false;
|
||||
int i;
|
||||
|
||||
//alreadyOutputed = true;
|
||||
|
||||
memset(&pairBuffer[0],0,length*4);
|
||||
memset(&myBuffer[0],0,length*4);
|
||||
// Check for dependant partial
|
||||
if(tibrePair != NULL) {
|
||||
if ((tibrePair->ownerChan == this->ownerChan) && (!tibrePair->alreadyOutputed)) {
|
||||
tibrePair->generateSamples(pairBuffer,length);
|
||||
}
|
||||
} else {
|
||||
if((useMix!=0) && (useMix != 3)){
|
||||
// Generate noise for parialless ring mix
|
||||
for(i=0;i<length;i++) pairBuffer[i] = smallnoise[i] << 2;
|
||||
}
|
||||
}
|
||||
|
||||
generateSamples(myBuffer, length);
|
||||
/* FILE *fo = fopen("/tmp/samp.raw", "a");
|
||||
for(i = 0; i < length; i++)
|
||||
fwrite(myBuffer + i, 1, 2, fo);
|
||||
fclose(fo);
|
||||
*/
|
||||
Bit16s * p1buf, * p2buf;
|
||||
|
||||
if((partNum==0) || ((partNum==1) && (tibrePair==NULL))) {
|
||||
p1buf = &myBuffer[0];
|
||||
p2buf = &pairBuffer[0];
|
||||
} else {
|
||||
p2buf = &myBuffer[0];
|
||||
p1buf = &pairBuffer[0];
|
||||
}
|
||||
|
||||
// LOG_MSG("useMix: %d", useMix);
|
||||
|
||||
switch(useMix) {
|
||||
case 0:
|
||||
// Standard sound mix
|
||||
mixBuffers(p1buf, p2buf, length);
|
||||
break;
|
||||
case 1:
|
||||
// Ring modulation with sound mix
|
||||
mixBuffersRingMix(p1buf, p2buf, length);
|
||||
break;
|
||||
case 2:
|
||||
// Ring modulation alone
|
||||
mixBuffersRing(p1buf, p2buf, length);
|
||||
break;
|
||||
case 3:
|
||||
// Stereo mixing. One partial to one channel, one to another.
|
||||
mixBuffersStereo(p1buf, p2buf, partialBuf, length);
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
mixBuffers(p1buf, p2buf, length);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
int m;
|
||||
m = 0;
|
||||
Bit16s leftvol, rightvol;
|
||||
if (!tmppoly->isRy) {
|
||||
leftvol = tmppoly->pansetptr->leftvol;
|
||||
rightvol = tmppoly->pansetptr->rightvol;
|
||||
} else {
|
||||
leftvol = (Bit16s)drumPan[tmppoly->pcmnum][0];
|
||||
rightvol = (Bit16s)drumPan[tmppoly->pcmnum][1];
|
||||
}
|
||||
|
||||
#if USE_MMX == 0
|
||||
for(i=0;i<length;i++) {
|
||||
partialBuf[m] = (Bit16s)(((Bit32s)p1buf[i] * (Bit32s)leftvol) >> 16);
|
||||
m++;
|
||||
partialBuf[m] = (Bit16s)(((Bit32s)p1buf[i] * (Bit32s)rightvol) >> 16);
|
||||
m++;
|
||||
}
|
||||
#else
|
||||
long quadlen = (length >> 1)+2;
|
||||
#ifdef I_ASM
|
||||
__asm {
|
||||
mov ecx,quadlen
|
||||
mov ax, leftvol
|
||||
shl eax,16
|
||||
mov ax, rightvol
|
||||
movd mm1, eax
|
||||
movd mm2, eax
|
||||
psllq mm1, 32
|
||||
por mm1, mm2
|
||||
mov edi, partialBuf
|
||||
mov esi, p1buf
|
||||
mmxloop1:
|
||||
mov bx, [esi]
|
||||
add esi,2
|
||||
mov dx, [esi]
|
||||
add esi,2
|
||||
|
||||
mov ax, dx
|
||||
shl eax, 16
|
||||
mov ax, dx
|
||||
movd mm2,eax
|
||||
psllq mm2, 32
|
||||
mov ax, bx
|
||||
shl eax, 16
|
||||
mov ax, bx
|
||||
movd mm3,eax
|
||||
por mm2,mm3
|
||||
|
||||
pmulhw mm2, mm1
|
||||
movq [edi], mm2
|
||||
add edi, 8
|
||||
|
||||
dec ecx
|
||||
cmp ecx,0
|
||||
jg mmxloop1
|
||||
emms
|
||||
}
|
||||
#else
|
||||
atti386_PartProductOutput(quadlen, leftvol, rightvol, partialBuf, p1buf);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
115
backends/midi/mt32/partial.h
Normal file
115
backends/midi/mt32/partial.h
Normal file
@ -0,0 +1,115 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2004 The ScummVM project
|
||||
* Based on Tristan's conversion of Canadacow's code
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* $Header$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __CPARTIALMT32_H__
|
||||
#define __CPARTIALMT32_H__
|
||||
|
||||
#include "backends/midi/mt32/structures.h"
|
||||
|
||||
// Class definition of MT-32 partials. 32 in all.
|
||||
class CPartialMT32 {
|
||||
private:
|
||||
int useMix;
|
||||
int partNum;
|
||||
|
||||
int pN;
|
||||
|
||||
|
||||
|
||||
|
||||
Bit16s myBuffer[2048];
|
||||
// For temporary output of paired buffer
|
||||
Bit16s pairBuffer[2048];
|
||||
|
||||
void mixBuffers(Bit16s * buf1, Bit16s * buf2, int len);
|
||||
void mixBuffersRingMix(Bit16s * buf1, Bit16s * buf2, int len);
|
||||
void mixBuffersRing(Bit16s * buf1, Bit16s * buf2, int len);
|
||||
void mixBuffersStereo(Bit16s * buf1, Bit16s * buf2, Bit16s * outBuf, int len);
|
||||
|
||||
|
||||
public:
|
||||
patchCache *tcache;
|
||||
patchCache cachebackup[4];
|
||||
|
||||
//FILE *fp;
|
||||
//FILE *fp2;
|
||||
|
||||
dpoly::partialStatus *partCache;
|
||||
|
||||
CPartialMT32 *tibrePair;
|
||||
bool isActive;
|
||||
bool alreadyOutputed;
|
||||
int ownerChan;
|
||||
Bit64s age;
|
||||
int timbreNum;
|
||||
dpoly *tmppoly;
|
||||
|
||||
CPartialMT32(int partialNum) {
|
||||
|
||||
isActive = false;
|
||||
pN = partialNum;
|
||||
|
||||
/*
|
||||
sprintf(buffer, "partial%d.raw",pN);
|
||||
fp = fopen(buffer,"wb");
|
||||
|
||||
sprintf(buffer, "partial%dx.raw",pN);
|
||||
fp2 = fopen(buffer,"wb");
|
||||
*/
|
||||
|
||||
|
||||
};
|
||||
|
||||
void startPartial(dpoly *usePoly, patchCache *useCache, dpoly::partialStatus * usePart, CPartialMT32 * pairPart, int mixType, int num, int ownChan, int timNum) {
|
||||
|
||||
//LOG_MSG("Starting partial %d for %d", num, ownChan);
|
||||
tmppoly = usePoly;
|
||||
tcache = useCache;
|
||||
partCache = usePart;
|
||||
tibrePair = pairPart;
|
||||
isActive = true;
|
||||
useMix = mixType;
|
||||
partNum = num;
|
||||
age = 0;
|
||||
ownerChan = ownChan;
|
||||
alreadyOutputed = false;
|
||||
timbreNum = timNum;
|
||||
memset(usePart->history,0,sizeof(usePart->history));
|
||||
|
||||
}
|
||||
|
||||
void stopPartial(void) { isActive = false; }
|
||||
|
||||
|
||||
// Returns true only if data written to buffer
|
||||
// This function (unline the one below it) returns processed stereo samples
|
||||
// made from combining this single partial with its pair, if it has one.
|
||||
bool produceOutput(Bit16s * partialBuf, long length);
|
||||
|
||||
// This function produces mono sample output of the specific partial
|
||||
void generateSamples(Bit16s * partialBuf, long length);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
780
backends/midi/mt32/structures.h
Normal file
780
backends/midi/mt32/structures.h
Normal file
@ -0,0 +1,780 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2004 The ScummVM project
|
||||
* Based on Tristan's conversion of Canadacow's code
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* $Header$
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined __MT32STRUCTURES_H__
|
||||
#define __MT32STRUCTURES_H__
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef unsigned __int64 Bit64u;
|
||||
typedef signed __int64 Bit64s;
|
||||
#else
|
||||
typedef unsigned long long Bit64u;
|
||||
typedef signed long long Bit64s;
|
||||
#endif
|
||||
typedef unsigned int Bit32u;
|
||||
typedef signed int Bit32s;
|
||||
typedef unsigned short int Bit16u;
|
||||
typedef signed short int Bit16s;
|
||||
typedef unsigned char Bit8u;
|
||||
typedef signed char Bit8s;
|
||||
|
||||
// The occurences of __int64 should be changed to Bit64s
|
||||
#define __int64 Bit64u
|
||||
|
||||
#define INLINE
|
||||
|
||||
|
||||
static inline void LOG_MSG(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stdout, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stdout, "\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW__))
|
||||
|
||||
#define ALIGN_PACKED
|
||||
|
||||
#else
|
||||
|
||||
//#define ALIGN_PACKED __attribute__ ((__packed__))
|
||||
#define ALIGN_PACKED __attribute__ ((aligned (1)))
|
||||
|
||||
#ifdef HAVE_X86
|
||||
#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value))
|
||||
#define cpuid_flag (1 << 21)
|
||||
|
||||
static inline bool atti386_DetectCPUID()
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
/* is there a cpuid */
|
||||
result = cpuid_flag; /* set test */
|
||||
eflag(result);
|
||||
if (!(result & cpuid_flag))
|
||||
return false;
|
||||
|
||||
result = 0; /* clear test */
|
||||
eflag(result);
|
||||
if (result & cpuid_flag)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool atti386_DetectSIMD()
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
if (atti386_DetectCPUID() == false)
|
||||
return false;
|
||||
|
||||
/* check cpuid */
|
||||
__asm__ __volatile__(
|
||||
"movl $1, %%eax \n" \
|
||||
"cpuid \n" \
|
||||
"movl %%edx, %0 \n" \
|
||||
: "=r"(result) : : "eax", "ebx", "ecx", "edx");
|
||||
|
||||
if (result & (1 << 25))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool atti386_Detect3DNow()
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
if (atti386_DetectCPUID() == false)
|
||||
return false;
|
||||
|
||||
/* get cpuid */
|
||||
__asm__ __volatile__(
|
||||
"movl $0x80000001, %%eax \n" \
|
||||
"cpuid \n" \
|
||||
"movl %%edx, %0 \n" \
|
||||
: "=r"(result) : : "eax", "ebx", "ecx", "edx");
|
||||
|
||||
if (result & 0x80000000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr)
|
||||
{
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"pushl %1 \n" \
|
||||
"pushl %2 \n" \
|
||||
"movss 0(%0), %%xmm1 \n" \
|
||||
"movups 0(%1), %%xmm2 \n" \
|
||||
"movlps 0(%2), %%xmm3 \n" \
|
||||
" \n" \
|
||||
"shufps $0x44, %%xmm3, %%xmm3 \n" \
|
||||
" \n" \
|
||||
"mulps %%xmm3, %%xmm2 \n" \
|
||||
" \n" \
|
||||
"subss %%xmm2, %%xmm1 \n" \
|
||||
"shufps $0x39, %%xmm2, %%xmm2 \n" \
|
||||
"subss %%xmm2, %%xmm1 \n" \
|
||||
" \n" \
|
||||
"movss %%xmm1, 0(%2) \n" \
|
||||
" \n" \
|
||||
"shufps $0x39, %%xmm2, %%xmm2 \n" \
|
||||
"addss %%xmm2, %%xmm1 \n" \
|
||||
" \n" \
|
||||
"shufps $0x39, %%xmm2, %%xmm2 \n" \
|
||||
"addss %%xmm2, %%xmm1 \n" \
|
||||
" \n" \
|
||||
"movss %%xmm3, 4(%2) \n" \
|
||||
" \n" \
|
||||
"addl $16, %1 \n" \
|
||||
"addl $8, %2 \n" \
|
||||
" \n" \
|
||||
"movups 0(%1), %%xmm2 \n" \
|
||||
" \n" \
|
||||
"movlps 0(%2), %%xmm3 \n" \
|
||||
"shufps $0x44, %%xmm3, %%xmm3 \n" \
|
||||
" \n" \
|
||||
"mulps %%xmm3, %%xmm2 \n" \
|
||||
" \n" \
|
||||
"subss %%xmm2, %%xmm1 \n" \
|
||||
"shufps $0x39, %%xmm2, %%xmm2 \n" \
|
||||
"subss %%xmm2, %%xmm1 \n" \
|
||||
" \n" \
|
||||
"movss %%xmm1, 0(%2) \n" \
|
||||
" \n" \
|
||||
"shufps $0x39, %%xmm2, %%xmm2 \n" \
|
||||
"addss %%xmm2, %%xmm1 \n" \
|
||||
" \n" \
|
||||
"shufps $0x39, %%xmm2, %%xmm2 \n" \
|
||||
"addss %%xmm2, %%xmm1 \n" \
|
||||
" \n" \
|
||||
"movss %%xmm3, 4(%2) \n" \
|
||||
"movss %%xmm1, 0(%0) \n" \
|
||||
"popl %2 \n" \
|
||||
"popl %1 \n" \
|
||||
: : "r"(output), "r"(coef_ptr), "r"(hist1_ptr)
|
||||
: "xmm1", "xmm2", "xmm3", "memory");
|
||||
|
||||
return(*output);
|
||||
}
|
||||
|
||||
static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr)
|
||||
{
|
||||
float tmp;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"movq %0, %%mm1 \n" \
|
||||
" \n" \
|
||||
"movl %1, %%ebx \n" \
|
||||
"movq 0(%%ebx), %%mm2 \n" \
|
||||
" \n" \
|
||||
"movl %2, %%eax; \n" \
|
||||
"movq 0(%%eax), %%mm3 \n" \
|
||||
" \n" \
|
||||
"pfmul %%mm3, %%mm2 \n" \
|
||||
"pfsub %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"psrlq $32, %%mm2 \n" \
|
||||
"pfsub %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"movd %%mm1, %3 \n" \
|
||||
" \n" \
|
||||
"addl $8, %%ebx \n" \
|
||||
"movq 0(%%ebx), %%mm2 \n" \
|
||||
"movq 0(%%eax), %%mm3 \n" \
|
||||
" \n" \
|
||||
"pfmul %%mm3, %%mm2 \n" \
|
||||
"pfadd %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"psrlq $32, %%mm2 \n" \
|
||||
"pfadd %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"pushl %3 \n" \
|
||||
"popl 0(%%eax) \n" \
|
||||
" \n" \
|
||||
"movd %%mm3, 4(%%eax) \n" \
|
||||
" \n" \
|
||||
"addl $8, %%ebx \n" \
|
||||
"addl $8, %%eax \n" \
|
||||
" \n" \
|
||||
"movq 0(%%ebx), %%mm2 \n" \
|
||||
"movq 0(%%eax), %%mm3 \n" \
|
||||
" \n" \
|
||||
"pfmul %%mm3, %%mm2 \n" \
|
||||
"pfsub %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"psrlq $32, %%mm2 \n" \
|
||||
"pfsub %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"movd %%mm1, %3 \n" \
|
||||
" \n" \
|
||||
"addl $8, %%ebx \n" \
|
||||
"movq 0(%%ebx), %%mm2 \n" \
|
||||
"movq 0(%%eax), %%mm3 \n" \
|
||||
" \n" \
|
||||
"pfmul %%mm3, %%mm2 \n" \
|
||||
"pfadd %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"psrlq $32, %%mm2 \n" \
|
||||
"pfadd %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"pushl %3 \n" \
|
||||
"popl 0(%%eax) \n" \
|
||||
"movd %%mm3, 4(%%eax) \n" \
|
||||
" \n" \
|
||||
"movd %%mm1, %0 \n" \
|
||||
"femms \n" \
|
||||
: "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp)
|
||||
: "eax", "ebx", "mm1", "mm2", "mm3", "memory");
|
||||
|
||||
return(output);
|
||||
}
|
||||
|
||||
static inline float atti386_iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"movl %0, %%ecx \n" \
|
||||
"movw %1, %%ax \n" \
|
||||
"shll $16, %%eax \n" \
|
||||
"movw %1, %%ax \n" \
|
||||
"movd %%eax, %%mm3 \n" \
|
||||
"movd %%eax, %%mm2 \n" \
|
||||
"psllq $32, %%mm3 \n" \
|
||||
"por %%mm2, %%mm3 \n" \
|
||||
"movl %2, %%esi \n" \
|
||||
"movl %3, %%edi \n" \
|
||||
"1: \n" \
|
||||
"movq 0(%%esi), %%mm1 \n" \
|
||||
"movq 0(%%edi), %%mm2 \n" \
|
||||
"pmulhw %%mm3, %%mm1 \n" \
|
||||
"paddw %%mm2, %%mm1 \n" \
|
||||
"movq %%mm1, 0(%%edi) \n" \
|
||||
" \n" \
|
||||
"addl $8, %%esi \n" \
|
||||
"addl $8, %%edi \n" \
|
||||
" \n" \
|
||||
"decl %%ecx \n" \
|
||||
"cmpl $0, %%ecx \n" \
|
||||
"jg 1b \n" \
|
||||
"emms \n" \
|
||||
: : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd)
|
||||
: "eax", "ecx", "edi", "esi", "mm1", "mm2", "mm3", "memory");
|
||||
}
|
||||
|
||||
// FIXME: This is buggy
|
||||
static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"movl %4, %%ecx \n" \
|
||||
"shrl $1, %%ecx \n" \
|
||||
"addl $4, %%ecx \n" \
|
||||
"pushl %%ecx \n" \
|
||||
" \n" \
|
||||
"movl %0, %%esi \n" \
|
||||
"movups 0(%%esi), %%xmm1 \n" \
|
||||
" \n" \
|
||||
"movl %1, %%esi \n" \
|
||||
"movl %2, %%edi \n" \
|
||||
"1: \n" \
|
||||
"xorl %%eax, %%eax \n" \
|
||||
"movw 0(%1), %%ax \n" \
|
||||
"cwde \n" \
|
||||
"incl %1 \n" \
|
||||
"incl %1 \n" \
|
||||
"movd %%eax, %%mm1 \n" \
|
||||
"psrlq $32, %%mm1 \n" \
|
||||
"movw 0(%1), %%ax \n" \
|
||||
"incl %1 \n" \
|
||||
"incl %1 \n" \
|
||||
"movd %%eax, %%mm2 \n" \
|
||||
"por %%mm2, %%mm1 \n" \
|
||||
" \n" \
|
||||
"decl %%ecx \n" \
|
||||
"jnz 1b \n" \
|
||||
" \n" \
|
||||
"popl %%ecx \n" \
|
||||
"movl %1, %%esi \n" \
|
||||
"movl %3, %%edi \n" \
|
||||
"incl %%esi \n" \
|
||||
"2: \n" \
|
||||
"decl %%ecx \n" \
|
||||
"jnz 2b \n" \
|
||||
: : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len)
|
||||
: "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory");
|
||||
}
|
||||
|
||||
static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"movl %0, %%ecx \n" \
|
||||
"movl %1, %%esi \n" \
|
||||
"movl %2, %%edi \n" \
|
||||
"1: \n" \
|
||||
"movq 0(%%edi), %%mm1 \n" \
|
||||
"movq 0(%%esi), %%mm2 \n" \
|
||||
"paddw %%mm2, %%mm1 \n" \
|
||||
"movq %%mm1, 0(%%esi) \n" \
|
||||
"addl $8, %%edi \n" \
|
||||
"addl $8, %%esi \n" \
|
||||
"decl %%ecx \n" \
|
||||
"cmpl $0, %%ecx \n" \
|
||||
"jg 1b \n" \
|
||||
"emms \n" \
|
||||
: : "g"(len), "g"(buf1), "g"(buf2)
|
||||
: "ecx", "edi", "esi", "mm1", "mm2", "memory");
|
||||
}
|
||||
|
||||
static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"movl %0, %%ecx \n" \
|
||||
"movl %1, %%esi \n" \
|
||||
"movl %2, %%edi \n" \
|
||||
"1: \n" \
|
||||
"movq 0(%%esi), %%mm1 \n" \
|
||||
"movq 0(%%edi), %%mm2 \n" \
|
||||
"movq %%mm1, %%mm3 \n" \
|
||||
"pmulhw %%mm2, %%mm1 \n" \
|
||||
"paddw %%mm3, %%mm1 \n" \
|
||||
"movq %%mm1, 0(%%esi) \n" \
|
||||
"addl $8, %%edi \n" \
|
||||
"addl $8, %%esi \n" \
|
||||
"decl %%ecx \n" \
|
||||
"cmpl $0, %%ecx \n" \
|
||||
"jg 1b \n" \
|
||||
"emms \n" \
|
||||
: : "g"(len), "g"(buf1), "g"(buf2)
|
||||
: "ecx", "edi", "esi", "mm1", "mm2", "mm3", "memory");
|
||||
}
|
||||
|
||||
static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"movl %0, %%ecx \n" \
|
||||
"movl %1, %%esi \n" \
|
||||
"movl %2, %%edi \n" \
|
||||
"1: \n" \
|
||||
"movq 0(%%esi), %%mm1 \n" \
|
||||
"movq 0(%%edi), %%mm2 \n" \
|
||||
"pmulhw %%mm2, %%mm1 \n" \
|
||||
"movq %%mm1, 0(%%esi) \n" \
|
||||
"addl $8, %%edi \n" \
|
||||
"addl $8, %%esi \n" \
|
||||
"decl %%ecx \n" \
|
||||
"cmpl $0, %%ecx \n" \
|
||||
"jg 1b \n" \
|
||||
"emms \n" \
|
||||
: : "g"(len), "g"(buf1), "g"(buf2)
|
||||
: "ecx", "edi", "esi", "mm1", "mm2", "memory");
|
||||
}
|
||||
|
||||
static inline void atti386_PartProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol,
|
||||
Bit16s *partialBuf, Bit16s *p1buf)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"movl %0, %%ecx \n" \
|
||||
"movw %1, %%ax \n" \
|
||||
"shll $16, %%eax \n" \
|
||||
"movw %2, %%ax \n" \
|
||||
"movd %%eax, %%mm1 \n" \
|
||||
"movd %%eax, %%mm2 \n" \
|
||||
"psllq $32, %%mm1 \n" \
|
||||
"por %%mm2, %%mm1 \n" \
|
||||
"movl %3, %%edi \n" \
|
||||
"movl %4, %%esi \n" \
|
||||
"1: \n" \
|
||||
"movw 0(%%esi), %%bx \n" \
|
||||
"addl $2, %%esi \n" \
|
||||
"movw 0(%%esi), %%dx \n" \
|
||||
"addl $2, %%esi \n" \
|
||||
"" \
|
||||
"movw %%dx, %%ax \n" \
|
||||
"shll $16, %%eax \n" \
|
||||
"movw %%dx, %%ax \n" \
|
||||
"movd %%eax, %%mm2 \n" \
|
||||
"psllq $32, %%mm2 \n" \
|
||||
"movw %%bx, %%ax \n" \
|
||||
"shll $16, %%eax \n" \
|
||||
"movw %%bx, %%ax \n" \
|
||||
"movd %%eax, %%mm3 \n" \
|
||||
"por %%mm3, %%mm2 \n" \
|
||||
"" \
|
||||
"pmulhw %%mm1, %%mm2 \n" \
|
||||
"movq %%mm2, 0(%%edi) \n" \
|
||||
"addl $8, %%edi \n" \
|
||||
"" \
|
||||
"decl %%ecx \n" \
|
||||
"cmpl $0, %%ecx \n" \
|
||||
"jg 1b \n" \
|
||||
"emms \n" \
|
||||
: :"g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf)
|
||||
: "eax", "ebx", "ecx", "edx", "edi", "esi", "mm1", "mm2", "mm3", "memory");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
extern bool enabled3DNow;
|
||||
extern bool enabledSSE;
|
||||
|
||||
#pragma pack(1)
|
||||
struct timbreParam {
|
||||
struct commonParam {
|
||||
char name[10];
|
||||
char pstruct12; // 1&2 0-12 (1-13)
|
||||
char pstruct34; // #3&4 0-12 (1-13)
|
||||
char pmute; // 0-15 (0000-1111)
|
||||
char nosustain; // 0-1(Normal, No sustain)
|
||||
} ALIGN_PACKED common;
|
||||
|
||||
struct partialParam {
|
||||
struct wgParam {
|
||||
char coarse; // 0-96 (C1,C#1-C9)
|
||||
char fine; // 0-100 (-50 - +50)
|
||||
char keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
|
||||
char bender; // 0,1 (ON/OFF)
|
||||
char waveform; // 0-1 (SQU/SAW)
|
||||
char pcmwave; // 0-127 (1-128)
|
||||
char pulsewid; // 0-100
|
||||
char pwvelo; // 0-14 (-7 - +7)
|
||||
} ALIGN_PACKED wg;
|
||||
|
||||
struct envParam {
|
||||
char depth; // 0-10
|
||||
char sensitivity; // 1-100
|
||||
char timekeyfollow; // 0-4
|
||||
char time[4]; // 1-100
|
||||
char level[5]; // 1-100 (-50 - +50)
|
||||
} ALIGN_PACKED env;
|
||||
|
||||
struct lfoParam {
|
||||
char rate; // 0-100
|
||||
char depth; // 0-100
|
||||
char modsense; // 0-100
|
||||
} ALIGN_PACKED lfo;
|
||||
|
||||
struct tvfParam {
|
||||
char cutoff; // 0-100
|
||||
char resonance; // 0-30
|
||||
char keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
|
||||
char biaspoint; // 0-127 (<1A-<7C >1A-7C)
|
||||
char biaslevel; // 0-14 (-7 - +7)
|
||||
char envdepth; // 0-100
|
||||
char envsense; // 0-100
|
||||
char envdkf; // DEPTH KEY FOLL0W 0-4
|
||||
char envtkf; // TIME KEY FOLLOW 0-4
|
||||
char envtime[5]; // 1-100
|
||||
char envlevel[4]; // 1-100
|
||||
} ALIGN_PACKED tvf;
|
||||
|
||||
struct tvaParam {
|
||||
char level; // 0-100
|
||||
char velosens; // 0-100
|
||||
char biaspoint1; // 0-127 (<1A-<7C >1A-7C)
|
||||
char biaslevel1; // 0-12 (-12 - 0)
|
||||
char biaspoint2; // 0-127 (<1A-<7C >1A-7C)
|
||||
char biaslevel2; // 0-12 (-12 - 0)
|
||||
char envtkf; // TIME KEY FOLLOW 0-4
|
||||
char envvkf; // VELOS KEY FOLL0W 0-4
|
||||
char envtime[5]; // 1-100
|
||||
char envlevel[4]; // 1-100
|
||||
} ALIGN_PACKED tva;
|
||||
|
||||
} ALIGN_PACKED partial[4];
|
||||
//char dummy[20];
|
||||
} ALIGN_PACKED;
|
||||
|
||||
struct memParams {
|
||||
struct patchTemp {
|
||||
char timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
|
||||
char timbreNum; // TIMBRE NUMBER 0-63
|
||||
char keyShift; // KEY SHIFT 0-48 (-24 - +24)
|
||||
char fineTune; // FINE TUNE 0-100 (-50 - +50)
|
||||
char benderRange; // BENDER RANGE 0-24
|
||||
char assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
|
||||
char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
|
||||
char dummy; // (DUMMY)
|
||||
char outlevel; // OUTPUT LEVEL 0-100
|
||||
char panpot; // PANPOT 0-14 (R-L)
|
||||
char dummyv[6];
|
||||
} ALIGN_PACKED tmpSettings[8];
|
||||
struct ryhTemp {
|
||||
char timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF)
|
||||
char outlevel; // OUTPUT LEVEL 0-100
|
||||
char panpot; // PANPOT 0-14 (R-L)
|
||||
char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
|
||||
} ALIGN_PACKED rhySettings[64];
|
||||
|
||||
timbreParam timTemp[8];
|
||||
|
||||
struct patchArea {
|
||||
char timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
|
||||
char timbreNum; // TIMBRE NUMBER 0-63
|
||||
char keyShift; // KEY SHIFT 0-48 (-24 - +24)
|
||||
char fineTune; // FINE TUNE 0-100 (-50 - +50)
|
||||
char benderRange; // BENDER RANGE 0-24
|
||||
char assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
|
||||
char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
|
||||
char dummy; // (DUMMY)
|
||||
} ALIGN_PACKED pSettings[128];
|
||||
timbreParam patch[192];
|
||||
struct systemArea {
|
||||
char masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
|
||||
char reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
|
||||
char reverbTime; // REVERB TIME 0-7 (1-8)
|
||||
char reverbLevel; // REVERB LEVEL 0-7 (1-8)
|
||||
char reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
|
||||
char chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
|
||||
char masterVol; // MASTER VOLUME 0-100
|
||||
} ALIGN_PACKED system;
|
||||
|
||||
} ALIGN_PACKED;
|
||||
|
||||
struct memBanks {
|
||||
char pTemp[8][sizeof(memParams::patchTemp)];
|
||||
char rTemp[64][sizeof(memParams::ryhTemp)];
|
||||
char tTemp[8][sizeof(timbreParam)];
|
||||
char patchmemory[128][sizeof(memParams::patchArea)];
|
||||
char patchbanks[128][sizeof(timbreParam)];
|
||||
char timbrebanks[64][sizeof(timbreParam)];
|
||||
char systemBank[sizeof(memParams::systemArea)];
|
||||
} ALIGN_PACKED;
|
||||
|
||||
struct memAbsolute {
|
||||
char mt32memory[sizeof(memBanks)];
|
||||
} ALIGN_PACKED;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
struct partialFormat {
|
||||
Bit32u addr;
|
||||
Bit16u len;
|
||||
bool loop;
|
||||
float tune;
|
||||
Bit32s ampval;
|
||||
};
|
||||
|
||||
struct partialTable {
|
||||
Bit32u addr;
|
||||
Bit32u len;
|
||||
Bit32u pcmnum;
|
||||
Bit32s ampval;
|
||||
bool loop;
|
||||
Bit32s aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations
|
||||
};
|
||||
|
||||
|
||||
|
||||
union soundaddr {
|
||||
Bit32u pcmabs;
|
||||
struct offsets {
|
||||
#if defined(SCUMM_LITTLE_ENDIAN)
|
||||
Bit16u pcmoffset;
|
||||
Bit16u pcmplace;
|
||||
#else
|
||||
Bit16u pcmplace;
|
||||
Bit16u pcmoffset;
|
||||
#endif
|
||||
} pcmoffs;
|
||||
};
|
||||
|
||||
|
||||
struct volset {
|
||||
Bit16s leftvol;
|
||||
Bit16s rightvol;
|
||||
Bit16s leftvol2;
|
||||
Bit16s rightvol2;
|
||||
};
|
||||
|
||||
struct patchCache {
|
||||
int rawPCM;
|
||||
partialTable convPCM;
|
||||
|
||||
bool playPartial;
|
||||
bool usePartial;
|
||||
bool PCMPartial;
|
||||
char waveform;
|
||||
int pulsewidth;
|
||||
int pwsens;
|
||||
int pitchshift;
|
||||
int fineshift;
|
||||
bool sustain;
|
||||
|
||||
int lfodepth;
|
||||
int lforate;
|
||||
Bit32u lfoperiod;
|
||||
int modsense;
|
||||
|
||||
int keydir;
|
||||
int pitchkeyfollow;
|
||||
int pitchkeydir;
|
||||
|
||||
int filtkeyfollow;
|
||||
|
||||
int tvfbias;
|
||||
int tvfblevel;
|
||||
int tvfdir;
|
||||
|
||||
int ampbias[2];
|
||||
int ampblevel[2];
|
||||
int ampdir[2];
|
||||
|
||||
int ampdepth;
|
||||
int ampenvdir;
|
||||
int amplevel;
|
||||
int tvfdepth;
|
||||
|
||||
int prevsample;
|
||||
|
||||
bool useBender;
|
||||
|
||||
timbreParam::partialParam::envParam pitchEnv;
|
||||
timbreParam::partialParam::tvaParam ampEnv;
|
||||
timbreParam::partialParam::tvfParam filtEnv;
|
||||
|
||||
Bit32s ampsustain;
|
||||
Bit32s pitchsustain;
|
||||
Bit32s filtsustain;
|
||||
|
||||
Bit32u partCount;
|
||||
|
||||
Bit8u padding[64]; //Used to pad the patch cache to 4096 bytes. This replaces an imul with a shl 12
|
||||
|
||||
};
|
||||
|
||||
struct dpoly {
|
||||
bool isPlaying;
|
||||
bool isDecay;
|
||||
bool isActive;
|
||||
|
||||
bool partActive[4];
|
||||
|
||||
bool isRy;
|
||||
Bit32u *bendptr;
|
||||
Bit32u drumbend;
|
||||
Bit32s *volumeptr;
|
||||
volset *pansetptr;
|
||||
|
||||
int pcmnum;
|
||||
int freq;
|
||||
int freqnum;
|
||||
int vel;
|
||||
|
||||
Bit32u partCount;
|
||||
|
||||
soundaddr pcmoff;
|
||||
Bit32u pcmdelta;
|
||||
|
||||
|
||||
struct partialStatus {
|
||||
// Note played on keyboard
|
||||
int noteval;
|
||||
// Keyfollowed note values
|
||||
int keyedval;
|
||||
|
||||
// Keyfollowed filter values
|
||||
int realval;
|
||||
int filtval;
|
||||
// Keyfollowed filter w/o table
|
||||
int filtnoval;
|
||||
int pulsewidth;
|
||||
|
||||
struct envstatus {
|
||||
Bit32s envpos;
|
||||
Bit32s envstat;
|
||||
Bit32s envbase;
|
||||
Bit32s envdist;
|
||||
Bit32s envsize;
|
||||
|
||||
bool sustaining;
|
||||
bool decaying;
|
||||
bool notdecayed;
|
||||
Bit32u decay;
|
||||
Bit32s prevlevel;
|
||||
|
||||
Bit32s counter;
|
||||
Bit32s count;
|
||||
|
||||
} envs[4];
|
||||
|
||||
Bit32u lfopos;
|
||||
soundaddr partialOff;
|
||||
soundaddr wgOff;
|
||||
|
||||
Bit32u ampEnvCache;
|
||||
Bit32u pitchEnvCache;
|
||||
|
||||
bool isDecayed;
|
||||
bool PCMDone;
|
||||
|
||||
float history[32];
|
||||
|
||||
float pastfilt;
|
||||
bool pitchsustain;
|
||||
bool playPartial;
|
||||
bool usePartial;
|
||||
|
||||
int looppos;
|
||||
int partNum;
|
||||
|
||||
patchCache *tcache;
|
||||
|
||||
void * myPart;
|
||||
|
||||
|
||||
|
||||
} pStatus[4];
|
||||
|
||||
|
||||
int chan;
|
||||
|
||||
int origpat;
|
||||
int drumnum;
|
||||
|
||||
int age;
|
||||
|
||||
bool pedalhold;
|
||||
bool firstsamp;
|
||||
|
||||
Bit32u P1Mix;
|
||||
Bit32u P2Mix;
|
||||
bool sustain;
|
||||
};
|
||||
|
||||
#endif
|
4564
backends/midi/mt32/synth.cpp
Normal file
4564
backends/midi/mt32/synth.cpp
Normal file
File diff suppressed because it is too large
Load Diff
170
backends/midi/mt32/synth.h
Normal file
170
backends/midi/mt32/synth.h
Normal file
@ -0,0 +1,170 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2004 The ScummVM project
|
||||
* Based on Tristan's conversion of Canadacow's code
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* $Header$
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined __CSYNTHMT32_H__
|
||||
#define __CSYNTHMT32_H__
|
||||
|
||||
#ifdef HAVE_X86
|
||||
#if defined(_MSC_VER)
|
||||
#define USE_MMX 2
|
||||
#define I_ASM
|
||||
#else
|
||||
#define USE_MMX 0
|
||||
#undef I_ASM
|
||||
#endif
|
||||
#else
|
||||
#define USE_MMX 0
|
||||
#endif
|
||||
|
||||
extern const char *rom_path;
|
||||
|
||||
#define AMPENV 0
|
||||
#define FILTENV 1
|
||||
#define PITCHENV 2
|
||||
|
||||
// Filter setting
|
||||
#define FILTER_FLOAT 1
|
||||
#define FILTER_64BIT 0
|
||||
#define FILTER_INT 0
|
||||
|
||||
#define FILTERGRAN 512
|
||||
|
||||
// Amplitude of waveform generator
|
||||
#define WGAMP (7168)
|
||||
//#define WGAMP (8192)
|
||||
|
||||
#include "backends/midi/mt32/structures.h"
|
||||
#include "sound/mixer.h"
|
||||
|
||||
// Function that detects the availablity of SSE SIMD instructions
|
||||
// On non-MSVC compilers it automatically returns FALSE as inline assembly is required
|
||||
bool DetectSIMD();
|
||||
// Function that detects the availablity of 3DNow instructions
|
||||
// On non-MSVC compilers it automatically returns FALSE as inline assembly is required
|
||||
bool Detect3DNow();
|
||||
|
||||
struct SynthProperties {
|
||||
// Sample rate to use in mixing
|
||||
int SampleRate;
|
||||
// Flag to activate reverb. True = use reverb, False = no reverb
|
||||
bool UseReverb;
|
||||
// Flag True to use software set reverb settings, Flag False to set reverb settings in
|
||||
// following parameters
|
||||
bool UseDefault;
|
||||
// When not using the default settings, this specifies one of the 4 reverb types
|
||||
// 1 = Room 2 = Hall 3 = Plate 4 = Tap
|
||||
int RevType;
|
||||
// This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
|
||||
int RevTime;
|
||||
// This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
|
||||
int RevLevel;
|
||||
};
|
||||
|
||||
#ifndef BOOL
|
||||
#define BOOL bool
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE true
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE false
|
||||
#endif
|
||||
|
||||
// This is the specification of the Callback routine used when calling the RecalcWaveforms
|
||||
// function
|
||||
typedef void (*recalcStatusCallback)(int percDone);
|
||||
|
||||
// This external function recreates the base waveform file (waveforms.raw) using a specifed
|
||||
// sampling rate. The callback routine provides interactivity to let the user know what
|
||||
// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the
|
||||
// callback routine, no status is reported.
|
||||
BOOL RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
|
||||
|
||||
typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
|
||||
extern iir_filter_type usefilter;
|
||||
|
||||
extern partialFormat PCM[54];
|
||||
extern Bit16s romfile[262656];
|
||||
extern Bit32s divtable[256];
|
||||
extern Bit32s smalldivtable[256];
|
||||
extern Bit32u wavtabler[64][256];
|
||||
extern Bit32u looptabler[16][16][256];
|
||||
extern Bit16s sintable[65536];
|
||||
extern Bit32s penvtable[16][128];
|
||||
extern Bit32s pulsetable[101];
|
||||
extern Bit32s pulseoffset[101];
|
||||
extern Bit32s sawtable[128][128];
|
||||
extern float filtcoeff[FILTERGRAN][32][16];
|
||||
extern Bit32u lfoptable[101][128];
|
||||
extern Bit32s ampveltable[128][64];
|
||||
extern Bit32s amptable[129];
|
||||
extern Bit16s smallnoise[441];
|
||||
extern Bit32s samplepos;
|
||||
extern Bit16s* waveforms[4][256];
|
||||
extern Bit32u waveformsize[4][256];
|
||||
extern Bit8s LoopPatterns[16][16];
|
||||
extern int drumPan[30][2];
|
||||
extern float ResonFactor[32];
|
||||
extern float ResonInv[32];
|
||||
|
||||
extern Bit32s getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly);
|
||||
extern Bit32s getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly);
|
||||
extern Bit32s getFiltEnvelope(Bit16s wg, dpoly::partialStatus *pStat, dpoly *poly);
|
||||
|
||||
class CSynthMT32 {
|
||||
private:
|
||||
|
||||
unsigned char initmode;
|
||||
bool isOpen;
|
||||
SynthProperties myProp;
|
||||
|
||||
bool InitTables(const char * baseDir);
|
||||
|
||||
public:
|
||||
CSynthMT32() : isOpen(false) {};
|
||||
|
||||
// Used to initialized the MT-32. The baseDir parameter points to the location in the
|
||||
// filesystem where the ROM and data files are located. The second parameter specifies
|
||||
// properties for the synthesizer, as outlined in the structure above.
|
||||
// Returns TRUE if initialization was sucessful, otherwise returns FALSE.
|
||||
bool ClassicOpen(const char *baseDir, SynthProperties useProp);
|
||||
|
||||
// Closes the MT-32 and deallocates any memory used by the synthesizer
|
||||
void Close(void);
|
||||
|
||||
// Sends a 4-byte MIDI message to the MT-32 for immediate playback
|
||||
void PlayMsg(Bit32u msg);
|
||||
|
||||
// Sends a string of Sysex commands to the MT-32 for immediate interpretation
|
||||
void PlaySysex(Bit8u * sysex, Bit32u len);
|
||||
|
||||
// Save the system state to a sysex file specified by filename
|
||||
int DumpSysex(char *filename);
|
||||
|
||||
// This callback routine is used to have the MT-32 generate samples to the specified
|
||||
// output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo,
|
||||
// one sample is 4 bytes)
|
||||
void MT32_CallBack(Bit8u * stream, Bit32u len, int volume);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
@ -12,14 +12,20 @@ MODULE_OBJS := \
|
||||
backends/midi/seq.o \
|
||||
backends/midi/alsa.o \
|
||||
backends/midi/windows.o \
|
||||
backends/midi/ym2612.o
|
||||
backends/midi/ym2612.o \
|
||||
backends/midi/mt32/mt32.o \
|
||||
backends/midi/mt32/partial.o \
|
||||
backends/midi/mt32/synth.o \
|
||||
backends/midi/mt32/freeverb.o
|
||||
|
||||
|
||||
MODULE_DIRS += \
|
||||
backends \
|
||||
backends/fs/posix \
|
||||
backends/fs/morphos \
|
||||
backends/fs/windows \
|
||||
backends/midi
|
||||
backends/midi \
|
||||
backends/midi/mt32
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/common.rules
|
||||
|
@ -599,6 +599,8 @@ MidiDriver *GameDetector::createMidi(int midiDriver) {
|
||||
// driver.
|
||||
case MD_ADLIB: return NULL;
|
||||
|
||||
case MD_MT32: return MidiDriver_MT32_create(g_engine->_mixer, ConfMan.get("extrapath").c_str());
|
||||
|
||||
case MD_TOWNS: return MidiDriver_YM2612_create(g_engine->_mixer);
|
||||
|
||||
// Right now PC Speaker and PCjr are handled
|
||||
|
22
configure
vendored
22
configure
vendored
@ -51,6 +51,7 @@ _nasmpath="$PATH"
|
||||
NASMFLAGS=""
|
||||
NASM=""
|
||||
_prefix=/usr/local
|
||||
_have_x86=""
|
||||
|
||||
_srcdir=`dirname $0`
|
||||
|
||||
@ -659,6 +660,26 @@ EOF
|
||||
esac
|
||||
rm -f tmp_endianness_check$EXEEXT tmp_endianness_check.cpp
|
||||
|
||||
#
|
||||
# Check whether we can use x86 asm routines
|
||||
#
|
||||
echo_n "Running on x86... "
|
||||
case $_host_cpu in
|
||||
i386|i486|i586|i686)
|
||||
_have_x86=yes
|
||||
;;
|
||||
*)
|
||||
_have_x86=no
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "$_have_x86" = yes ; then
|
||||
_def_x86='#define HAVE_X86'
|
||||
else
|
||||
_def_x86='#undef HAVE_X86'
|
||||
fi
|
||||
echo "$_have_x86"
|
||||
|
||||
#
|
||||
# Check whether memory alignment is required
|
||||
#
|
||||
@ -1019,6 +1040,7 @@ cat > config.h << EOF
|
||||
|
||||
$_def_endianness
|
||||
$_def_align
|
||||
$_def_x86
|
||||
|
||||
$_def_linupy
|
||||
|
||||
|
@ -12,6 +12,7 @@ choices of output, depending on your operating system and configuration.
|
||||
|
||||
\begin{tabular}[h]{ll}
|
||||
adlib & Uses internal Adlib Emulation (default)\\
|
||||
mt32 & Uses internal MT-32 Emulation\\
|
||||
pcjr & Uses internal PCjr Emulation \\
|
||||
pcspk & Uses internal PC Speaker Emulation\\
|
||||
towns & Uses FM-TOWNS YM2612 Emulation\\
|
||||
@ -31,6 +32,7 @@ for example:
|
||||
\end{verbatim}
|
||||
|
||||
\input {07_01.tex}
|
||||
\input {07_02a.tex}
|
||||
\input {07_02.tex}
|
||||
\input {07_03.tex}
|
||||
\input {07_04.tex}
|
||||
|
@ -10,6 +10,6 @@ Some games (such as Sam and Max) only contain MIDI music data. This once
|
||||
prevented music for these games from working on platforms that do not support
|
||||
MIDI, or soundcards that do not provide MIDI drivers (e.g, many soundcards will
|
||||
not play MIDI under Linux). ScummVM can now emulate MIDI mode using sampled
|
||||
waves and Adlib emulation using the -eadlib option. However, if you are capable
|
||||
of using native MIDI, we recommend using one of the MIDI modes below for best
|
||||
sound.
|
||||
waves and Adlib or MT-32 emulation using the -eadlib or -emt32 options respectively.
|
||||
However, if you are capable of using native MIDI, we recommend using one of the
|
||||
MIDI modes below for best sound.
|
||||
|
18
doc/07_02a.tex
Normal file
18
doc/07_02a.tex
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "readme"
|
||||
%%% End:
|
||||
|
||||
\subsection{Playing sound with MT-32 emulation}
|
||||
|
||||
Some games which contain MIDI music data also have improved tracks designed
|
||||
for MT-32 sound module. ScummVM can now emulate this card, however you should
|
||||
provide original MT-32 ROMs to make it work. Put the roms in game directory or
|
||||
directory specified by extrapath.
|
||||
|
||||
You don't need to specify --native-mt32 with this driver, as it automatically
|
||||
gets turned on.
|
||||
|
||||
\textbf{NOTE:} You need to have enough processor power to use this emulator as
|
||||
it uses heavy floating-point computations.
|
@ -65,9 +65,12 @@ Thanks!
|
||||
\item Special thanks to:\\
|
||||
\begin{tabular}{p{4cm}l}
|
||||
Sander Buskens & For his work on the initial reversing of Monkey2\\
|
||||
Canadacow & For his MT-32 emulator\\
|
||||
Kevin Carnes & For Scumm16, the basis of ScummVM older gfx codec\\
|
||||
Jezar & For his freeverb filter implementation\\
|
||||
Jim Leiterman & Various info on his FM-TOWNS/Marty SCUMM ports\\
|
||||
Jimmi Thogersen & For ScummRev, and much obscure code/documentation\\
|
||||
Tristan & For his Linux port of MT-32 emulator\\
|
||||
\end{tabular}
|
||||
|
||||
Tony Warriner and everyone at Revolution Software Ltd. for sharing
|
||||
|
@ -1343,7 +1343,7 @@ void ScummEngine_v90he::scummInit() {
|
||||
|
||||
void ScummEngine::setupMusic(int midi) {
|
||||
_midiDriver = GameDetector::detectMusicDriver(midi);
|
||||
_native_mt32 = ConfMan.getBool("native_mt32");
|
||||
_native_mt32 = (ConfMan.getBool("native_mt32") || (_midiDriver == MD_MT32));
|
||||
|
||||
#ifndef __GP32__ //ph0x FIXME, "quick dirty hack"
|
||||
/* Bind the mixer to the system => mixer will be invoked
|
||||
@ -1393,6 +1393,9 @@ void ScummEngine::setupMusic(int midi) {
|
||||
}
|
||||
if (midi == MDT_TOWNS)
|
||||
_imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
|
||||
if (_midiDriver == MD_MT32) {
|
||||
_imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
|
||||
}
|
||||
_imuse->set_music_volume(ConfMan.getInt("music_volume"));
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ Specify music output where \fI<mode>\fP can be one of:
|
||||
.br
|
||||
\fBadlib\fP Uses internal Adlib Emulation (default)
|
||||
.br
|
||||
\fBmt32\fP Uses internal MT-32 Emulation
|
||||
.br
|
||||
\fBwindows\fP Windows MIDI. Uses built-in sequencer.
|
||||
.br
|
||||
\fBseq\fP Uses /dev/sequencer for MIDI, *nix users.
|
||||
|
@ -57,6 +57,7 @@ static const struct MidiDriverDescription midiDrivers[] = {
|
||||
{"towns", "FM Towns", MD_TOWNS},
|
||||
{"pcspk", "PC Speaker", MD_PCSPK},
|
||||
{"pcjr", "IBM PCjr", MD_PCJR},
|
||||
{"mt32", "MT-32", MD_MT32},
|
||||
|
||||
#if defined(__PALM_OS__)
|
||||
{"ypa1", "Yamaha Pa1", MD_YPA1},
|
||||
|
@ -47,7 +47,8 @@ enum {
|
||||
MD_PCJR = 12,
|
||||
MD_TOWNS = 13,
|
||||
MD_YPA1 = 14, // PalmOS
|
||||
MD_ZODIAC = 15 // PalmOS
|
||||
MD_ZODIAC = 15, // PalmOS
|
||||
MD_MT32 = 16
|
||||
};
|
||||
|
||||
/** Convert a string containing a music driver name into MIDI Driver type. */
|
||||
@ -177,6 +178,7 @@ extern MidiDriver *MidiDriver_CORE_create();
|
||||
extern MidiDriver *MidiDriver_ETUDE_create();
|
||||
extern MidiDriver *MidiDriver_ALSA_create();
|
||||
extern MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer);
|
||||
extern MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer, const char *path);
|
||||
extern MidiDriver *MidiDriver_YamahaPa1_create();
|
||||
extern MidiDriver *MidiDriver_Zodiac_create();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user