- skeleton of new player class for Timidity++ - not tested yet.

This commit is contained in:
Christoph Oelckers 2018-02-20 01:23:56 +01:00
parent 803369fb8d
commit d72b87f469
4 changed files with 131 additions and 920 deletions

View file

@ -45,93 +45,31 @@
#include "tmpfileplus.h"
#include "m_misc.h"
#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifndef __OpenBSD__
#include <wordexp.h>
#endif
#include <glob.h>
#include <signal.h>
int ChildQuit;
void ChildSigHandler (int signum)
{
ChildQuit = waitpid (-1, NULL, WNOHANG);
}
#endif
class TimidityPPMIDIDevice : public PseudoMIDIDevice
class TimidityPPMIDIDevice : public SoftSynthMIDIDevice
{
TArray<uint8_t> midi;
FString CurrentConfig;
public:
TimidityPPMIDIDevice(const char *args);
~TimidityPPMIDIDevice();
int Open(MidiCallback, void *userdata);
bool Preprocess(MIDIStreamer *song, bool looping);
bool IsOpen() const;
int Resume();
void Stop();
bool IsOpen();
void TimidityVolumeChanged();
void PrecacheInstruments(const uint16_t *instruments, int count);
//FString GetStats();
int GetDeviceType() const override { return MDEV_TIMIDITY; }
bool Preprocess(MIDIStreamer *song, bool looping);
void TimidityVolumeChanged();
protected:
bool LaunchTimidity();
//TimidityPlus::Player *Renderer;
FString DiskName;
#ifdef _WIN32
HANDLE ReadWavePipe;
HANDLE WriteWavePipe;
HANDLE ChildProcess;
FString CommandLine;
bool Validated;
bool ValidateTimidity();
#else // _WIN32
int WavePipe[2];
pid_t ChildProcess;
#endif
FString ExeName;
bool Looping;
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
#ifdef _WIN32
static const char EventName[];
#endif
void HandleEvent(int status, int parm1, int parm2);
void HandleLongEvent(const uint8_t *data, int len);
void ComputeOutput(float *buffer, int len);
};
#ifdef _WIN32
BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode);
static char TimidityTitle[] = "TiMidity (" GAMENAME " Launched)";
const char TimidityPPMIDIDevice::EventName[] = "TiMidity Killer";
CUSTOM_CVAR (String, timidity_exe, "timidity.exe", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
#else
CUSTOM_CVAR(String, timidity_exe, "timidity", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
#endif
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_TIMIDITY)
{
MIDIDeviceChanged(-1, true);
}
}
CVAR (String, timidity_extargs, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // extra args to pass to Timidity
// Config file to use
CVAR(String, timidity_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
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)
// added because Timidity's output is rather loud.
CUSTOM_CVAR (Float, timidity_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
@ -144,13 +82,6 @@ CUSTOM_CVAR (Float, timidity_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
currSong->TimidityVolumeChanged();
}
CUSTOM_CVAR (Int, timidity_pipe, 90, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{ // pipe size in ms
if (self < 20)
{ // Don't allow pipes less than 20ms
self = 20;
}
}
CUSTOM_CVAR (Int, timidity_frequency, 44100, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{ // Clamp frequency to Timidity's limits
@ -166,34 +97,19 @@ CUSTOM_CVAR (Int, timidity_frequency, 44100, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//
//==========================================================================
TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args) :
#ifdef _WIN32
ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE),
ChildProcess(INVALID_HANDLE_VALUE),
Validated(false),
#else
ChildProcess(-1),
#endif
Looping(false)
TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args)
{
#ifndef _WIN32
WavePipe[0] = WavePipe[1] = -1;
#endif
if (args == NULL || *args == 0) args = timidity_config;
if (args == NULL || *args == 0) args = timidity_exe;
ExeName = args;
#ifdef _WIN32
CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ",
args, *timidity_extargs,
*timidity_chorus, *timidity_reverb, *timidity_frequency);
if (**timidity_config != '\0')
if (CurrentConfig.CompareNoCase(args) != 0)
{
CommandLine += "-c \"";
CommandLine += timidity_config;
CommandLine += "\" ";
// reload instruments
// if (!reload_inst()) CurrentConfig = "";
}
if (CurrentConfig.IsNotEmpty())
{
//Renderer = new TimidityPlus::Player(timidity_frequency, args);
}
#endif
}
//==========================================================================
@ -204,34 +120,14 @@ TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args) :
TimidityPPMIDIDevice::~TimidityPPMIDIDevice ()
{
if (DiskName.IsNotEmpty())
Close();
/*
if (Renderer != nullptr)
{
remove(DiskName);
delete Renderer;
}
*/
#if _WIN32
if (WriteWavePipe != INVALID_HANDLE_VALUE)
{
CloseHandle (WriteWavePipe);
WriteWavePipe = INVALID_HANDLE_VALUE;
}
if (ReadWavePipe != INVALID_HANDLE_VALUE)
{
CloseHandle (ReadWavePipe);
ReadWavePipe = 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
}
//==========================================================================
@ -242,47 +138,9 @@ TimidityPPMIDIDevice::~TimidityPPMIDIDevice ()
bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
{
TArray<uint8_t> midi;
bool success;
FILE *f;
if (ExeName.IsEmpty())
{
return false;
}
Looping = looping;
// Write MIDI song to temporary file
song->CreateSMF(midi, looping ? 0 : 1);
FString path = M_GetAppDataPath(false);
path += "/tmp";
CreatePath(path);
f = tmpfileplus(path, "zmid", &DiskName, 1);
if (f == NULL)
{
Printf(PRINT_BOLD, "Could not open temp music file\n");
return false;
}
success = (fwrite(&midi[0], 1, midi.Size(), f) == (size_t)midi.Size());
fclose (f);
if (!success)
{
Printf(PRINT_BOLD, "Could not write temp music file\n");
}
#ifdef _WIN32
CommandLine.AppendFormat("-o - -Ors%c%c%c -id%c %s",
timidity_stereo ? 'S' : 'M',
timidity_8bit ? '8' : '1',
timidity_byteswap ? 'x' : ' ',
looping ? 'l' : ' ',
DiskName);
#endif
return false;
return midi.Size() > 0;
}
//==========================================================================
@ -290,414 +148,79 @@ bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
// TimidityPPMIDIDevice :: Open
//
//==========================================================================
namespace TimidityPlus
{
int load_midi_file(FileReader *fr);
void run_midi(int samples);
void timidity_close();
}
int TimidityPPMIDIDevice::Open(MidiCallback callback, void *userdata)
{
int pipeSize;
#ifdef _WIN32
static SECURITY_ATTRIBUTES inheritable = { sizeof(inheritable), NULL, true };
if (!Validated && !ValidateTimidity ())
int ret = OpenStream(2, 0, callback, userdata);
if (ret == 0)
{
return 101;
//Renderer->Reset();
MemoryReader fr((char*)&midi[0], midi.Size());
return !TimidityPlus::load_midi_file(&fr);
}
Validated = true;
#endif // WIN32
pipeSize = (timidity_pipe * timidity_frequency / 1000)
<< (timidity_stereo + !timidity_8bit);
{
#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");
return 1;
}
Stream = GSnd->CreateStream(FillStream, pipeSize,
(timidity_stereo ? 0 : SoundStream::Mono) |
(timidity_8bit ? SoundStream::Bits8 : 0),
timidity_frequency, this);
if (Stream == NULL)
{
Printf(PRINT_BOLD, "Could not create music stream.\n");
#ifdef _WIN32
CloseHandle(WriteWavePipe);
CloseHandle(ReadWavePipe);
ReadWavePipe = WriteWavePipe = INVALID_HANDLE_VALUE;
#else
close(WavePipe[1]);
close(WavePipe[0]);
WavePipe[0] = WavePipe[1] = -1;
#endif
return 1;
}
}
return 0;
return ret;
}
//==========================================================================
//
// TimidityPPMIDIDevice :: ValidateTimidity
// TimidityPPMIDIDevice :: PrecacheInstruments
//
// 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.
// Each entry is packed as follows:
// Bits 0- 6: Instrument number
// Bits 7-13: Bank number
// Bit 14: Select drum set if 1, tone bank if 0
//
//==========================================================================
#ifdef _WIN32
bool TimidityPPMIDIDevice::ValidateTimidity()
void TimidityPPMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count)
{
char foundPath[MAX_PATH];
char *filePart;
DWORD pathLen;
DWORD fileLen;
HANDLE diskFile;
HANDLE mapping;
const uint8_t *exeBase;
const uint8_t *exeEnd;
const uint8_t *exe;
bool good;
pathLen = SearchPath (NULL, timidity_exe, NULL, MAX_PATH, foundPath, &filePart);
if (pathLen == 0)
for (int i = 0; i < count; ++i)
{
Printf(PRINT_BOLD, "Please set the timidity_exe cvar to the location of TiMidity++\n");
return false;
//Renderer->MarkInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127);
}
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 uint8_t *)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 uint8_t *)tSpot + 1;
}
}
catch (...)
{
Printf(PRINT_BOLD, "Error reading %s\n", foundPath);
}
if (!good)
{
Printf(PRINT_BOLD, GAMENAME " requires a special version of TiMidity++\n");
}
UnmapViewOfFile((LPVOID)exeBase);
CloseHandle(mapping);
CloseHandle(diskFile);
return good;
}
#endif // _WIN32
//==========================================================================
//
// TimidityPPMIDIDevice :: LaunchTimidity
//
//==========================================================================
bool TimidityPPMIDIDevice::LaunchTimidity ()
{
if (ExeName.IsEmpty() || DiskName.IsEmpty())
{
return false;
}
#ifdef _WIN32
DPrintf (DMSG_NOTIFY, "cmd: \x1cG%s\n", CommandLine.GetChars());
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 = TimidityTitle;
startup.wShowWindow = SW_SHOWMINNOACTIVE;
if (CreateProcess(NULL, CommandLine.LockBuffer(), NULL, NULL, true,
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
CommandLine.UnlockBuffer();
return true;
}
CommandLine.UnlockBuffer();
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))
{
mysnprintf(hres, countof(hres), "%08lx", err);
msgBuf = hres;
}
Printf(PRINT_BOLD, "Could not run timidity with the command line:\n%s\n"
"Reason: %s\n", CommandLine.GetChars(), msgBuf);
if (msgBuf != hres)
{
LocalFree (msgBuf);
}
return false;
#else
if (WavePipe[0] != -1 && WavePipe[1] == -1 && 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 Stream;
Stream = NULL;
Open (NULL, NULL);
}
int forkres;
#ifndef __OpenBSD__
wordexp_t words;
#endif
glob_t glb;
// Get timidity executable path
const char *exename = "timidity"; // Fallback default
glob(ExeName.GetChars(), 0, NULL, &glb);
if(glb.gl_pathc != 0)
exename = glb.gl_pathv[0];
#ifndef __OpenBSD__
// Get user-defined extra args
wordexp(timidity_extargs, &words, WRDE_NOCMD);
#endif
std::string chorusarg = std::string("-EFchorus=") + *timidity_chorus;
std::string reverbarg = std::string("-EFreverb=") + *timidity_reverb;
std::string sratearg = std::string("-s") + std::to_string(*timidity_frequency);
std::string outfilearg = "-o"; // An extra "-" is added later
std::string outmodearg = "-Or";
outmodearg += timidity_8bit ? "u8" : "s1";
outmodearg += timidity_stereo ? "S" : "M";
if(timidity_byteswap) outmodearg += "x";
std::string ifacearg = "-id";
if(Looping) ifacearg += "l";
std::vector<const char*> arglist;
arglist.push_back(exename);
#ifndef __OpenBSD__
for(size_t i = 0;i < words.we_wordc;i++)
arglist.push_back(words.we_wordv[i]);
#endif
if(**timidity_config != '\0')
{
arglist.push_back("-c");
arglist.push_back(timidity_config);
}
arglist.push_back(chorusarg.c_str());
arglist.push_back(reverbarg.c_str());
arglist.push_back(sratearg.c_str());
arglist.push_back(outfilearg.c_str());
arglist.push_back("-");
arglist.push_back(outmodearg.c_str());
arglist.push_back(ifacearg.c_str());
arglist.push_back(DiskName.GetChars());
DPrintf(DMSG_NOTIFY, "Timidity EXE: \x1cG%s\n", exename);
int i = 1;
std::for_each(arglist.begin()+1, arglist.end(),
[&i](const char *arg)
{ DPrintf(DMSG_NOTIFY, "arg %d: \x1cG%s\n", i++, arg); }
);
arglist.push_back(nullptr);
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]);
execvp (exename, const_cast<char*const*>(arglist.data()));
fprintf(stderr,"execvp failed: %s\n", strerror(errno));
_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;
/* usleep(1000000);
if (waitpid(ChildProcess, NULL, WNOHANG) == ChildProcess)
{
fprintf(stderr,"Launching timidity failed\n");
}*/
}
#ifndef __OpenBSD__
wordfree(&words);
#endif
globfree (&glb);
return ChildProcess != -1;
#endif // _WIN32
//Renderer->load_missing_instruments();
}
//==========================================================================
//
// TimidityPPMIDIDevice :: FillStream
// TimidityPPMIDIDevice :: HandleEvent
//
//==========================================================================
bool TimidityPPMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata)
void TimidityPPMIDIDevice::HandleEvent(int status, int parm1, int parm2)
{
TimidityPPMIDIDevice *song = (TimidityPPMIDIDevice *)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, (uint8_t *)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 ((uint8_t *)buff+didget, 0, len-didget);
break;
}
}
}
#else
fd_set rfds;
struct timeval tv;
if (ChildQuit == song->ChildProcess)
{
ChildQuit = 0;
fprintf(stderr, "child gone\n");
song->ChildProcess = -1;
return false;
//Renderer->HandleEvent(status, parm1, parm2);
}
FD_ZERO(&rfds);
FD_SET(song->WavePipe[0], &rfds);
tv.tv_sec = 0;
tv.tv_usec = 50;
// fprintf(stderr,"select\n");
if (select(1, &rfds, NULL, NULL, &tv) <= 0 && 0)
{ // Nothing available, so play silence.
// fprintf(stderr,"nothing\n");
// memset(buff, 0, len);
return true;
}
// fprintf(stderr,"something\n");
//==========================================================================
//
// TimidityPPMIDIDevice :: HandleLongEvent
//
//==========================================================================
ssize_t got = 0;
do {
ssize_t r = read(song->WavePipe[0], (uint8_t*)buff+got, len-got);
if(r < 0)
void TimidityPPMIDIDevice::HandleLongEvent(const uint8_t *data, int len)
{
if(errno == EWOULDBLOCK || errno == EAGAIN)
//Renderer->HandleLongMessage(data, len);
}
//==========================================================================
//
// TimidityPPMIDIDevice :: ComputeOutput
//
//==========================================================================
void TimidityPPMIDIDevice::ComputeOutput(float *buffer, int len)
{
FD_ZERO(&rfds);
FD_SET(song->WavePipe[0], &rfds);
tv.tv_sec = 0;
tv.tv_usec = 50;
select(1, &rfds, NULL, NULL, &tv);
continue;
}
break;
}
got += r;
} while(got < len);
if(got < len)
memset((uint8_t*)buff+got, 0, len-got);
#endif
return true;
TimidityPlus::run_midi(len / 8); // bytes to samples
memset(buffer, len, 0); // to do
//Renderer->ComputeOutput(buffer, len);
}
//==========================================================================
@ -714,162 +237,6 @@ void TimidityPPMIDIDevice::TimidityVolumeChanged()
}
}
//==========================================================================
//
// TimidityPPMIDIDevice :: IsOpen
//
//==========================================================================
bool TimidityPPMIDIDevice::IsOpen() const
{
#ifdef _WIN32
if (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)
{
if (waitpid (ChildProcess, NULL, WNOHANG) == ChildProcess)
{
const_cast<TimidityPPMIDIDevice *>(this)->ChildProcess = -1;
#endif
return false;
}
return true;
}
return false;
}
//==========================================================================
//
// TimidityPPMIDIDevice :: Resume
//
//==========================================================================
int TimidityPPMIDIDevice::Resume()
{
if (!Started)
{
if (LaunchTimidity())
{
if (Stream != NULL && Stream->Play(true, timidity_mastervolume))
{
Started = true;
return 0;
}
}
return 1;
}
return 0;
}
//==========================================================================
//
// TimidityPPMIDIDevice :: Stop
//
//==========================================================================
void TimidityPPMIDIDevice::Stop ()
{
if (Started)
{
if (Stream != NULL)
{
Stream->Stop();
}
#ifdef _WIN32
if (ChildProcess != INVALID_HANDLE_VALUE)
{
if (!SafeTerminateProcess(ChildProcess, 666) && GetLastError() != ERROR_PROCESS_ABORTED)
{
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;
}
#ifdef _WIN32
/*
Safely terminate a process by creating a remote thread
in the process that calls ExitProcess
Source is a Dr Dobbs article circa 1999.
*/
typedef HANDLE (WINAPI *CreateRemoteThreadProto)(HANDLE,LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD);
BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode)
{
DWORD dwTID, dwCode;
HRESULT dwErr = 0;
HANDLE hRT = NULL;
HINSTANCE hKernel = GetModuleHandle("Kernel32");
BOOL bSuccess = FALSE;
// Detect the special case where the process is already dead...
if ( GetExitCodeProcess(hProcess, &dwCode) && (dwCode == STILL_ACTIVE) )
{
FARPROC pfnExitProc;
CreateRemoteThreadProto pfCreateRemoteThread;
pfnExitProc = GetProcAddress(hKernel, "ExitProcess");
// CreateRemoteThread does not exist on 9x systems.
pfCreateRemoteThread = (CreateRemoteThreadProto)GetProcAddress(hKernel, "CreateRemoteThread");
if (pfCreateRemoteThread == NULL)
{
dwErr = ERROR_INVALID_FUNCTION;
}
else
{
hRT = pfCreateRemoteThread(hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pfnExitProc,
(PVOID)(UINT_PTR)uExitCode, 0, &dwTID);
if ( hRT == NULL )
dwErr = GetLastError();
}
}
else
{
dwErr = ERROR_PROCESS_ABORTED;
}
if ( hRT )
{
// Must wait for process to terminate to guarantee that it has exited...
DWORD res = WaitForSingleObject(hProcess, 1000);
CloseHandle(hRT);
bSuccess = (res == WAIT_OBJECT_0);
dwErr = WAIT_TIMEOUT;
}
if ( !bSuccess )
SetLastError(dwErr);
return bSuccess;
}
#endif
MIDIDevice *CreateTimidityPPMIDIDevice(const char *args)
{

View file

@ -733,6 +733,8 @@ public:
// Only until streaming works.
void skip_to(int32_t until_time, MidiEvent *evt_start);
int start_midi(MidiEvent *eventlist, int32_t samples);
void run_midi(int);
int play_midi(MidiEvent *eventlist, int32_t samples);
friend MidiEvent *groom_list(int32_t divisions, int32_t *eventsp, int32_t *samplesp);

View file

@ -18,6 +18,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -28,6 +29,7 @@
#include "tables.h"
#include "reverb.h"
#include "aq.h"
#include "tarray.h"
#include <math.h>
namespace TimidityPlus
@ -124,10 +126,9 @@ void Player::skip_to(int32_t until_time, MidiEvent *evt_start)
}
int Player::play_midi(MidiEvent *eventlist, int32_t samples)
int Player::start_midi(MidiEvent *eventlist, int32_t samples)
{
int rc;
static int play_count = 0;
int i, j;
sample_count = samples;
@ -155,29 +156,9 @@ int Player::play_midi(MidiEvent *eventlist, int32_t samples)
skip_to(0, eventlist);
rc = RC_OK;
for (;;)
{
rc = play_event(current_event);
if (rc != RC_OK)
break;
current_event++;
return RC_OK;
}
if (play_count++ > 3)
{
int cnt;
play_count = 0;
cnt = free_global_mblock(); /* free unused memory */
if (cnt > 0)
ctl_cmsg(CMSG_INFO, VERB_VERBOSE,
"%d memory blocks are free", cnt);
}
return rc;
}
static MidiEventList *alloc_midi_event()
{
return (MidiEventList *)new_segment(&mempool, sizeof(MidiEventList));
@ -1491,23 +1472,7 @@ static MidiEvent *read_midi_file(struct timidity_file *tf, int32_t *count, int32
//////////////////////////////////////// MID file player
// temporary crutch
static struct timidity_file *open_file(char *name, int decompress, int noise_mode)
{
FileReader *fr = new FileReader;
if (!fr->Open(name))
{
delete fr;
return nullptr;
}
auto tf = new timidity_file;
tf->url = fr;
tf->filename = name;
return tf;
}
static int play_midi_load_file(char *fn,
static int play_midi_load_file(FileReader *fr,
MidiEvent **event,
int32_t *nsamples)
{
@ -1517,16 +1482,14 @@ static int play_midi_load_file(char *fn,
*event = NULL;
ctl_cmsg(CMSG_INFO, VERB_VERBOSE, "MIDI file: %s", fn);
if ((tf = open_file(fn, 1, OF_VERBOSE)) == NULL)
{
return RC_ERROR;
}
tf = new timidity_file;
tf->filename = "zdoom";
tf->url = fr;
*event = NULL;
rc = RC_OK;
*event = read_midi_file(tf, &nevents, nsamples, fn);
*event = read_midi_file(tf, &nevents, nsamples, "zdoom");
close_file(tf);
if (*event == NULL)
@ -1564,15 +1527,35 @@ static int play_midi_load_file(char *fn,
}
int play_midi_file(char *fn)
Instruments *instruments;
CRITICAL_SECTION critSect;
int load_midi_file(FileReader *fr)
{
int rc;
static int last_rc = RC_OK;
MidiEvent *event;
int32_t nsamples;
InitializeCriticalSection(&critSect);
if (play_mode->open_output() < 0)
{
return RC_ERROR;
}
instruments = new Instruments;
if (instruments->load("timidity.cfg"))
{
return RC_ERROR;
}
gplayer = new Player;
/* Set current file information */
auto current_file_info = gplayer->get_midi_file_info(fn, 1);
auto current_file_info = gplayer->get_midi_file_info("zdoom", 1);
rc = RC_OK;
@ -1581,31 +1564,39 @@ int play_midi_file(char *fn)
midi_restart_time = 0;
rc = play_midi_load_file(fn, &event, &nsamples);
rc = play_midi_load_file(fr, &event, &nsamples);
if (RC_IS_SKIP_FILE(rc))
goto play_end; /* skip playing */
return RC_ERROR; /* skip playing */
rc = gplayer->play_midi(event, nsamples);
play_end:
instruments->free_special_patch(-1);
if (event != NULL)
free(event);
last_rc = rc;
return rc;
return gplayer->start_midi(event, nsamples);
}
int dumb_pass_playing_list(int number_of_files, char *list_of_files[])
void Player::run_midi(int samples)
{
gplayer = new Player;
for (;;)
auto time = current_event->time + samples;
while (current_event->time < samples)
{
if (play_midi_file(list_of_files[0]) == RC_QUIT) return 0;
if (play_event(current_event) != RC_OK)
return;
current_event++;
}
}
void run_midi(int msec)
{
gplayer->run_midi(msec);
}
void timidity_close()
{
delete gplayer;
delete instruments;
play_mode->close_output();
DeleteCriticalSection(&critSect);
free_global_mblock();
}
}

View file

@ -1,149 +0,0 @@
/*
TiMidity++ -- MIDI to WAVE converter and player
Copyright (C) 1999-2008 Masanao Izumo <iz@onicos.co.jp>
Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#include <shlobj.h>
#endif
#include <time.h>
#include <signal.h>
#include "timidity.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "tables.h"
#include "reverb.h"
#include "resample.h"
#include "recache.h"
#include "aq.h"
#include "mix.h"
#include "quantity.h"
#include "c_cvars.h"
namespace TimidityPlus
{
Instruments *instruments;
/* main interfaces (To be used another main) */
int timidity_pre_load_configuration(void);
void timidity_init_player(void);
int timidity_play_main(int nfiles, char **files);
int got_a_configuration;
CRITICAL_SECTION critSect;
/* -------- functions for getopt_long ends here --------- */
int timidity_pre_load_configuration(void)
{
/* Windows */
char *strp;
char local[1024];
/* First, try read configuration file which is in the
* TiMidity directory.
*/
if(GetModuleFileNameA(NULL, local, 1023))
{
local[1023] = '\0';
if (strp = strrchr(local, '\\'))
{
*(++strp) = '\0';
strncat(local, "TIMIDITY.CFG", sizeof(local) - strlen(local) - 1);
if (true)
{
if (!instruments->load("timidity.cfg"))
{
got_a_configuration = 1;
return 0;
}
}
}
}
return 0;
}
int dumb_pass_playing_list(int number_of_files, char *list_of_files[]);
int timidity_play_main(int nfiles, char **files)
{
int need_stdin = 0, need_stdout = 0;
int output_fail = 0;
int retval;
#ifdef _WIN32
InitializeCriticalSection(&critSect);
#endif
/* Open output device */
if(play_mode->open_output() < 0)
{
output_fail = 1;
return 2;
}
retval=dumb_pass_playing_list(nfiles, files);
play_mode->close_output();
#ifdef _WIN32
DeleteCriticalSection (&critSect);
#endif
return retval;
}
}
using namespace TimidityPlus;
void main(int argc, char **argv)
{
int err;
char *files_nbuf = NULL;
int main_ret;
instruments = new Instruments;
if ((err = timidity_pre_load_configuration()) != 0)
return;
main_ret = timidity_play_main(1, &argv[1]);
//free_readmidi();
free_global_mblock();
}