mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-01-18 15:11:46 +00:00
- Moved sound sample rate, buffer size, and buffer count to the
advanced sound options menu. Removed opl_enable from the menu. - Added OPL synth as MIDI device -3. Since this is based on the MUS player code, it only supports those events and controllers supported by MUS. Some of Duke's MIDIs sound awful, but I think that may be more because it's using different instruments... There's a thread in the MIDI streamer class that could be taken out for Linux, since it doesn't need to deal with the Windows Multimedia API, but for now, this is still Windows-only. SVN r864 (trunk)
This commit is contained in:
parent
8f49ea7f48
commit
cd70087ed5
15 changed files with 707 additions and 106 deletions
|
@ -1,3 +1,17 @@
|
|||
March 28, 2008
|
||||
- Moved sound sample rate, buffer size, and buffer count to the
|
||||
advanced sound options menu. Removed opl_enable from the menu.
|
||||
- Added OPL synth as MIDI device -3. Since this is based on the MUS player
|
||||
code, it only supports those events and controllers supported by MUS.
|
||||
Some of Duke's MIDIs sound awful, but I think that may be more because
|
||||
it's using different instruments... There's a thread in the MIDI streamer
|
||||
class that could be taken out for Linux, since it doesn't need to deal
|
||||
with the Windows Multimedia API, but for now, this is still Windows-only.
|
||||
- Changed the output of the OPL emulator from 32-bit integers to 32-bit
|
||||
floats, so I can write its output directly to the stream buffer. In
|
||||
addition, this lets me bring the OPL volume level much closer to the
|
||||
standard MIDI volume.
|
||||
|
||||
March 27, 2008
|
||||
- Did some restructuring of the OPL code in preparation for turning it
|
||||
into a general MIDI player.
|
||||
|
|
|
@ -1244,9 +1244,6 @@ static menuitem_t SoundItems[] =
|
|||
{ ediscrete,"Speaker mode", {&snd_speakermode}, {8.0}, {0.0}, {0.0}, {(value_t *)SpeakerModes} },
|
||||
{ ediscrete,"Resampler", {&snd_resampler}, {4.0}, {0.0}, {0.0}, {(value_t *)Resamplers} },
|
||||
{ discrete, "HRTF filter", {&snd_hrtf}, {2.0}, {0.0}, {0.0}, {(value_t *)OnOff} },
|
||||
{ discrete, "Sample rate", {&snd_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} },
|
||||
{ discrete, "Buffer size", {&snd_buffersize}, {8.0}, {0.0}, {0.0}, {BufferSizes} },
|
||||
{ discrete, "Buffer count", {&snd_buffercount}, {12.0}, {0.0}, {0.0}, {BufferCounts} },
|
||||
|
||||
{ redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} },
|
||||
{ more, "Advanced options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)AdvSoundOptions} },
|
||||
|
@ -1274,15 +1271,18 @@ EXTERN_CVAR (Bool, opl_onechip)
|
|||
|
||||
static menuitem_t AdvSoundItems[] =
|
||||
{
|
||||
{ whitetext,"OPL Synthesis", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} },
|
||||
{ discrete, "Use FM Synth for MUS music",{&opl_enable}, {2.0}, {0.0}, {0.0}, {OnOff} },
|
||||
{ discrete, "Sample rate", {&snd_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} },
|
||||
{ discrete, "Buffer size", {&snd_buffersize}, {8.0}, {0.0}, {0.0}, {BufferSizes} },
|
||||
{ discrete, "Buffer count", {&snd_buffercount}, {12.0}, {0.0}, {0.0}, {BufferCounts} },
|
||||
{ redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} },
|
||||
{ whitetext,"OPL Synthesis", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} },
|
||||
{ discrete, "Only emulate one OPL chip", {&opl_onechip}, {2.0}, {0.0}, {0.0}, {OnOff} },
|
||||
};
|
||||
|
||||
static menu_t AdvSoundMenu =
|
||||
{
|
||||
"ADVANCED SOUND OPTIONS",
|
||||
1,
|
||||
0,
|
||||
countof(AdvSoundItems),
|
||||
0,
|
||||
AdvSoundItems,
|
||||
|
|
|
@ -208,7 +208,8 @@ struct OP2instrEntry *musicBlock::getInstrument(uint channel, uchar note)
|
|||
if (note < 35 || note > 81)
|
||||
return NULL; /* wrong percussion number */
|
||||
instrnumber = note + (128-35);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
instrnumber = driverdata.channelInstr[channel];
|
||||
}
|
||||
|
@ -295,15 +296,14 @@ void musicBlock::OPLchangeControl(uint channel, uchar controller, int value)
|
|||
{
|
||||
uint i;
|
||||
uint id = channel;
|
||||
struct OPLdata *data = &driverdata;
|
||||
|
||||
switch (controller)
|
||||
{
|
||||
case ctrlPatch: /* change instrument */
|
||||
data->channelInstr[channel] = value;
|
||||
OPLprogramChange(channel, value);
|
||||
break;
|
||||
case ctrlModulation:
|
||||
data->channelModulation[channel] = value;
|
||||
driverdata.channelModulation[channel] = value;
|
||||
for(i = 0; i < io->OPLchannels; i++)
|
||||
{
|
||||
struct channelEntry *ch = &channels[i];
|
||||
|
@ -325,7 +325,7 @@ void musicBlock::OPLchangeControl(uint channel, uchar controller, int value)
|
|||
}
|
||||
break;
|
||||
case ctrlVolume: /* change volume */
|
||||
data->channelVolume[channel] = value;
|
||||
driverdata.channelVolume[channel] = value;
|
||||
for(i = 0; i < io->OPLchannels; i++)
|
||||
{
|
||||
struct channelEntry *ch = &channels[i];
|
||||
|
@ -338,7 +338,7 @@ void musicBlock::OPLchangeControl(uint channel, uchar controller, int value)
|
|||
}
|
||||
break;
|
||||
case ctrlPan: /* change pan (balance) */
|
||||
data->channelPan[channel] = value -= 64;
|
||||
driverdata.channelPan[channel] = value -= 64;
|
||||
for(i = 0; i < io->OPLchannels; i++)
|
||||
{
|
||||
struct channelEntry *ch = &channels[i];
|
||||
|
@ -350,22 +350,26 @@ void musicBlock::OPLchangeControl(uint channel, uchar controller, int value)
|
|||
}
|
||||
break;
|
||||
case ctrlSustainPedal: /* change sustain pedal (hold) */
|
||||
data->channelSustain[channel] = value;
|
||||
driverdata.channelSustain[channel] = value;
|
||||
if (value < 0x40)
|
||||
releaseSustain(channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void musicBlock::OPLprogramChange(uint channel, int value)
|
||||
{
|
||||
driverdata.channelInstr[channel] = value;
|
||||
}
|
||||
|
||||
void musicBlock::OPLplayMusic()
|
||||
void musicBlock::OPLplayMusic(int vol)
|
||||
{
|
||||
uint i;
|
||||
struct OPLdata *data = &driverdata;
|
||||
|
||||
for (i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
data->channelVolume[i] = 127; /* default volume 127 (full volume) */
|
||||
data->channelVolume[i] = vol; /* default volume 127 for MUS (full volume) */
|
||||
data->channelSustain[i] = 0;
|
||||
data->channelLastVolume[i] = 64;
|
||||
data->channelPitch[i] = 64;
|
||||
|
|
481
src/oplsynth/music_opl_mididevice.cpp
Normal file
481
src/oplsynth/music_opl_mididevice.cpp
Normal file
|
@ -0,0 +1,481 @@
|
|||
/*
|
||||
** music_opl_mididevice.cpp
|
||||
** Provides an emulated OPL implementation of a MIDI output device.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2008 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
** Uses Vladimir Arnost's MUS player library.
|
||||
*/
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include "i_musicinterns.h"
|
||||
#include "templates.h"
|
||||
#include "doomdef.h"
|
||||
#include "m_swap.h"
|
||||
#include "w_wad.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
#if defined(_DEBUG) && defined(_WIN32)
|
||||
#define UNIMPL(m,c,s,t) \
|
||||
{ char foo[128]; sprintf(foo, m, c, s, t); OutputDebugString(foo); }
|
||||
#else
|
||||
#define UNIMPL(m,c,s,t)
|
||||
#endif
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
|
||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
extern OPLmusicBlock *BlockForStats;
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice Contructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
OPLMIDIDevice::OPLMIDIDevice()
|
||||
{
|
||||
Stream = NULL;
|
||||
Tempo = 0;
|
||||
Division = 0;
|
||||
Events = NULL;
|
||||
Started = false;
|
||||
|
||||
FWadLump data = Wads.OpenLumpName("GENMIDI");
|
||||
OPLloadBank(data);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
OPLMIDIDevice::~OPLMIDIDevice()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: Open
|
||||
//
|
||||
// Returns 0 on success.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::Open(void (*callback)(UINT, void *, DWORD, DWORD), void *userdata)
|
||||
{
|
||||
if (io == NULL || io->OPLinit(TwoChips + 1, uint(OPL_SAMPLE_RATE)))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
Stream = GSnd->CreateStream(FillStream, int(OPL_SAMPLE_RATE / 14) * 4,
|
||||
SoundStream::Mono | SoundStream::Float, int(OPL_SAMPLE_RATE), this);
|
||||
if (Stream == NULL)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
Callback = callback;
|
||||
CallbackData = userdata;
|
||||
Tempo = 500000;
|
||||
Division = 100;
|
||||
CalcTickRate();
|
||||
|
||||
OPLstopMusic();
|
||||
OPLplayMusic(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: Close
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void OPLMIDIDevice::Close()
|
||||
{
|
||||
if (Stream != NULL)
|
||||
{
|
||||
delete Stream;
|
||||
Stream = NULL;
|
||||
}
|
||||
io->OPLdeinit();
|
||||
Started = false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: IsOpen
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool OPLMIDIDevice::IsOpen() const
|
||||
{
|
||||
return Stream != NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: GetTechnology
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::GetTechnology() const
|
||||
{
|
||||
return MOD_FMSYNTH;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: SetTempo
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::SetTempo(int tempo)
|
||||
{
|
||||
Tempo = tempo;
|
||||
CalcTickRate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: SetTimeDiv
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::SetTimeDiv(int timediv)
|
||||
{
|
||||
Division = timediv;
|
||||
CalcTickRate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: CalcTickRate
|
||||
//
|
||||
// Tempo is the number of microseconds per quarter note.
|
||||
// Division is the number of ticks per quarter note.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void OPLMIDIDevice::CalcTickRate()
|
||||
{
|
||||
SamplesPerTick = OPL_SAMPLE_RATE / (1000000.0 / Tempo) / Division;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: Resume
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::Resume()
|
||||
{
|
||||
if (!Started)
|
||||
{
|
||||
if (Stream->Play(true, 1, false))
|
||||
{
|
||||
Started = true;
|
||||
BlockForStats = this;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: Stop
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void OPLMIDIDevice::Stop()
|
||||
{
|
||||
if (Started)
|
||||
{
|
||||
Stream->Stop();
|
||||
Started = false;
|
||||
BlockForStats = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: StreamOut
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::StreamOut(MIDIHDR *header)
|
||||
{
|
||||
Serialize();
|
||||
header->lpNext = NULL;
|
||||
if (Events == NULL)
|
||||
{
|
||||
Events = header;
|
||||
NextTickIn = SamplesPerTick * *(DWORD *)header->lpData;
|
||||
Position = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
MIDIHDR **p;
|
||||
|
||||
for (p = &Events; *p != NULL; p = &(*p)->lpNext)
|
||||
{ }
|
||||
*p = header;
|
||||
}
|
||||
Unserialize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: PrepareHeader
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::PrepareHeader(MIDIHDR *header)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: UnprepareHeader
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::UnprepareHeader(MIDIHDR *header)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: FakeVolume
|
||||
//
|
||||
// Since the OPL output is rendered as a normal stream, its volume is
|
||||
// controlled through the GSnd interface, not here.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool OPLMIDIDevice::FakeVolume()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: Pause
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool OPLMIDIDevice::Pause(bool paused)
|
||||
{
|
||||
if (Stream != NULL)
|
||||
{
|
||||
return Stream->SetPaused(paused);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: PlayTick
|
||||
//
|
||||
// event[0] = delta time
|
||||
// event[1] = unused
|
||||
// event[2] = event
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLMIDIDevice::PlayTick()
|
||||
{
|
||||
DWORD delay = 0;
|
||||
|
||||
while (delay == 0 && Events != NULL)
|
||||
{
|
||||
DWORD *event = (DWORD *)(Events->lpData + Position);
|
||||
if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO)
|
||||
{
|
||||
Tempo = MEVT_EVENTPARM(event[2]);
|
||||
CalcTickRate();
|
||||
}
|
||||
else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG)
|
||||
{ // Should I handle master volume changes?
|
||||
}
|
||||
else if (MEVT_EVENTTYPE(event[2]) == 0)
|
||||
{ // Short MIDI event
|
||||
int status = event[2] & 0xff;
|
||||
int parm1 = (event[2] >> 8) & 0x7f;
|
||||
int parm2 = (event[2] >> 16) & 0x7f;
|
||||
HandleEvent(status, parm1, parm2);
|
||||
}
|
||||
|
||||
// Advance to next event.
|
||||
if (event[2] < 0x80000000)
|
||||
{ // Short message
|
||||
Position += 12;
|
||||
}
|
||||
else
|
||||
{ // Long message
|
||||
Position += 12 + ((MEVT_EVENTPARM(event[2]) + 3) & ~3);
|
||||
}
|
||||
|
||||
// Did we use up this buffer?
|
||||
if (Position >= Events->dwBytesRecorded)
|
||||
{
|
||||
Events = Events->lpNext;
|
||||
Position = 0;
|
||||
|
||||
if (Callback != NULL)
|
||||
{
|
||||
Callback(MOM_DONE, CallbackData, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (Events == NULL)
|
||||
{ // No more events. Just return something to keep the song playing
|
||||
// while we wait for more to be submitted.
|
||||
return int(Division);
|
||||
}
|
||||
|
||||
delay = *(DWORD *)(Events->lpData + Position);
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: HandleEvent
|
||||
//
|
||||
// Processes a normal MIDI event.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2)
|
||||
{
|
||||
int command = status & 0xF0;
|
||||
int channel = status & 0x0F;
|
||||
|
||||
// Swap channels 9 and 15, because their roles are reversed
|
||||
// in MUS and MIDI formats.
|
||||
if (channel == 9)
|
||||
{
|
||||
channel = 15;
|
||||
}
|
||||
else if (channel == 15)
|
||||
{
|
||||
channel = 9;
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case MIDI_NOTEOFF:
|
||||
playingcount--;
|
||||
OPLreleaseNote(channel, parm1);
|
||||
break;
|
||||
|
||||
case MIDI_NOTEON:
|
||||
playingcount++;
|
||||
OPLplayNote(channel, parm1, parm2);
|
||||
break;
|
||||
|
||||
case MIDI_POLYPRESS:
|
||||
UNIMPL("Unhandled note aftertouch: Channel %d, note %d, value %d\n", channel, parm1, parm2);
|
||||
break;
|
||||
|
||||
case MIDI_CTRLCHANGE:
|
||||
switch (parm1)
|
||||
{
|
||||
case 0: OPLchangeControl(channel, ctrlBank, parm2); break;
|
||||
case 1: OPLchangeControl(channel, ctrlModulation, parm2); break;
|
||||
case 7: OPLchangeControl(channel, ctrlVolume, parm2); break;
|
||||
case 10: OPLchangeControl(channel, ctrlPan, parm2); break;
|
||||
case 11: OPLchangeControl(channel, ctrlExpression, parm2); break;
|
||||
case 64: OPLchangeControl(channel, ctrlSustainPedal, parm2); break;
|
||||
case 67: OPLchangeControl(channel, ctrlSoftPedal, parm2); break;
|
||||
case 91: OPLchangeControl(channel, ctrlReverb, parm2); break;
|
||||
case 93: OPLchangeControl(channel, ctrlChorus, parm2); break;
|
||||
case 120: OPLchangeControl(channel, ctrlSoundsOff, parm2); break;
|
||||
case 121: OPLchangeControl(channel, ctrlResetCtrls, parm2); break;
|
||||
case 123: OPLchangeControl(channel, ctrlNotesOff, parm2); break;
|
||||
case 126: OPLchangeControl(channel, ctrlMono, parm2); break;
|
||||
case 127: OPLchangeControl(channel, ctrlPoly, parm2); break;
|
||||
default:
|
||||
UNIMPL("Unhandled controller: Channel %d, controller %d, value %d\n", channel, parm1, parm2);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case MIDI_PRGMCHANGE:
|
||||
OPLprogramChange(channel, parm1);
|
||||
break;
|
||||
|
||||
case MIDI_CHANPRESS:
|
||||
UNIMPL("Unhandled channel aftertouch: Channel %d, value %d\n", channel, parm1, 0);
|
||||
break;
|
||||
|
||||
case MIDI_PITCHBEND:
|
||||
// MUS pitch is 8 bit, but MIDI pitch is 14-bit
|
||||
OPLpitchWheel(channel, (parm1 | (parm2 >> 1)) >> (14 - 8));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: FillStream static
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool OPLMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata)
|
||||
{
|
||||
OPLMIDIDevice *device = (OPLMIDIDevice *)userdata;
|
||||
return device->ServiceStream (buff, len);
|
||||
}
|
|
@ -199,7 +199,8 @@ struct musicBlock {
|
|||
void OPLreleaseNote(uint channel, uchar note);
|
||||
void OPLpitchWheel(uint channel, int pitch);
|
||||
void OPLchangeControl(uint channel, uchar controller, int value);
|
||||
void OPLplayMusic();
|
||||
void OPLprogramChange(uint channel, int value);
|
||||
void OPLplayMusic(int vol);
|
||||
void OPLstopMusic();
|
||||
void OPLchangeVolume(uint volume);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
EXTERN_CVAR (Bool, opl_onechip)
|
||||
|
||||
static OPLmusicBlock *BlockForStats;
|
||||
OPLmusicBlock *BlockForStats;
|
||||
|
||||
OPLmusicBlock::OPLmusicBlock()
|
||||
{
|
||||
|
@ -55,17 +55,18 @@ OPLmusicBlock::~OPLmusicBlock()
|
|||
delete io;
|
||||
}
|
||||
|
||||
void OPLmusicBlock::ResetChips ()
|
||||
void OPLmusicBlock::Serialize()
|
||||
{
|
||||
TwoChips = !opl_onechip;
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection (&ChipAccess);
|
||||
#else
|
||||
if (SDL_mutexP (ChipAccess) != 0)
|
||||
return;
|
||||
#endif
|
||||
io->OPLdeinit ();
|
||||
io->OPLinit (TwoChips + 1, uint(OPL_SAMPLE_RATE));
|
||||
}
|
||||
|
||||
void OPLmusicBlock::Unserialize()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection (&ChipAccess);
|
||||
#else
|
||||
|
@ -73,10 +74,19 @@ void OPLmusicBlock::ResetChips ()
|
|||
#endif
|
||||
}
|
||||
|
||||
void OPLmusicBlock::ResetChips ()
|
||||
{
|
||||
TwoChips = !opl_onechip;
|
||||
Serialize();
|
||||
io->OPLdeinit ();
|
||||
io->OPLinit (TwoChips + 1, uint(OPL_SAMPLE_RATE));
|
||||
Unserialize();
|
||||
}
|
||||
|
||||
void OPLmusicBlock::Restart()
|
||||
{
|
||||
OPLstopMusic ();
|
||||
OPLplayMusic ();
|
||||
OPLplayMusic (127);
|
||||
MLtime = 0;
|
||||
playingcount = 0;
|
||||
}
|
||||
|
@ -229,15 +239,12 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
|||
samples1 = samples;
|
||||
memset(buff, 0, numbytes);
|
||||
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection (&ChipAccess);
|
||||
#else
|
||||
if (SDL_mutexP (ChipAccess) != 0)
|
||||
return true;
|
||||
#endif
|
||||
Serialize();
|
||||
while (numsamples > 0)
|
||||
{
|
||||
int samplesleft = MIN (numsamples, int(NextTickIn));
|
||||
double ticky = NextTickIn;
|
||||
int tick_in = int(NextTickIn);
|
||||
int samplesleft = MIN(numsamples, tick_in);
|
||||
|
||||
if (samplesleft > 0)
|
||||
{
|
||||
|
@ -246,6 +253,7 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
|||
{
|
||||
YM3812UpdateOne (1, samples1, samplesleft);
|
||||
}
|
||||
assert(NextTickIn == ticky);
|
||||
NextTickIn -= samplesleft;
|
||||
assert (NextTickIn >= 0);
|
||||
numsamples -= samplesleft;
|
||||
|
@ -254,7 +262,8 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
|||
|
||||
if (NextTickIn < 1)
|
||||
{
|
||||
int next = PlayTick ();
|
||||
int next = PlayTick();
|
||||
assert(next >= 0);
|
||||
if (next == 0)
|
||||
{ // end of song
|
||||
if (!Looping || prevEnded)
|
||||
|
@ -286,11 +295,7 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection (&ChipAccess);
|
||||
#else
|
||||
SDL_mutexV (ChipAccess);
|
||||
#endif
|
||||
Unserialize();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -447,7 +452,7 @@ OPLmusicWriter::OPLmusicWriter (const char *songname, const char *filename)
|
|||
io = new DiskWriterIO ();
|
||||
if (((DiskWriterIO *)io)->OPLinit (filename) == 0)
|
||||
{
|
||||
OPLplayMusic ();
|
||||
OPLplayMusic (127);
|
||||
score = scoredata + ((MUSheader *)scoredata)->scoreStart;
|
||||
Go ();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,9 @@ public:
|
|||
protected:
|
||||
virtual int PlayTick() = 0;
|
||||
|
||||
void Serialize();
|
||||
void Unserialize();
|
||||
|
||||
double NextTickIn;
|
||||
double SamplesPerTick;
|
||||
bool TwoChips;
|
||||
|
|
|
@ -305,6 +305,7 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
- OPL:
|
||||
- if explicitly selected by $mididevice
|
||||
- when opl_enable is true and no midi device is set for the song
|
||||
- when snd_mididevice is -3 and no midi device is set for the song
|
||||
|
||||
Timidity:
|
||||
- if explicitly selected by $mididevice
|
||||
|
@ -320,7 +321,7 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
- when snd_mididevice is >= 0 and no midi device is set for the song
|
||||
- as fallback when both OPL and Timidity failed and snd_mididevice is >= 0
|
||||
*/
|
||||
if ((opl_enable && device == MDEV_DEFAULT) || device == MDEV_OPL)
|
||||
if (((opl_enable || snd_mididevice == -3) && device == MDEV_DEFAULT) || device == MDEV_OPL)
|
||||
{
|
||||
info = new OPLMUSSong (file, musiccache, len);
|
||||
}
|
||||
|
@ -379,10 +380,13 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
if (id == MAKE_ID('M','T','h','d'))
|
||||
{
|
||||
// This is a midi file
|
||||
// MIDI can't be played with OPL so use default.
|
||||
if (device == MDEV_OPL) device = MDEV_DEFAULT;
|
||||
|
||||
/* MIDI are played as:
|
||||
OPL:
|
||||
- if explicitly selected by $mididevice
|
||||
- when opl_enable is true and no midi device is set for the song
|
||||
- when snd_mididevice is -3 and no midi device is set for the song
|
||||
|
||||
Timidity:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -2 and no midi device is set for the song
|
||||
|
@ -397,21 +401,24 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
- when snd_mididevice is >= 0 and no midi device is set for the song
|
||||
- as fallback when Timidity failed and snd_mididevice is >= 0
|
||||
*/
|
||||
|
||||
if ((device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT)) && GSnd != NULL)
|
||||
if ((device == MDEV_OPL || (snd_mididevice == -3 && device == MDEV_DEFAULT)) && GSnd != NULL)
|
||||
{
|
||||
info = new MIDISong2 (file, musiccache, len, true);
|
||||
}
|
||||
else if ((device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT)) && GSnd != NULL)
|
||||
{
|
||||
info = new TimiditySong (file, musiccache, len);
|
||||
if (!info->IsValid())
|
||||
{
|
||||
delete info;
|
||||
info = NULL;
|
||||
device = MDEV_DEFAULT;
|
||||
}
|
||||
}
|
||||
if (info != NULL && !info->IsValid())
|
||||
{
|
||||
delete info;
|
||||
info = NULL;
|
||||
device = MDEV_DEFAULT;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (info == NULL && device != MDEV_FMOD && (snd_mididevice >= 0 || device == MDEV_MMAPI))
|
||||
{
|
||||
info = new MIDISong2 (file, musiccache, len);
|
||||
info = new MIDISong2 (file, musiccache, len, false);
|
||||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
|
|
@ -66,8 +66,28 @@ struct MIDIHDR
|
|||
BYTE *lpData;
|
||||
DWORD dwBufferLength;
|
||||
DWORD dwBytesRecorded;
|
||||
MIDIHDR *Next;
|
||||
MIDIHDR *lpNext;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
MOD_MIDIPORT = 1,
|
||||
MOD_SYNTH,
|
||||
MOD_SQSYNTH,
|
||||
MOD_FMSYNTH,
|
||||
MOD_MAPPER,
|
||||
MOD_WAVETABLE,
|
||||
MOD_SWSYNTH
|
||||
};
|
||||
|
||||
#define MEVT_TEMPO ((BYTE)1)
|
||||
#define MEVT_NOP ((BYTE)2)
|
||||
#define MEVT_LONGMSG ((BYTE)128)
|
||||
|
||||
#define MEVT_EVENTTYPE(x) ((BYTE)((x) >> 24))
|
||||
#define MEVT_EVENTPARM(x) ((x) & 0xffffff)
|
||||
|
||||
#define MOM_DONE 969
|
||||
#endif
|
||||
|
||||
class MIDIDevice
|
||||
|
@ -87,6 +107,8 @@ public:
|
|||
virtual void Stop() = 0;
|
||||
virtual int PrepareHeader(MIDIHDR *data) = 0;
|
||||
virtual int UnprepareHeader(MIDIHDR *data) = 0;
|
||||
virtual bool FakeVolume() = 0;
|
||||
virtual bool Pause(bool paused) = 0;
|
||||
};
|
||||
|
||||
// WinMM implementation of a MIDI output device -----------------------------
|
||||
|
@ -107,6 +129,8 @@ public:
|
|||
void Stop();
|
||||
int PrepareHeader(MIDIHDR *data);
|
||||
int UnprepareHeader(MIDIHDR *data);
|
||||
bool FakeVolume();
|
||||
bool Pause(bool paused);
|
||||
|
||||
protected:
|
||||
static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD);
|
||||
|
@ -138,10 +162,25 @@ public:
|
|||
void Stop();
|
||||
int PrepareHeader(MIDIHDR *data);
|
||||
int UnprepareHeader(MIDIHDR *data);
|
||||
bool FakeVolume();
|
||||
bool Pause(bool paused);
|
||||
|
||||
protected:
|
||||
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
|
||||
|
||||
void (*Callback)(unsigned int, void *, DWORD, DWORD);
|
||||
void *CallbackData;
|
||||
|
||||
void CalcTickRate();
|
||||
void HandleEvent(int status, int parm1, int parm2);
|
||||
int PlayTick();
|
||||
|
||||
SoundStream *Stream;
|
||||
double Tempo;
|
||||
double Division;
|
||||
MIDIHDR *Events;
|
||||
bool Started;
|
||||
DWORD Position;
|
||||
};
|
||||
|
||||
// Base class for streaming MUS and MIDI files ------------------------------
|
||||
|
@ -149,7 +188,7 @@ protected:
|
|||
class MIDIStreamer : public MusInfo
|
||||
{
|
||||
public:
|
||||
MIDIStreamer();
|
||||
MIDIStreamer(bool opl);
|
||||
~MIDIStreamer();
|
||||
|
||||
void MusicVolumeChanged();
|
||||
|
@ -208,6 +247,7 @@ protected:
|
|||
int InitialTempo;
|
||||
BYTE ChannelVolumes[16];
|
||||
DWORD Volume;
|
||||
bool UseOPLDevice;
|
||||
};
|
||||
|
||||
// MUS file played with a MIDI stream ---------------------------------------
|
||||
|
@ -235,7 +275,7 @@ protected:
|
|||
class MIDISong2 : public MIDIStreamer
|
||||
{
|
||||
public:
|
||||
MIDISong2 (FILE *file, char *musiccache, int length);
|
||||
MIDISong2 (FILE *file, char *musiccache, int length, bool opl);
|
||||
~MIDISong2 ();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -21,7 +21,7 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|||
if (!nummididevicesset)
|
||||
return;
|
||||
|
||||
if ((self >= (signed)nummididevices) || (self < -2))
|
||||
if ((self >= (signed)nummididevices) || (self < -3))
|
||||
{
|
||||
Printf ("ID out of range. Using default device.\n");
|
||||
self = 0;
|
||||
|
@ -76,43 +76,38 @@ void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues)
|
|||
{
|
||||
if (*outValues == NULL)
|
||||
{
|
||||
int count = 1 + nummididevices + (nummididevices > 0);
|
||||
int count = 3 + nummididevices;
|
||||
value_t *values;
|
||||
UINT id;
|
||||
int p;
|
||||
|
||||
*outValues = values = new value_t[count];
|
||||
|
||||
values[0].name = "TiMidity++";
|
||||
values[0].value = -2.0;
|
||||
values[1].name = "FMOD";
|
||||
values[1].value = -1.0;
|
||||
if (nummididevices > 0)
|
||||
values[0].name = "OPL Synth Emulation";
|
||||
values[0].value = -3.0;
|
||||
values[1].name = "TiMidity++";
|
||||
values[1].value = -2.0;
|
||||
values[2].name = "FMOD";
|
||||
values[2].value = -1.0;
|
||||
for (id = 0, p = 3; id < nummididevices; ++id)
|
||||
{
|
||||
UINT id;
|
||||
int p;
|
||||
MIDIOUTCAPS caps;
|
||||
MMRESULT res;
|
||||
|
||||
for (id = 0, p = 2; id < nummididevices; ++id)
|
||||
res = midiOutGetDevCaps (id, &caps, sizeof(caps));
|
||||
if (res == MMSYSERR_NOERROR)
|
||||
{
|
||||
MIDIOUTCAPS caps;
|
||||
MMRESULT res;
|
||||
size_t len = strlen (caps.szPname) + 1;
|
||||
char *name = new char[len];
|
||||
|
||||
res = midiOutGetDevCaps (id, &caps, sizeof(caps));
|
||||
if (res == MMSYSERR_NOERROR)
|
||||
{
|
||||
size_t len = strlen (caps.szPname) + 1;
|
||||
char *name = new char[len];
|
||||
|
||||
memcpy (name, caps.szPname, len);
|
||||
values[p].name = name;
|
||||
values[p].value = (float)id;
|
||||
++p;
|
||||
}
|
||||
memcpy (name, caps.szPname, len);
|
||||
values[p].name = name;
|
||||
values[p].value = (float)id;
|
||||
++p;
|
||||
}
|
||||
*numValues = (float)p;
|
||||
}
|
||||
else
|
||||
{
|
||||
*numValues = 2.f;
|
||||
}
|
||||
assert(p == count);
|
||||
*numValues = float(count);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,8 +180,8 @@ CCMD (snd_listmididevices)
|
|||
|
||||
CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < -2)
|
||||
self = -2;
|
||||
if (self < -3)
|
||||
self = -3;
|
||||
else if (self > -1)
|
||||
self = -1;
|
||||
}
|
||||
|
@ -195,16 +190,17 @@ void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues)
|
|||
{
|
||||
if (*outValues == NULL)
|
||||
{
|
||||
int count = 1 + nummididevices + (nummididevices > 0);
|
||||
value_t *values;
|
||||
|
||||
*outValues = values = new value_t[count];
|
||||
*outValues = values = new value_t[3];
|
||||
|
||||
values[0].name = "TiMidity++";
|
||||
values[0].value = -2.0;
|
||||
values[1].name = "FMOD";
|
||||
values[1].value = -1.0;
|
||||
*numValues = 2.f;
|
||||
values[0].name = "OPL Synth Emulation";
|
||||
values[0].value = -3.0;
|
||||
values[1].name = "TiMidity++";
|
||||
values[1].value = -2.0;
|
||||
values[2].name = "FMOD";
|
||||
values[2].value = -1.0;
|
||||
*numValues = 3.f;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,8 +87,6 @@ struct MIDISong2::TrackInfo
|
|||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
extern UINT mididevice;
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
static BYTE EventLengths[7] = { 2, 2, 2, 2, 1, 1, 2 };
|
||||
|
@ -106,8 +104,8 @@ static BYTE CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
MIDISong2::MIDISong2 (FILE *file, char *musiccache, int len)
|
||||
: MusHeader(0), Tracks(0)
|
||||
MIDISong2::MIDISong2 (FILE *file, char *musiccache, int len, bool opl)
|
||||
: MIDIStreamer(opl), MusHeader(0), Tracks(0)
|
||||
{
|
||||
int p;
|
||||
int i;
|
||||
|
|
|
@ -69,9 +69,9 @@ extern UINT mididevice;
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
MIDIStreamer::MIDIStreamer()
|
||||
MIDIStreamer::MIDIStreamer(bool opl)
|
||||
: MIDI(0), PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
|
||||
Division(0), InitialTempo(500000)
|
||||
Division(0), InitialTempo(500000), UseOPLDevice(opl)
|
||||
{
|
||||
BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (BufferDoneEvent == NULL)
|
||||
|
@ -164,7 +164,14 @@ void MIDIStreamer::Play (bool looping)
|
|||
InitialPlayback = true;
|
||||
|
||||
assert(MIDI == NULL);
|
||||
MIDI = new WinMIDIDevice(mididevice);
|
||||
if (UseOPLDevice)
|
||||
{
|
||||
MIDI = new OPLMIDIDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
MIDI = new WinMIDIDevice(mididevice);
|
||||
}
|
||||
|
||||
if (0 != MIDI->Open(Callback, this))
|
||||
{
|
||||
|
@ -251,7 +258,10 @@ void MIDIStreamer::Pause ()
|
|||
if (m_Status == STATE_Playing)
|
||||
{
|
||||
m_Status = STATE_Paused;
|
||||
OutputVolume(0);
|
||||
if (!MIDI->Pause(true))
|
||||
{
|
||||
OutputVolume(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,7 +278,10 @@ void MIDIStreamer::Resume ()
|
|||
{
|
||||
if (m_Status == STATE_Paused)
|
||||
{
|
||||
OutputVolume(Volume);
|
||||
if (!MIDI->Pause(false))
|
||||
{
|
||||
OutputVolume(Volume);
|
||||
}
|
||||
m_Status = STATE_Playing;
|
||||
}
|
||||
}
|
||||
|
@ -328,12 +341,18 @@ bool MIDIStreamer::IsPlaying ()
|
|||
|
||||
void MIDIStreamer::MusicVolumeChanged ()
|
||||
{
|
||||
float realvolume = clamp<float>(snd_musicvolume * relative_volume, 0.f, 1.f);
|
||||
DWORD onechanvol = clamp<DWORD>((DWORD)(realvolume * 65535.f), 0, 65535);
|
||||
Volume = onechanvol;
|
||||
if (MIDI->FakeVolume())
|
||||
{
|
||||
float realvolume = clamp<float>(snd_musicvolume * relative_volume, 0.f, 1.f);
|
||||
Volume = clamp<DWORD>((DWORD)(realvolume * 65535.f), 0, 65535);
|
||||
}
|
||||
else
|
||||
{
|
||||
Volume = 0xFFFF;
|
||||
}
|
||||
if (m_Status == STATE_Playing)
|
||||
{
|
||||
OutputVolume(onechanvol);
|
||||
OutputVolume(Volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -347,8 +366,11 @@ void MIDIStreamer::MusicVolumeChanged ()
|
|||
|
||||
void MIDIStreamer::OutputVolume (DWORD volume)
|
||||
{
|
||||
NewVolume = volume;
|
||||
VolumeChanged = true;
|
||||
if (MIDI->FakeVolume())
|
||||
{
|
||||
NewVolume = volume;
|
||||
VolumeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
#define MAX_TIME (140/20) // Each stream buffer lasts only 1/20 of a second
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
|
@ -91,7 +89,7 @@ static const BYTE CtrlTranslate[15] =
|
|||
//==========================================================================
|
||||
|
||||
MUSSong2::MUSSong2 (FILE *file, char *musiccache, int len)
|
||||
: MusHeader(0), MusBuffer(0)
|
||||
: MIDIStreamer(false), MusHeader(0), MusBuffer(0)
|
||||
{
|
||||
if (ExitEvent == NULL)
|
||||
{
|
||||
|
|
|
@ -203,6 +203,20 @@ void WinMIDIDevice::Stop()
|
|||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// WinMIDIDevice :: Pause
|
||||
//
|
||||
// Some docs claim pause is unreliable and can cause the stream to stop
|
||||
// functioning entirely. Truth or fiction?
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool WinMIDIDevice::Pause(bool paused)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// WinMIDIDevice :: StreamOut
|
||||
|
@ -236,6 +250,20 @@ int WinMIDIDevice::UnprepareHeader(MIDIHDR *header)
|
|||
return midiOutUnprepareHeader((HMIDIOUT)MidiOut, header, sizeof(MIDIHDR));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// WinMIDIDevice :: FakeVolume
|
||||
//
|
||||
// Because there are too many MIDI devices out there that don't support
|
||||
// global volume changes, fake the volume for all of them.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool WinMIDIDevice::FakeVolume()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// WinMIDIDevice :: CallbackFunc static
|
||||
|
|
|
@ -2840,6 +2840,10 @@
|
|||
RelativePath="src\sound\music_mus_opl.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\oplsynth\music_opl_mididevice.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="src\sound\music_spc.cpp"
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue