- Reimplemented TiMidity++ playback so that it also can handle XMI subsongs.

SVN r2874 (trunk)
This commit is contained in:
Randy Heit 2010-10-02 03:36:19 +00:00
parent fa429ad844
commit 90dd40c58f
5 changed files with 252 additions and 312 deletions

View file

@ -326,12 +326,7 @@ static MIDIStreamer *CreateMIDIStreamer(FILE *file, BYTE *musiccache, int len, E
static MusInfo *CreateMIDISong(FILE *file, const char *filename, BYTE *musiccache, int offset, int len, EMIDIDevice devtype, EMIDIType miditype)
{
if (devtype == MIDI_Timidity)
{
assert(miditype == MIDI_MIDI);
return new TimiditySong(file, musiccache, len);
}
else if (devtype >= MIDI_Null)
if (devtype >= MIDI_Null)
{
assert(miditype == MIDI_MIDI);
if (musiccache != NULL)

View file

@ -100,7 +100,7 @@ public:
virtual void FluidSettingInt(const char *setting, int value);
virtual void FluidSettingNum(const char *setting, double value);
virtual void FluidSettingStr(const char *setting, const char *value);
virtual bool Preprocess(MIDIStreamer *song);
virtual bool Preprocess(MIDIStreamer *song, bool looping);
virtual FString GetStats();
};
@ -165,8 +165,7 @@ public:
protected:
SoundStream *Stream;
bool Started;
int SampleRate;
bool bLooping;
};
// FMOD psuedo-MIDI device --------------------------------------------------
@ -175,9 +174,51 @@ class FMODMIDIDevice : public PsuedoMIDIDevice
{
public:
int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata);
bool Preprocess(MIDIStreamer *song);
bool Preprocess(MIDIStreamer *song, bool looping);
};
// MIDI file played with TiMidity++ and possibly streamed through FMOD ------
class TimidityPPMIDIDevice : public PsuedoMIDIDevice
{
public:
TimidityPPMIDIDevice();
~TimidityPPMIDIDevice();
int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata);
bool Preprocess(MIDIStreamer *song, bool looping);
bool IsOpen() const;
int Resume();
void Stop();
bool IsOpen();
void TimidityVolumeChanged();
protected:
bool LaunchTimidity();
FTempFileName DiskName;
#ifdef _WIN32
HANDLE ReadWavePipe;
HANDLE WriteWavePipe;
HANDLE KillerEvent;
HANDLE ChildProcess;
bool Validated;
bool ValidateTimidity();
#else // _WIN32
int WavePipe[2];
pid_t ChildProcess;
#endif
FString CommandLine;
size_t LoopPos;
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
#ifdef _WIN32
static const char EventName[];
#endif
};
// Base class for software synthesizer MIDI output devices ------------------
class SoftSynthMIDIDevice : public MIDIDevice
@ -371,10 +412,10 @@ enum EMIDIDevice
MIDI_GUS,
MIDI_Fluid,
MIDI_FMOD,
MIDI_Timidity,
// only used by I_RegisterSong
MIDI_Null,
MIDI_Timidity
};
class MIDIStreamer : public MusInfo
@ -651,44 +692,6 @@ protected:
SoundStream *m_Stream;
};
// MIDI file played with Timidity and possibly streamed through FMOD --------
class TimiditySong : public StreamSong
{
public:
TimiditySong (FILE *file, BYTE *musiccache, int length);
~TimiditySong ();
void Play (bool looping, int subsong);
void Stop ();
bool IsPlaying ();
bool IsValid () const { return CommandLine.Len() > 0; }
void TimidityVolumeChanged();
protected:
void PrepTimidity ();
bool LaunchTimidity ();
FTempFileName DiskName;
#ifdef _WIN32
HANDLE ReadWavePipe;
HANDLE WriteWavePipe;
HANDLE KillerEvent;
HANDLE ChildProcess;
bool Validated;
bool ValidateTimidity ();
#else // _WIN32
int WavePipe[2];
pid_t ChildProcess;
#endif
FString CommandLine;
size_t LoopPos;
static bool FillStream (SoundStream *stream, void *buff, int len, void *userdata);
#ifdef _WIN32
static const char EventName[];
#endif
};
// MUS file played by a software OPL2 synth and streamed through FMOD -------
class OPLMUSSong : public StreamSong

