- 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) static MusInfo *CreateMIDISong(FILE *file, const char *filename, BYTE *musiccache, int offset, int len, EMIDIDevice devtype, EMIDIType miditype)
{ {
if (devtype == MIDI_Timidity) if (devtype >= MIDI_Null)
{
assert(miditype == MIDI_MIDI);
return new TimiditySong(file, musiccache, len);
}
else if (devtype >= MIDI_Null)
{ {
assert(miditype == MIDI_MIDI); assert(miditype == MIDI_MIDI);
if (musiccache != NULL) if (musiccache != NULL)

View file

@ -100,7 +100,7 @@ public:
virtual void FluidSettingInt(const char *setting, int value); virtual void FluidSettingInt(const char *setting, int value);
virtual void FluidSettingNum(const char *setting, double value); virtual void FluidSettingNum(const char *setting, double value);
virtual void FluidSettingStr(const char *setting, const char *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(); virtual FString GetStats();
}; };
@ -165,8 +165,7 @@ public:
protected: protected:
SoundStream *Stream; SoundStream *Stream;
bool Started; bool Started;
int SampleRate; bool bLooping;
}; };
// FMOD psuedo-MIDI device -------------------------------------------------- // FMOD psuedo-MIDI device --------------------------------------------------
@ -175,9 +174,51 @@ class FMODMIDIDevice : public PsuedoMIDIDevice
{ {
public: public:
int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); 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 ------------------ // Base class for software synthesizer MIDI output devices ------------------
class SoftSynthMIDIDevice : public MIDIDevice class SoftSynthMIDIDevice : public MIDIDevice
@ -371,10 +412,10 @@ enum EMIDIDevice
MIDI_GUS, MIDI_GUS,
MIDI_Fluid, MIDI_Fluid,
MIDI_FMOD, MIDI_FMOD,
MIDI_Timidity,
// only used by I_RegisterSong // only used by I_RegisterSong
MIDI_Null, MIDI_Null,
MIDI_Timidity
}; };
class MIDIStreamer : public MusInfo class MIDIStreamer : public MusInfo
@ -651,44 +692,6 @@ protected:
SoundStream *m_Stream; 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 ------- // MUS file played by a software OPL2 synth and streamed through FMOD -------
class OPLMUSSong : public StreamSong class OPLMUSSong : public StreamSong

View file

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

View file

@ -251,6 +251,10 @@ void MIDIStreamer::Play(bool looping, int subsong)
MIDI = new OPLMIDIDevice; MIDI = new OPLMIDIDevice;
break; break;
case MIDI_Timidity:
MIDI = new TimidityPPMIDIDevice;
break;
default: default:
MIDI = NULL; MIDI = NULL;
break; break;
@ -269,7 +273,7 @@ void MIDIStreamer::Play(bool looping, int subsong)
SetMIDISubsong(subsong); SetMIDISubsong(subsong);
CheckCaps(MIDI->GetTechnology()); CheckCaps(MIDI->GetTechnology());
if (MIDI->Preprocess(this)) if (MIDI->Preprocess(this, looping))
{ {
StartPlayback(); 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; return true;
} }

View file

@ -67,7 +67,7 @@ PsuedoMIDIDevice::PsuedoMIDIDevice()
{ {
Stream = NULL; Stream = NULL;
Started = false; Started = false;
SampleRate = GSnd != NULL ? (int)GSnd->GetOutputRate() : 44100; bLooping = true;
} }
//========================================================================== //==========================================================================
@ -129,7 +129,7 @@ int PsuedoMIDIDevice::Resume()
{ {
if (!Started) if (!Started)
{ {
if (Stream->Play(true, 1)) if (Stream->Play(bLooping, 1))
{ {
Started = true; Started = true;
return 0; 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; TArray<BYTE> midi;
song->CreateSMF(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; return false;
} }