2006-02-24 04:48:15 +00:00
|
|
|
#include "i_musicinterns.h"
|
|
|
|
#include "c_cvars.h"
|
|
|
|
#include "cmdlib.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>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <wordexp.h>
|
|
|
|
|
|
|
|
int ChildQuit;
|
|
|
|
|
|
|
|
void ChildSigHandler (int signum)
|
|
|
|
{
|
|
|
|
ChildQuit = waitpid (-1, NULL, WNOHANG);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
const char TimiditySong::EventName[] = "TiMidity Killer";
|
|
|
|
|
|
|
|
CVAR (String, timidity_exe, "timidity.exe", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
#else
|
|
|
|
CVAR (String, timidity_exe, "timidity", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
#endif
|
|
|
|
CVAR (String, timidity_extargs, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // extra args to pass to Timidity
|
|
|
|
CVAR (String, timidity_chorus, "0", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
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)
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, timidity_pipe, 60, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
{ // pipe size in ms
|
|
|
|
if (timidity_pipe < 0)
|
|
|
|
{ // a negative size makes no sense
|
|
|
|
timidity_pipe = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, timidity_frequency, 22050, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
{ // Clamp frequency to Timidity's limits
|
|
|
|
if (self < 4000)
|
|
|
|
self = 4000;
|
|
|
|
else if (self > 65000)
|
|
|
|
self = 65000;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TimiditySong::Play (bool looping)
|
|
|
|
{
|
|
|
|
m_Status = STATE_Stopped;
|
|
|
|
m_Looping = looping;
|
|
|
|
|
|
|
|
if (LaunchTimidity ())
|
|
|
|
{
|
|
|
|
if (m_Stream != NULL)
|
|
|
|
{
|
|
|
|
if (m_Stream->Play (snd_musicvolume))
|
|
|
|
{
|
|
|
|
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
|
|
|
|
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
|
|
|
|
}
|
|
|
|
m_Status = STATE_Stopped;
|
|
|
|
}
|
|
|
|
|
|
|
|
TimiditySong::~TimiditySong ()
|
|
|
|
{
|
|
|
|
Stop ();
|
|
|
|
#if _WIN32
|
|
|
|
if (WriteWavePipe != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
CloseHandle (WriteWavePipe);
|
|
|
|
WriteWavePipe = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
if (ReadWavePipe != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
CloseHandle (ReadWavePipe);
|
|
|
|
ReadWavePipe = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
if (KillerEvent != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
CloseHandle (KillerEvent);
|
|
|
|
KillerEvent = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (WavePipe[1] != -1)
|
|
|
|
{
|
|
|
|
close (WavePipe[1]);
|
|
|
|
WavePipe[1] = -1;
|
|
|
|
}
|
|
|
|
if (WavePipe[0] != -1)
|
|
|
|
{
|
|
|
|
close (WavePipe[0]);
|
|
|
|
WavePipe[0] = -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2006-04-14 12:58:52 +00:00
|
|
|
TimiditySong::TimiditySong (FILE *file, char * musiccache, int len)
|
2006-02-24 04:48:15 +00:00
|
|
|
: DiskName ("zmid"),
|
|
|
|
#ifdef _WIN32
|
|
|
|
ReadWavePipe (INVALID_HANDLE_VALUE), WriteWavePipe (INVALID_HANDLE_VALUE),
|
|
|
|
KillerEvent (INVALID_HANDLE_VALUE),
|
|
|
|
ChildProcess (INVALID_HANDLE_VALUE),
|
2006-04-09 19:34:35 +00:00
|
|
|
Validated (false)
|
2006-02-24 04:48:15 +00:00
|
|
|
#else
|
2006-04-09 19:34:35 +00:00
|
|
|
ChildProcess (-1)
|
2006-02-24 04:48:15 +00:00
|
|
|
#endif
|
|
|
|
{
|
|
|
|
bool success;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
WavePipe[0] = WavePipe[1] = -1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (DiskName == NULL)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "Could not create temp music file\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
f = fopen (DiskName, "wb");
|
|
|
|
if (f == NULL)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "Could not open temp music file\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-04-14 12:58:52 +00:00
|
|
|
BYTE *buf;
|
|
|
|
|
|
|
|
if (file!=NULL)
|
|
|
|
{
|
|
|
|
buf = new BYTE[len];
|
|
|
|
fread (buf, 1, len, file);
|
|
|
|
}
|
|
|
|
else buf = (BYTE*)musiccache;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// The file type has already been checked before this class instance was
|
|
|
|
// created, so we only need to check one character to determine if this
|
|
|
|
// is a MUS or MIDI file and write it to disk as appropriate.
|
|
|
|
if (buf[1] == 'T')
|
|
|
|
{
|
|
|
|
success = (fwrite (buf, 1, len, f) == (size_t)len);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
success = ProduceMIDI (buf, f);
|
|
|
|
}
|
2006-04-14 12:58:52 +00:00
|
|
|
if (file!=NULL)
|
|
|
|
{
|
|
|
|
fclose (f);
|
|
|
|
delete[] buf;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
PrepTimidity ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "Could not write temp music file\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TimiditySong::PrepTimidity ()
|
|
|
|
{
|
|
|
|
int pipeSize;
|
|
|
|
#ifdef _WIN32
|
|
|
|
static SECURITY_ATTRIBUTES inheritable = { sizeof(inheritable), NULL, TRUE };
|
|
|
|
|
|
|
|
if (!Validated && !ValidateTimidity ())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Validated = true;
|
|
|
|
KillerEvent = CreateEvent (NULL, FALSE, FALSE, EventName);
|
|
|
|
if (KillerEvent == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "Could not create TiMidity++ kill event.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif // WIN32
|
|
|
|
|
2006-04-09 19:34:35 +00:00
|
|
|
CommandLine.Format ("%s %s -EFchorus=%s -EFreverb=%s -s%d ",
|
2006-02-24 04:48:15 +00:00
|
|
|
*timidity_exe, *timidity_extargs,
|
|
|
|
*timidity_chorus, *timidity_reverb, *timidity_frequency);
|
|
|
|
|
|
|
|
pipeSize = (timidity_pipe * timidity_frequency / 1000)
|
|
|
|
<< (timidity_stereo + !timidity_8bit);
|
|
|
|
|
|
|
|
if (pipeSize != 0)
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Round pipe size up to nearest power of 2 to try and avoid partial
|
|
|
|
// buffer reads in FillStream() under NT. This does not seem to be an
|
|
|
|
// issue under 9x.
|
|
|
|
int bitmask = pipeSize & -pipeSize;
|
|
|
|
|
|
|
|
while (bitmask < pipeSize)
|
|
|
|
bitmask <<= 1;
|
|
|
|
pipeSize = bitmask;
|
|
|
|
|
|
|
|
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");
|
|
|
|
pipeSize = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_Stream = GSnd->CreateStream (FillStream, pipeSize,
|
|
|
|
(timidity_stereo ? 0 : SoundStream::Mono) |
|
|
|
|
(timidity_8bit ? SoundStream::Bits8 : 0),
|
|
|
|
timidity_frequency, this);
|
|
|
|
if (m_Stream == NULL)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "Could not create music stream.\n");
|
|
|
|
pipeSize = 0;
|
|
|
|
#ifdef _WIN32
|
|
|
|
CloseHandle (WriteWavePipe);
|
|
|
|
CloseHandle (ReadWavePipe);
|
|
|
|
ReadWavePipe = WriteWavePipe = INVALID_HANDLE_VALUE;
|
|
|
|
#else
|
|
|
|
close (WavePipe[1]);
|
|
|
|
close (WavePipe[0]);
|
|
|
|
WavePipe[0] = WavePipe[1] = -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pipeSize == 0)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "If your soundcard cannot play more than one\n"
|
|
|
|
"wave at a time, you will hear no music.\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-09 19:34:35 +00:00
|
|
|
CommandLine += "-o - -Ors";
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pipeSize == 0)
|
|
|
|
{
|
2006-04-09 19:34:35 +00:00
|
|
|
CommandLine += "-Od";
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2006-04-09 19:34:35 +00:00
|
|
|
CommandLine += timidity_stereo ? 'S' : 'M';
|
|
|
|
CommandLine += timidity_8bit ? '8' : '1';
|
2006-02-24 04:48:15 +00:00
|
|
|
if (timidity_byteswap)
|
|
|
|
{
|
2006-04-09 19:34:35 +00:00
|
|
|
CommandLine += 'x';
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2006-04-09 19:34:35 +00:00
|
|
|
LoopPos = CommandLine.Len() + 4;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-04-09 19:34:35 +00:00
|
|
|
CommandLine += " -idl ";
|
|
|
|
CommandLine += DiskName.GetName();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
// 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 ()
|
|
|
|
{
|
|
|
|
char foundPath[MAX_PATH];
|
|
|
|
char *filePart;
|
|
|
|
DWORD pathLen;
|
|
|
|
DWORD fileLen;
|
|
|
|
HANDLE diskFile;
|
|
|
|
HANDLE mapping;
|
|
|
|
const BYTE *exeBase;
|
|
|
|
const BYTE *exeEnd;
|
|
|
|
const BYTE *exe;
|
|
|
|
bool good;
|
|
|
|
|
|
|
|
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");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (pathLen > MAX_PATH)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "The path to TiMidity++ is too long\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
diskFile = CreateFile (foundPath, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
|
|
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
|
|
if (diskFile == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
CloseHandle (mapping);
|
|
|
|
CloseHandle (diskFile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
good = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
for (exe = exeBase, exeEnd = exeBase+fileLen; exe < exeEnd; )
|
|
|
|
{
|
|
|
|
const char *tSpot = (const char *)memchr (exe, 'T', exeEnd - exe);
|
|
|
|
if (tSpot == NULL)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (memcmp (tSpot+1, EventName+1, sizeof(EventName)-1) == 0)
|
|
|
|
{
|
|
|
|
good = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
exe = (const BYTE *)tSpot + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "Error reading %s\n", foundPath);
|
|
|
|
}
|
|
|
|
if (!good)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "ZDoom requires a special version of TiMidity++\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
UnmapViewOfFile ((LPVOID)exeBase);
|
|
|
|
CloseHandle (mapping);
|
|
|
|
CloseHandle (diskFile);
|
|
|
|
|
|
|
|
return good;
|
|
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
|
|
|
|
bool TimiditySong::LaunchTimidity ()
|
|
|
|
{
|
2006-05-16 02:50:18 +00:00
|
|
|
if (CommandLine.IsEmpty())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell Timidity whether it should loop or not
|
2006-05-16 02:50:18 +00:00
|
|
|
char *cmdline = CommandLine.LockBuffer();
|
|
|
|
cmdline[LoopPos] = m_Looping ? 'l' : ' ';
|
|
|
|
DPrintf ("cmd: \x1cG%s\n", cmdline);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
STARTUPINFO startup = { sizeof(startup), };
|
|
|
|
PROCESS_INFORMATION procInfo;
|
|
|
|
|
|
|
|
startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
|
|
|
|
|
|
|
startup.hStdInput = INVALID_HANDLE_VALUE;
|
|
|
|
startup.hStdOutput = WriteWavePipe != INVALID_HANDLE_VALUE ?
|
|
|
|
WriteWavePipe : GetStdHandle (STD_OUTPUT_HANDLE);
|
|
|
|
startup.hStdError = GetStdHandle (STD_ERROR_HANDLE);
|
|
|
|
|
|
|
|
startup.lpTitle = "TiMidity (ZDoom Launched)";
|
|
|
|
startup.wShowWindow = SW_SHOWMINNOACTIVE;
|
|
|
|
|
2006-05-16 02:50:18 +00:00
|
|
|
if (CreateProcess (NULL, cmdline, NULL, NULL, TRUE,
|
2006-02-24 04:48:15 +00:00
|
|
|
/*HIGH_PRIORITY_CLASS|*/DETACHED_PROCESS, NULL, NULL, &startup, &procInfo))
|
|
|
|
{
|
|
|
|
ChildProcess = procInfo.hProcess;
|
|
|
|
//SetThreadPriority (procInfo.hThread, THREAD_PRIORITY_HIGHEST);
|
|
|
|
CloseHandle (procInfo.hThread); // Don't care about the created thread
|
2006-05-16 02:50:18 +00:00
|
|
|
CommandLine.UnlockBuffer();
|
2006-02-24 04:48:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
2006-05-16 02:50:18 +00:00
|
|
|
CommandLine.UnlockBuffer();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
char hres[9];
|
|
|
|
LPTSTR msgBuf;
|
|
|
|
HRESULT err = GetLastError ();
|
|
|
|
|
|
|
|
if (!FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
NULL, err, 0, (LPTSTR)&msgBuf, 0, NULL))
|
|
|
|
{
|
|
|
|
sprintf (hres, "%08lx", err);
|
|
|
|
msgBuf = hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
Printf (PRINT_BOLD, "Could not run timidity with the command line:\n%s\n"
|
2006-04-09 19:34:35 +00:00
|
|
|
"Reason: %s\n", CommandLine.GetChars(), msgBuf);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (msgBuf != hres)
|
|
|
|
{
|
|
|
|
LocalFree (msgBuf);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
#else
|
|
|
|
if (WavePipe[0] != -1 && WavePipe[1] == -1 && m_Stream != NULL)
|
|
|
|
{
|
|
|
|
// Timidity was previously launched, so the write end of the pipe
|
|
|
|
// is closed, and the read end is still open. Close the pipe
|
|
|
|
// completely and reopen it.
|
|
|
|
|
|
|
|
close (WavePipe[0]);
|
|
|
|
WavePipe[0] = -1;
|
|
|
|
delete m_Stream;
|
|
|
|
m_Stream = NULL;
|
|
|
|
PrepTimidity ();
|
|
|
|
}
|
|
|
|
|
|
|
|
int forkres;
|
|
|
|
wordexp_t words;
|
|
|
|
|
2006-04-09 19:34:35 +00:00
|
|
|
switch (wordexp (CommandLine.GetChars(), &words, 0))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
case 0: // all good
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WRDE_NOSPACE:
|
|
|
|
wordfree (&words);
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
forkres = fork ();
|
|
|
|
|
|
|
|
if (forkres == 0)
|
|
|
|
{
|
|
|
|
close (WavePipe[0]);
|
|
|
|
dup2 (WavePipe[1], STDOUT_FILENO);
|
|
|
|
freopen ("/dev/null", "r", stdin);
|
|
|
|
freopen ("/dev/null", "w", stderr);
|
|
|
|
close (WavePipe[1]);
|
2006-04-12 01:50:09 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
printf ("exec %s\n", words.we_wordv[0]);
|
|
|
|
execvp (words.we_wordv[0], words.we_wordv);
|
|
|
|
exit (0); // if execvp succeeds, we never get here
|
|
|
|
}
|
|
|
|
else if (forkres < 0)
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "Could not fork when trying to start timidity\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// printf ("child is %d\n", forkres);
|
|
|
|
ChildProcess = forkres;
|
|
|
|
close (WavePipe[1]);
|
|
|
|
WavePipe[1] = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wordfree (&words);
|
|
|
|
return ChildProcess != -1;
|
|
|
|
#endif // _WIN32
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *userdata)
|
|
|
|
{
|
|
|
|
TimiditySong *song = (TimiditySong *)userdata;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
DWORD avail, got, didget;
|
|
|
|
|
|
|
|
if (!PeekNamedPipe (song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0)
|
|
|
|
{ // If nothing is available from the pipe, play silence.
|
|
|
|
memset (buff, 0, len);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
didget = 0;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
Sleep (10);
|
|
|
|
if (!PeekNamedPipe (song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0)
|
|
|
|
{
|
|
|
|
memset ((BYTE *)buff+didget, 0, len-didget);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
ssize_t got;
|
|
|
|
|
|
|
|
got = read (song->WavePipe[0], (BYTE *)buff, len);
|
|
|
|
if (got < len)
|
|
|
|
{
|
|
|
|
memset ((BYTE *)buff+got, 0, len-got);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ChildQuit == song->ChildProcess)
|
|
|
|
{
|
|
|
|
ChildQuit = 0;
|
|
|
|
// printf ("child gone\n");
|
|
|
|
song->ChildProcess = -1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TimiditySong::IsPlaying ()
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (ChildProcess != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
if (WaitForSingleObject (ChildProcess, 0) != WAIT_TIMEOUT)
|
|
|
|
{ // Timidity has quit
|
|
|
|
CloseHandle (ChildProcess);
|
|
|
|
ChildProcess = INVALID_HANDLE_VALUE;
|
|
|
|
#else
|
|
|
|
if (ChildProcess != -1)
|
|
|
|
{
|
|
|
|
if (waitpid (ChildProcess, NULL, WNOHANG) == ChildProcess)
|
|
|
|
{
|
|
|
|
ChildProcess = -1;
|
|
|
|
#endif
|
|
|
|
if (m_Looping)
|
|
|
|
{
|
|
|
|
if (!LaunchTimidity ())
|
|
|
|
{
|
|
|
|
Stop ();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
Buffer = new BYTE[BuffSize];
|
|
|
|
if (Buffer != NULL)
|
|
|
|
{
|
|
|
|
BufferMutex = SDL_CreateMutex ();
|
|
|
|
if (BufferMutex == NULL)
|
|
|
|
{
|
|
|
|
Reader = SDL_CreateThread (ThreadProc, (void *)this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int FPipeBuffer::ReadFrag (BYTE *buf)
|
|
|
|
{
|
|
|
|
int startavvail;
|
|
|
|
int avail;
|
|
|
|
int pos;
|
|
|
|
int opos;
|
|
|
|
|
|
|
|
if (SDL_mutexP (BufferMutex) == -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (WritePos > ReadPos)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
pos = 0;
|
|
|
|
avail -= thistime;
|
|
|
|
}
|
|
|
|
opos += thistime;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadPos = pos;
|
|
|
|
|
|
|
|
SDL_mutexV (BufferMutex);
|
|
|
|
|
|
|
|
return startavail;
|
|
|
|
}
|
|
|
|
#endif // !_WIN32
|