View file

@ -3,31 +3,6 @@
#include "cmdlib.h"
#include "templates.h"
#if !defined(_WIN32) && 0
// Under Linux, buffer output from Timidity to try to avoid "bubbles"
// in the sound output.
class FPipeBuffer
{
public:
FPipeBuffer::FPipeBuffer (int fragSize, int nFrags, int pipe);
~FPipeBuffer ();
int ReadFrag (BYTE *Buf);
private:
int PipeHandle;
int FragSize;
int BuffSize;
int WritePos, ReadPos;
BYTE *Buffer;
bool GotFull;
SDL_mutex *BufferMutex;
SDL_thread *Reader;
static int ThreadProc (void *data);
};
#endif
#ifndef _WIN32
#include <unistd.h>
@ -45,7 +20,7 @@ void ChildSigHandler (int signum)
#endif
#ifdef _WIN32
const char TimiditySong::EventName[] = "TiMidity Killer";
const char TimidityPPMIDIDevice::EventName[] = "TiMidity Killer";
static char TimidityTitle[] = "TiMidity (ZDoom Launched)";
CVAR (String, timidity_exe, "timidity.exe", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
@ -58,6 +33,7 @@ CVAR (String, timidity_reverb, "0", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, timidity_stereo, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, timidity_8bit, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, timidity_byteswap, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
// added because Timidity's output is rather loud.
CUSTOM_CVAR (Float, timidity_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
@ -69,8 +45,7 @@ CUSTOM_CVAR (Float, timidity_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
currSong->TimidityVolumeChanged();
}
CUSTOM_CVAR (Int, timidity_pipe, 60, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR (Int, timidity_pipe, 90, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{ // pipe size in ms
if (timidity_pipe < 0)
{ // a negative size makes no sense
@ -86,64 +61,42 @@ CUSTOM_CVAR (Int, timidity_frequency, 22050, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
self = 65000;
}
void TimiditySong::Play (bool looping, int subsong)
{
m_Status = STATE_Stopped;
m_Looping = looping;
//==========================================================================
//
// TimidityPPMIDIDevice Constructor
//
//==========================================================================
if (LaunchTimidity ())
{
if (m_Stream != NULL)
{
if (m_Stream->Play (true, timidity_mastervolume))
{
m_Status = STATE_Playing;
}
}
else
{ // Assume success if not mixing with FMOD
m_Status = STATE_Playing;
}
}
}
void TimiditySong::Stop ()
{
if (m_Status != STATE_Stopped)
{
if (m_Stream != NULL)
{
m_Stream->Stop ();
}
TimidityPPMIDIDevice::TimidityPPMIDIDevice()
: DiskName("zmid"),
#ifdef _WIN32
if (ChildProcess != INVALID_HANDLE_VALUE)
{
SetEvent (KillerEvent);
if (WaitForSingleObject (ChildProcess, 500) != WAIT_OBJECT_0)
{
TerminateProcess (ChildProcess, 666);
}
CloseHandle (ChildProcess);
ChildProcess = INVALID_HANDLE_VALUE;
}
ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE),
KillerEvent(INVALID_HANDLE_VALUE),
ChildProcess(INVALID_HANDLE_VALUE),
Validated(false)
#else
if (ChildProcess != -1)
{
if (kill (ChildProcess, SIGTERM) != 0)
{
kill (ChildProcess, SIGKILL);
}
waitpid (ChildProcess, NULL, 0);
ChildProcess = -1;
}
ChildProcess(-1)
#endif
{
#ifndef _WIN32
WavePipe[0] = WavePipe[1] = -1;
#endif
if (DiskName == NULL)
{
Printf(PRINT_BOLD, "Could not create temp music file\n");
return;
}
m_Status = STATE_Stopped;
}
TimiditySong::~TimiditySong ()
//==========================================================================
//
// TimidityPPMIDIDevice Destructor
//
//==========================================================================
TimidityPPMIDIDevice::~TimidityPPMIDIDevice ()
{
Stop ();
#if _WIN32
if (WriteWavePipe != INVALID_HANDLE_VALUE)
{
@ -174,68 +127,53 @@ TimiditySong::~TimiditySong ()
#endif
}
TimiditySong::TimiditySong (FILE *file, BYTE *musiccache, int len)
: DiskName ("zmid"),
#ifdef _WIN32
ReadWavePipe (INVALID_HANDLE_VALUE), WriteWavePipe (INVALID_HANDLE_VALUE),
KillerEvent (INVALID_HANDLE_VALUE),
ChildProcess (INVALID_HANDLE_VALUE),
Validated (false)
#else
ChildProcess (-1)
#endif
//==========================================================================
//
// TimidityPPMIDIDevice :: Preprocess
//
//==========================================================================
bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
{
TArray<BYTE> midi;
bool success;
FILE *f;
#ifndef _WIN32
WavePipe[0] = WavePipe[1] = -1;
#endif
if (DiskName == NULL)
if (CommandLine.IsEmpty())
{
Printf (PRINT_BOLD, "Could not create temp music file\n");
return;
return false;
}
f = fopen (DiskName, "wb");
// Tell TiMidity++ whether it should loop or not
CommandLine.LockBuffer()[LoopPos] = looping ? 'l' : ' ';
CommandLine.UnlockBuffer();
// Write MIDI song to temporary file
song->CreateSMF(midi);
f = fopen(DiskName, "wb");
if (f == NULL)
{
Printf (PRINT_BOLD, "Could not open temp music file\n");
return;
Printf(PRINT_BOLD, "Could not open temp music file\n");
return false;
}
BYTE *buf;
if (file != NULL)
{
buf = new BYTE[len];
fread (buf, 1, len, file);
}
else
{
buf = musiccache;
}
// Write to temporary file
success = (fwrite (buf, 1, len, f) == (size_t)len);
success = (fwrite(&midi[0], 1, midi.Size(), f) == (size_t)midi.Size());
fclose (f);
if (file != NULL)
{
delete[] buf;
}
if (success)
if (!success)
{
PrepTimidity ();
}
else
{
Printf (PRINT_BOLD, "Could not write temp music file\n");
Printf(PRINT_BOLD, "Could not write temp music file\n");
}
return false;
}
void TimiditySong::PrepTimidity ()
//==========================================================================
//
// TimidityPPMIDIDevice :: Open
//
//==========================================================================
int TimidityPPMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
{
int pipeSize;
@ -244,19 +182,19 @@ void TimiditySong::PrepTimidity ()
if (!Validated && !ValidateTimidity ())
{
return;
return 101;
}
Validated = true;
KillerEvent = CreateEvent (NULL, FALSE, FALSE, EventName);
KillerEvent = CreateEvent(NULL, FALSE, FALSE, EventName);
if (KillerEvent == INVALID_HANDLE_VALUE)
{
Printf (PRINT_BOLD, "Could not create TiMidity++ kill event.\n");
return;
Printf(PRINT_BOLD, "Could not create TiMidity++ kill event.\n");
return 102;
}
#endif // WIN32
CommandLine.Format ("%s %s -EFchorus=%s -EFreverb=%s -s%d ",
CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ",
*timidity_exe, *timidity_extargs,
*timidity_chorus, *timidity_reverb, *timidity_frequency);
@ -274,31 +212,31 @@ void TimiditySong::PrepTimidity ()
bitmask <<= 1;
pipeSize = bitmask;
if (!CreatePipe (&ReadWavePipe, &WriteWavePipe, &inheritable, pipeSize))
if (!CreatePipe(&ReadWavePipe, &WriteWavePipe, &inheritable, pipeSize))
#else // WIN32
if (pipe (WavePipe) == -1)
#endif
{
Printf (PRINT_BOLD, "Could not create a data pipe for TiMidity++.\n");
Printf(PRINT_BOLD, "Could not create a data pipe for TiMidity++.\n");
pipeSize = 0;
}
else
{
m_Stream = GSnd->CreateStream (FillStream, pipeSize,
Stream = GSnd->CreateStream(FillStream, pipeSize,
(timidity_stereo ? 0 : SoundStream::Mono) |
(timidity_8bit ? SoundStream::Bits8 : 0),
timidity_frequency, this);
if (m_Stream == NULL)
if (Stream == NULL)
{
Printf (PRINT_BOLD, "Could not create music stream.\n");
Printf(PRINT_BOLD, "Could not create music stream.\n");
pipeSize = 0;
#ifdef _WIN32
CloseHandle (WriteWavePipe);
CloseHandle (ReadWavePipe);
CloseHandle(WriteWavePipe);
CloseHandle(ReadWavePipe);
ReadWavePipe = WriteWavePipe = INVALID_HANDLE_VALUE;
#else
close (WavePipe[1]);
close (WavePipe[0]);
close(WavePipe[1]);
close(WavePipe[0]);
WavePipe[0] = WavePipe[1] = -1;
#endif
}
@ -306,7 +244,7 @@ void TimiditySong::PrepTimidity ()
if (pipeSize == 0)
{
Printf (PRINT_BOLD, "If your soundcard cannot play more than one\n"
Printf(PRINT_BOLD, "If your soundcard cannot play more than one\n"
"wave at a time, you will hear no music.\n");
}
else
@ -331,14 +269,22 @@ void TimiditySong::PrepTimidity ()
CommandLine += " -idl ";
CommandLine += DiskName.GetName();
return 0;
}
#ifdef _WIN32
//==========================================================================
//
// TimidityPPMIDIDevice :: ValidateTimidity
//
// Check that this TiMidity++ knows about the TiMidity Killer event.
// If not, then we can't use it, because Win32 provides no other way
// to conveniently signal it to quit. The check is done by simply
// searching for the event's name somewhere in the executable.
bool TimiditySong::ValidateTimidity ()
//
//==========================================================================
#ifdef _WIN32
bool TimidityPPMIDIDevice::ValidateTimidity()
{
char foundPath[MAX_PATH];
char *filePart;
@ -354,12 +300,12 @@ bool TimiditySong::ValidateTimidity ()
pathLen = SearchPath (NULL, timidity_exe, NULL, MAX_PATH, foundPath, &filePart);
if (pathLen == 0)
{
Printf (PRINT_BOLD, "Please set the timidity_exe cvar to the location of TiMidity++\n");
Printf(PRINT_BOLD, "Please set the timidity_exe cvar to the location of TiMidity++\n");
return false;
}
if (pathLen > MAX_PATH)
{
Printf (PRINT_BOLD, "The path to TiMidity++ is too long\n");
Printf(PRINT_BOLD, "The path to TiMidity++ is too long\n");
return false;
}
@ -367,21 +313,21 @@ bool TimiditySong::ValidateTimidity ()
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (diskFile == INVALID_HANDLE_VALUE)
{
Printf (PRINT_BOLD, "Could not access %s\n", foundPath);
Printf(PRINT_BOLD, "Could not access %s\n", foundPath);
return false;
}
fileLen = GetFileSize (diskFile, NULL);
mapping = CreateFileMapping (diskFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (mapping == NULL)
{
Printf (PRINT_BOLD, "Could not create mapping for %s\n", foundPath);
Printf(PRINT_BOLD, "Could not create mapping for %s\n", foundPath);
CloseHandle (diskFile);
return false;
}
exeBase = (const BYTE *)MapViewOfFile (mapping, FILE_MAP_READ, 0, 0, 0);
if (exeBase == NULL)
{
Printf (PRINT_BOLD, "Could not map %s\n", foundPath);
Printf(PRINT_BOLD, "Could not map %s\n", foundPath);
CloseHandle (mapping);
CloseHandle (diskFile);
return false;
@ -392,12 +338,12 @@ bool TimiditySong::ValidateTimidity ()
{
for (exe = exeBase, exeEnd = exeBase+fileLen; exe < exeEnd; )
{
const char *tSpot = (const char *)memchr (exe, 'T', exeEnd - exe);
const char *tSpot = (const char *)memchr(exe, 'T', exeEnd - exe);
if (tSpot == NULL)
{
break;
}
if (memcmp (tSpot+1, EventName+1, sizeof(EventName)-1) == 0)
if (memcmp(tSpot+1, EventName+1, sizeof(EventName)-1) == 0)
{
good = true;
break;
@ -407,32 +353,35 @@ bool TimiditySong::ValidateTimidity ()
}
catch (...)
{
Printf (PRINT_BOLD, "Error reading %s\n", foundPath);
Printf(PRINT_BOLD, "Error reading %s\n", foundPath);
}
if (!good)
{
Printf (PRINT_BOLD, "ZDoom requires a special version of TiMidity++\n");
Printf(PRINT_BOLD, "ZDoom requires a special version of TiMidity++\n");
}
UnmapViewOfFile ((LPVOID)exeBase);
CloseHandle (mapping);
CloseHandle (diskFile);
UnmapViewOfFile((LPVOID)exeBase);
CloseHandle(mapping);
CloseHandle(diskFile);
return good;
}
#endif // _WIN32
bool TimiditySong::LaunchTimidity ()
//==========================================================================
//
// TimidityPPMIDIDevice :: LaunchTimidity
//
//==========================================================================
bool TimidityPPMIDIDevice::LaunchTimidity ()
{
if (CommandLine.IsEmpty())
{
return false;
}
// Tell Timidity whether it should loop or not
char *cmdline = CommandLine.LockBuffer();
cmdline[LoopPos] = m_Looping ? 'l' : ' ';
DPrintf ("cmd: \x1cG%s\n", cmdline);
DPrintf ("cmd: \x1cG%s\n", CommandLine.GetChars());
#ifdef _WIN32
STARTUPINFO startup = { sizeof(startup), };
@ -448,7 +397,7 @@ bool TimiditySong::LaunchTimidity ()
startup.lpTitle = TimidityTitle;
startup.wShowWindow = SW_SHOWMINNOACTIVE;
if (CreateProcess (NULL, cmdline, NULL, NULL, TRUE,
if (CreateProcess(NULL, CommandLine.LockBuffer(), NULL, NULL, TRUE,
/*HIGH_PRIORITY_CLASS|*/DETACHED_PROCESS, NULL, NULL, &startup, &procInfo))
{
ChildProcess = procInfo.hProcess;
@ -461,18 +410,18 @@ bool TimiditySong::LaunchTimidity ()
char hres[9];
LPTSTR msgBuf;
HRESULT err = GetLastError ();
HRESULT err = GetLastError();
if (!FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, 0, (LPTSTR)&msgBuf, 0, NULL))
{
mysnprintf (hres, countof(hres), "%08lx", err);
mysnprintf(hres, countof(hres), "%08lx", err);
msgBuf = hres;
}
Printf (PRINT_BOLD, "Could not run timidity with the command line:\n%s\n"
Printf(PRINT_BOLD, "Could not run timidity with the command line:\n%s\n"
"Reason: %s\n", CommandLine.GetChars(), msgBuf);
if (msgBuf != hres)
{
@ -543,14 +492,20 @@ bool TimiditySong::LaunchTimidity ()
#endif // _WIN32
}
bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *userdata)
//==========================================================================
//
// TimidityPPMIDIDevice :: FillStream
//
//==========================================================================
bool TimidityPPMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata)
{
TimiditySong *song = (TimiditySong *)userdata;
TimidityPPMIDIDevice *song = (TimidityPPMIDIDevice *)userdata;
#ifdef _WIN32
DWORD avail, got, didget;
if (!PeekNamedPipe (song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0)
if (!PeekNamedPipe(song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0)
{ // If nothing is available from the pipe, play silence.
memset (buff, 0, len);
}
@ -559,14 +514,14 @@ bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *u
didget = 0;
for (;;)
{
ReadFile (song->ReadWavePipe, (BYTE *)buff+didget, len-didget, &got, NULL);
ReadFile(song->ReadWavePipe, (BYTE *)buff+didget, len-didget, &got, NULL);
didget += got;
if (didget >= (DWORD)len)
break;
// Give TiMidity a chance to output something more to the pipe
// Give TiMidity++ a chance to output something more to the pipe
Sleep (10);
if (!PeekNamedPipe (song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0)
if (!PeekNamedPipe(song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0)
{
memset ((BYTE *)buff+didget, 0, len-didget);
break;
@ -581,7 +536,7 @@ bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *u
if (ChildQuit == song->ChildProcess)
{
ChildQuit = 0;
fprintf (stderr, "child gone\n");
fprintf(stderr, "child gone\n");
song->ChildProcess = -1;
return false;
}
@ -599,32 +554,44 @@ bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *u
}
// fprintf(stderr,"something\n");
got = read (song->WavePipe[0], (BYTE *)buff, len);
got = read(song->WavePipe[0], (BYTE *)buff, len);
if (got < len)
{
memset ((BYTE *)buff+got, 0, len-got);
memset((BYTE *)buff+got, 0, len-got);
}
#endif
return true;
}
void TimiditySong::TimidityVolumeChanged()
//==========================================================================
//
// TimidityPPMIDIDevice :: TimidityVolumeChanged
//
//==========================================================================
void TimidityPPMIDIDevice::TimidityVolumeChanged()
{
if (m_Stream != NULL)
if (Stream != NULL)
{
m_Stream->SetVolume(timidity_mastervolume);
Stream->SetVolume(timidity_mastervolume);
}
}
bool TimiditySong::IsPlaying ()
//==========================================================================
//
// TimidityPPMIDIDevice :: IsOpen
//
//==========================================================================
bool TimidityPPMIDIDevice::IsOpen() const
{
#ifdef _WIN32
if (ChildProcess != INVALID_HANDLE_VALUE)
{
if (WaitForSingleObject (ChildProcess, 0) != WAIT_TIMEOUT)
{ // Timidity has quit
CloseHandle (ChildProcess);
ChildProcess = INVALID_HANDLE_VALUE;
if (WaitForSingleObject(ChildProcess, 0) != WAIT_TIMEOUT)
{ // Timidity++ has quit
CloseHandle(ChildProcess);
const_cast<TimidityPPMIDIDevice *>(this)->ChildProcess = INVALID_HANDLE_VALUE;
#else
if (ChildProcess != -1)
{
@ -632,15 +599,6 @@ bool TimiditySong::IsPlaying ()
{
ChildProcess = -1;
#endif
if (m_Looping)
{
if (!LaunchTimidity ())
{
Stop ();
return false;
}
return true;
}
return false;
}
return true;
@ -648,86 +606,66 @@ bool TimiditySong::IsPlaying ()
return false;
}
#if !defined(_WIN32) && 0
FPipeBuffer::FPipeBuffer (int fragSize, int nFrags, int pipe)
: PipeHandle (pipe),
FragSize (fragSize),
BuffSize (fragSize * nFrags),
WritePos (0), ReadPos (0), GotFull (false),
Reader (0), PleaseExit (0)
//==========================================================================
//
// TimidityPPMIDIDevice :: Resume
//
//==========================================================================
int TimidityPPMIDIDevice::Resume()
{
Buffer = new BYTE[BuffSize];
if (Buffer != NULL)
if (!Started)
{
BufferMutex = SDL_CreateMutex ();
if (BufferMutex == NULL)
if (LaunchTimidity())
{
Reader = SDL_CreateThread (ThreadProc, (void *)this);
// Assume success if not mixing with FMOD
if (Stream == NULL || Stream->Play(true, timidity_mastervolume))
{
Started = true;
return 0;
}
}
return 1;
}
return 0;
}
FPipeBuffer::~FPipeBuffer ()
{
if (Reader != NULL)
{ // I like the Win32 IPC facilities better than SDL's
// Fortunately, this is a simple thread, so I can cheat
// like this.
SDL_KillThread (ThreadProc);
}
if (BufferMutex != NULL)
{
SDL_DestroyMutex (BufferMutex);
}
if (Buffer != NULL)
{
delete[] Buffer;
}
}
//==========================================================================
//
// TimidityPPMIDIDevice :: Stop
//
//==========================================================================
int FPipeBuffer::ReadFrag (BYTE *buf)
void TimidityPPMIDIDevice::Stop ()
{
int startavvail;
int avail;
int pos;
int opos;
if (SDL_mutexP (BufferMutex) == -1)
return 0;
if (WritePos > ReadPos)
if (Started)
{
avail = WritePos - ReadPos;
}
else
{
avail = BuffSize - ReadPos + WritePos;
}
if (avail > FragSize)
avail = FragSize;
startavail = avali;
pos = ReadPos;
opos = 0;
while (avail != 0)
{
int thistime;
thistime = (pos + avail > BuffSize) ? BuffSize - pos : avail;
memcpy (buf + opos, Buffer + pos, thistime);
if (thistime != avail)
if (Stream != NULL)
{
pos = 0;
avail -= thistime;
Stream->Stop();
}
opos += thistime;
#ifdef _WIN32
if (ChildProcess != INVALID_HANDLE_VALUE)
{
SetEvent(KillerEvent);
if (WaitForSingleObject(ChildProcess, 500) != WAIT_OBJECT_0)
{
TerminateProcess(ChildProcess, 666);
}
CloseHandle(ChildProcess);
ChildProcess = INVALID_HANDLE_VALUE;
}
#else
if (ChildProcess != -1)
{
if (kill(ChildProcess, SIGTERM) != 0)
{
kill(ChildProcess, SIGKILL);
}
waitpid(ChildProcess, NULL, 0);
ChildProcess = -1;
}
#endif
}
ReadPos = pos;
SDL_mutexV (BufferMutex);
return startavail;
Started = false;
}
#endif // !_WIN32

View file

@ -251,6 +251,10 @@ void MIDIStreamer::Play(bool looping, int subsong)
MIDI = new OPLMIDIDevice;
break;
case MIDI_Timidity:
MIDI = new TimidityPPMIDIDevice;
break;
default:
MIDI = NULL;
break;
@ -269,7 +273,7 @@ void MIDIStreamer::Play(bool looping, int subsong)
SetMIDISubsong(subsong);
CheckCaps(MIDI->GetTechnology());
if (MIDI->Preprocess(this))
if (MIDI->Preprocess(this, looping))
{
StartPlayback();
}
@ -1259,7 +1263,7 @@ void MIDIDevice::PrecacheInstruments(const WORD *instruments, int count)
//
//==========================================================================
bool MIDIDevice::Preprocess(MIDIStreamer *song)
bool MIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
{
return true;
}

View file

@ -67,7 +67,7 @@ PsuedoMIDIDevice::PsuedoMIDIDevice()
{
Stream = NULL;
Started = false;
SampleRate = GSnd != NULL ? (int)GSnd->GetOutputRate() : 44100;
bLooping = true;
}
//==========================================================================
@ -129,7 +129,7 @@ int PsuedoMIDIDevice::Resume()
{
if (!Started)
{
if (Stream->Play(true, 1))
if (Stream->Play(bLooping, 1))
{
Started = true;
return 0;
@ -250,11 +250,11 @@ int FMODMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), v
//
//==========================================================================
bool FMODMIDIDevice::Preprocess(MIDIStreamer *song)
bool FMODMIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
{
TArray<BYTE> midi;
song->CreateSMF(midi);
Stream = GSnd->OpenStream((char *)&midi[0], SoundStream::Loop, -1, midi.Size());
Stream = GSnd->OpenStream((char *)&midi[0], looping ? SoundStream::Loop : 0, -1, midi.Size());
return false;
}