make midiplayer more fancy with pipes for communication
This commit is contained in:
parent
e4c4b29805
commit
94617fb1cc
2 changed files with 372 additions and 75 deletions
src/sound
|
@ -35,6 +35,8 @@
|
|||
-- Includes
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "stratagus.h"
|
||||
|
||||
#include "sound_server.h"
|
||||
|
@ -63,104 +65,154 @@ static int MusicVolume = 0;
|
|||
static void (*MusicFinishedCallback)();
|
||||
|
||||
#ifdef USE_WIN32
|
||||
static volatile bool threadWaiting = false;
|
||||
static std::string externalFile;
|
||||
static HANDLE hWaitingThread;
|
||||
static bool externalPlayerIsPlaying = false;
|
||||
|
||||
static HANDLE g_hStatusThread;
|
||||
static HANDLE g_hDebugThread;
|
||||
static HANDLE g_hChildStd_IN_Wr;
|
||||
static PROCESS_INFORMATION pi;
|
||||
|
||||
DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
if (threadWaiting) {
|
||||
MusicFinishedCallback();
|
||||
threadWaiting = false;
|
||||
static DWORD WINAPI StatusThreadFunction(LPVOID lpParam) {
|
||||
CHAR chStatus;
|
||||
DWORD dwRead = 1;
|
||||
while (1) {
|
||||
if (!ReadFile((HANDLE)lpParam, &chStatus, 1, &dwRead, NULL) || dwRead == 0) {
|
||||
CloseHandle((HANDLE)lpParam);
|
||||
break;
|
||||
}
|
||||
// any write means we finished
|
||||
if (externalPlayerIsPlaying) {
|
||||
externalPlayerIsPlaying = false;
|
||||
MusicFinishedCallback();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void killPlayingProcess() {
|
||||
if (threadWaiting) {
|
||||
threadWaiting = false;
|
||||
TerminateProcess(pi.hProcess, 0);
|
||||
WaitForSingleObject(hWaitingThread, INFINITE);
|
||||
threadWaiting = false;
|
||||
} else {
|
||||
static DWORD WINAPI DebugThreadFunction(LPVOID lpParam) {
|
||||
DWORD dwRead = 1;
|
||||
while (1) {
|
||||
char *chStatus[1024] = {'\0'};
|
||||
if (!ReadFile((HANDLE)lpParam, &chStatus, 1024, &dwRead, NULL) || dwRead == 0) {
|
||||
CloseHandle((HANDLE)lpParam);
|
||||
break;
|
||||
}
|
||||
DebugPrint("%s" _C_ chStatus);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void KillPlayingProcess() {
|
||||
externalPlayerIsPlaying = false;
|
||||
if (g_hChildStd_IN_Wr) {
|
||||
TerminateProcess(pi.hProcess, 0);
|
||||
CloseHandle(g_hChildStd_IN_Wr);
|
||||
g_hChildStd_IN_Wr = NULL;
|
||||
WaitForSingleObject(StatusThreadFunction, 0);
|
||||
WaitForSingleObject(DebugThreadFunction, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool External_Play(const std::string &file) {
|
||||
if (threadWaiting && file == externalFile) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string midi = ".mid";
|
||||
auto it = midi.begin();
|
||||
if (file.size() > midi.size() &&
|
||||
std::all_of(std::next(file.begin(), file.size() - midi.size()), file.end(), [&it](const char & c) { return c == ::tolower(*(it++)); })) {
|
||||
// midi file, use external player, since windows vista+ does not allow midi volume control independent of process volume
|
||||
|
||||
std::string full_filename = LibraryFileName(file.c_str());
|
||||
|
||||
static const char* midiplayerExe = "stratagus-midiplayer.exe";
|
||||
static const int midiplayerExeSz = strlen(midiplayerExe);
|
||||
|
||||
// set up a job so our children die with us
|
||||
static bool firstRun = true;
|
||||
static HANDLE hJob;
|
||||
if (firstRun) {
|
||||
hJob = CreateJobObject(NULL, NULL);
|
||||
JOBOBJECT_BASIC_LIMIT_INFORMATION limitInfo;
|
||||
ZeroMemory(&limitInfo, sizeof(limitInfo));
|
||||
limitInfo.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
SetInformationJobObject(hJob, JobObjectBasicLimitInformation, &limitInfo, sizeof(limitInfo));
|
||||
AssignProcessToJobObject(hJob, GetCurrentProcess());
|
||||
firstRun = false;
|
||||
// try to communicate with the running midiplayer if we can
|
||||
if (g_hChildStd_IN_Wr != NULL) {
|
||||
// already playing, just send the new song
|
||||
// XXX: timfel: disabled, since the midiplayer behaves weirdly when it receives the next file, just kill and restart
|
||||
KillPlayingProcess();
|
||||
/*
|
||||
// negative value signals a new filename
|
||||
int fileSize = full_filename.size() & 0xffff;
|
||||
char loSize = fileSize & 0xff;
|
||||
char hiSize = (fileSize >> 8) & 0xff;
|
||||
char buf[2] = {loSize, hiSize};
|
||||
externalPlayerIsPlaying = true;
|
||||
if (!WriteFile(g_hChildStd_IN_Wr, buf, 2, NULL, NULL)) {
|
||||
KillPlayingProcess();
|
||||
} else {
|
||||
// then write the filename
|
||||
if (!WriteFile(g_hChildStd_IN_Wr, full_filename.c_str(), fileSize, NULL, NULL)) {
|
||||
KillPlayingProcess();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
// need to start an external player first
|
||||
|
||||
int sz = midiplayerExeSz + 2 + 3 + 2 + full_filename.size() + 1; // exe + 2 spaces + 3 volume + 2 quotes + filename + nullbyte
|
||||
char *cmdline = new char[sz];
|
||||
snprintf(cmdline, sz, "%s %3d \"%s\"", midiplayerExe, std::min(MusicVolume, 127), full_filename.c_str());
|
||||
DebugPrint("Using external command to play midi on windows: %s\n" _C_ cmdline);
|
||||
killPlayingProcess();
|
||||
// setup pipes to player
|
||||
HANDLE hChildStd_IN_Rd = NULL;
|
||||
HANDLE hChildStd_OUT_Rd = NULL;
|
||||
HANDLE hChildStd_OUT_Wr = NULL;
|
||||
HANDLE hChildStd_ERR_Rd = NULL;
|
||||
HANDLE hChildStd_ERR_Wr = NULL;
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = NULL;
|
||||
CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0);
|
||||
CreatePipe(&hChildStd_ERR_Rd, &hChildStd_ERR_Wr, &saAttr, 0);
|
||||
CreatePipe(&hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0);
|
||||
SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0);
|
||||
|
||||
// start the process
|
||||
std::vector<std::string> args = QuoteArguments({ "stratagus-midiplayer.exe", std::to_string(std::min(MusicVolume, 127)), full_filename });
|
||||
std::string cmd = std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, std::string b) { return a + " " + b; });
|
||||
DebugPrint("Using external command to play midi on windows: %s\n" _C_ cmd.c_str());
|
||||
STARTUPINFO si;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
si.hStdError = hChildStd_ERR_Wr;
|
||||
si.hStdOutput = hChildStd_OUT_Wr;
|
||||
si.hStdInput = hChildStd_IN_Rd;
|
||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
bool result = true;
|
||||
if (CreateProcess(NULL, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
|
||||
AssignProcessToJobObject(hJob, pi.hProcess);
|
||||
externalFile = file;
|
||||
hWaitingThread = CreateThread(NULL, 0, MyThreadFunction, NULL, 0, NULL);
|
||||
threadWaiting = true;
|
||||
char* cmdline = strdup(cmd.c_str());
|
||||
if (CreateProcess(NULL, cmdline, NULL, NULL, TRUE, /* Handles are inherited */ CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
|
||||
CloseHandle(hChildStd_OUT_Wr);
|
||||
CloseHandle(hChildStd_ERR_Wr);
|
||||
CloseHandle(hChildStd_IN_Rd);
|
||||
externalPlayerIsPlaying = true;
|
||||
g_hStatusThread = CreateThread(NULL, 0, StatusThreadFunction, hChildStd_OUT_Rd, 0, NULL);
|
||||
g_hDebugThread = CreateThread(NULL, 0, DebugThreadFunction, hChildStd_ERR_Rd, 0, NULL);
|
||||
} else {
|
||||
result = false;
|
||||
DebugPrint("CreateProcess failed (%d).\n" _C_ GetLastError());
|
||||
}
|
||||
delete[] cmdline;
|
||||
free(cmdline);
|
||||
return result;
|
||||
}
|
||||
killPlayingProcess();
|
||||
KillPlayingProcess();
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool External_IsPlaying() {
|
||||
return threadWaiting;
|
||||
return externalPlayerIsPlaying;
|
||||
}
|
||||
|
||||
static bool External_Stop() {
|
||||
if (External_IsPlaying()) {
|
||||
killPlayingProcess();
|
||||
KillPlayingProcess();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool External_Volume(int volume, int oldVolume) {
|
||||
if (External_IsPlaying() && externalFile.size() > 0) {
|
||||
if (oldVolume != volume) {
|
||||
if (External_IsPlaying()) {
|
||||
char buf[2] = {0, volume & 0xFF};
|
||||
if (!WriteFile(g_hChildStd_IN_Wr, buf, 2, NULL, NULL)) {
|
||||
External_Stop();
|
||||
External_Play(externalFile);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,16 @@
|
|||
#include <mmsystem.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
|
||||
HMIXER sMixerHandle;
|
||||
HMIDISTRM sOut;
|
||||
HANDLE hWaitingThread;
|
||||
int sVolume = 127;
|
||||
char *sFilename = NULL;
|
||||
unsigned int sDeviceId = 0;
|
||||
|
||||
#define MAX_BUFFER_SIZE (512 * 12)
|
||||
HANDLE event;
|
||||
|
@ -142,7 +152,7 @@ static int is_track_end(const struct evt* e)
|
|||
|
||||
static void CALLBACK example9_callback(HMIDIOUT out, UINT msg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
|
||||
{
|
||||
switch (msg)
|
||||
switch (msg)
|
||||
{
|
||||
case MOM_DONE:
|
||||
SetEvent(event);
|
||||
|
@ -283,7 +293,7 @@ static unsigned int get_buffer(struct trk* tracks, unsigned int ntracks, unsigne
|
|||
return 1;
|
||||
}
|
||||
|
||||
unsigned int example9(char* filename, int volume)
|
||||
unsigned int example9(char *filename)
|
||||
{
|
||||
unsigned char* midibuf = NULL;
|
||||
unsigned int midilen = 0;
|
||||
|
@ -299,15 +309,14 @@ unsigned int example9(char* filename, int volume)
|
|||
unsigned int* streambuf = NULL;
|
||||
unsigned int streamlen = 0;
|
||||
|
||||
HMIDISTRM out;
|
||||
MIDIPROPTIMEDIV prop;
|
||||
MIDIHDR mhdr;
|
||||
unsigned int device = 0;
|
||||
|
||||
midibuf = load_file((unsigned char*)filename, &midilen);
|
||||
if(midibuf == NULL)
|
||||
{
|
||||
printf("could not open %s\n", filename);
|
||||
fprintf(stderr, "could not open %s\n", filename);
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -338,52 +347,57 @@ unsigned int example9(char* filename, int volume)
|
|||
if ((event = CreateEvent(0, FALSE, FALSE, 0)) == NULL)
|
||||
goto error3;
|
||||
|
||||
if (midiStreamOpen(&out, &device, 1, (DWORD)example9_callback, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
|
||||
if (midiStreamOpen(&sOut, &sDeviceId, 1, (DWORD)example9_callback, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
|
||||
goto error4;
|
||||
|
||||
prop.cbStruct = sizeof(MIDIPROPTIMEDIV);
|
||||
prop.dwTimeDiv = swap_bytes_short(hdr->ticks);
|
||||
if(midiStreamProperty(out, (LPBYTE)&prop, MIDIPROP_SET|MIDIPROP_TIMEDIV) != MMSYSERR_NOERROR)
|
||||
if(midiStreamProperty(sOut, (LPBYTE)&prop, MIDIPROP_SET|MIDIPROP_TIMEDIV) != MMSYSERR_NOERROR)
|
||||
goto error5;
|
||||
|
||||
mhdr.lpData = (char*)streambuf;
|
||||
mhdr.dwBufferLength = mhdr.dwBytesRecorded = streambufsize;
|
||||
mhdr.dwFlags = 0;
|
||||
|
||||
if(midiOutPrepareHeader((HMIDIOUT)out, &mhdr, sizeof(MIDIHDR)) != MMSYSERR_NOERROR)
|
||||
if(midiOutPrepareHeader((HMIDIOUT)sOut, &mhdr, sizeof(MIDIHDR)) != MMSYSERR_NOERROR)
|
||||
goto error5;
|
||||
|
||||
if(midiStreamRestart(out) != MMSYSERR_NOERROR)
|
||||
if(midiStreamRestart(sOut) != MMSYSERR_NOERROR)
|
||||
goto error6;
|
||||
|
||||
if (midiOutSetVolume((HMIDIOUT)out, (DWORD)((volume & 0xFF) << 8) | (volume & 0xFF)) != MMSYSERR_NOERROR) {
|
||||
printf("Cannot set volume, will have to use windows application volume control");
|
||||
}
|
||||
|
||||
printf("buffering...\n");
|
||||
fprintf(stderr, "buffering...\n");
|
||||
fflush(stderr);
|
||||
get_buffer(tracks, ntracks, streambuf, &streamlen);
|
||||
while(streamlen > 0)
|
||||
{
|
||||
mhdr.dwBytesRecorded = streamlen;
|
||||
|
||||
if(midiStreamOut(out, &mhdr, sizeof(MIDIHDR)) != MMSYSERR_NOERROR)
|
||||
if(midiStreamOut(sOut, &mhdr, sizeof(MIDIHDR)) != MMSYSERR_NOERROR)
|
||||
goto error7;
|
||||
|
||||
WaitForSingleObject(event, INFINITE);
|
||||
|
||||
printf("buffering...\n");
|
||||
if (sFilename != filename) {
|
||||
fprintf(stderr, "switch to new file %s.\n", sFilename);
|
||||
fflush(stderr);
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "buffering...\n");
|
||||
fflush(stderr);
|
||||
get_buffer(tracks, ntracks, streambuf, &streamlen);
|
||||
}
|
||||
printf("done.\n");
|
||||
fprintf(stderr, "done.\n");
|
||||
fflush(stderr);
|
||||
|
||||
error7:
|
||||
midiOutReset((HMIDIOUT)out);
|
||||
midiOutReset((HMIDIOUT)sOut);
|
||||
|
||||
error6:
|
||||
midiOutUnprepareHeader((HMIDIOUT)out, &mhdr, sizeof(MIDIHDR));
|
||||
midiOutUnprepareHeader((HMIDIOUT)sOut, &mhdr, sizeof(MIDIHDR));
|
||||
|
||||
error5:
|
||||
midiStreamClose(out);
|
||||
midiStreamClose(sOut);
|
||||
|
||||
error4:
|
||||
CloseHandle(event);
|
||||
|
@ -400,13 +414,244 @@ error1:
|
|||
return(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
|
||||
while (1) {
|
||||
MMRESULT r = midiOutSetVolume((HMIDIOUT)sOut, (DWORD)((sVolume & 0xFF) << 8) | (sVolume & 0xFF));
|
||||
if (r != MMSYSERR_NOERROR) {
|
||||
fprintf(stderr, "Cannot set volume via midi...");
|
||||
switch (r) {
|
||||
case MMSYSERR_INVALHANDLE:
|
||||
fprintf(stderr, "Reason: handle not valid\n");
|
||||
break;
|
||||
case MMSYSERR_NOMEM:
|
||||
fprintf(stderr, "Reason: memory error\n");
|
||||
break;
|
||||
case MMSYSERR_NOTSUPPORTED:
|
||||
fprintf(stderr, "Reason: not supported\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Reason: unknown\n");
|
||||
}
|
||||
fflush(stderr);
|
||||
MMRESULT err = mixerOpen(&sMixerHandle, sDeviceId, 0, 0, 0);
|
||||
if (err) {
|
||||
fprintf(stderr, "ERROR: Can't open Mixer Device! -- %08X\n", err);
|
||||
switch (err) {
|
||||
case MMSYSERR_ALLOCATED:
|
||||
fprintf(stderr, "MMSYSERR_ALLOCATED\n");
|
||||
break;
|
||||
case MMSYSERR_BADDEVICEID:
|
||||
fprintf(stderr, "MMSYSERR_BADDEVICEID\n");
|
||||
break;
|
||||
case MMSYSERR_INVALFLAG:
|
||||
fprintf(stderr, "MMSYSERR_INVALFLAG\n");
|
||||
break;
|
||||
case MMSYSERR_INVALHANDLE:
|
||||
fprintf(stderr, "MMSYSERR_INVALHANDLE\n");
|
||||
break;
|
||||
case MMSYSERR_INVALPARAM:
|
||||
fprintf(stderr, "MMSYSERR_INVALPARAM\n");
|
||||
break;
|
||||
case MMSYSERR_NODRIVER:
|
||||
fprintf(stderr, "MMSYSERR_NODRIVER\n");
|
||||
break;
|
||||
case MMSYSERR_NOMEM:
|
||||
fprintf(stderr, "MMSYSERR_NOMEM\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Reason: unknown\n");
|
||||
}
|
||||
fflush(stderr);
|
||||
} else {
|
||||
MIXERCAPS mixcaps;
|
||||
MIXERLINE mixerline;
|
||||
MMRESULT err;
|
||||
unsigned long i;
|
||||
/* Get info about the first Mixer Device */
|
||||
if (!(err = mixerGetDevCaps((UINT)sMixerHandle, &mixcaps, sizeof(MIXERCAPS)))) {
|
||||
/* Print out the name of each destination line */
|
||||
for (i = 0; i < mixcaps.cDestinations; i++) {
|
||||
mixerline.cbStruct = sizeof(MIXERLINE);
|
||||
mixerline.dwDestination = i;
|
||||
|
||||
if (!(err = mixerGetLineInfo((HMIXEROBJ)sMixerHandle, &mixerline, MIXER_GETLINEINFOF_DESTINATION)))
|
||||
{
|
||||
fprintf(stderr, "Destination #%lu = %s\n", i, mixerline.szName);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
mixerline.cbStruct = sizeof(MIXERLINE);
|
||||
mixerline.dwDestination = 0;
|
||||
mixerline.dwSource = 0;
|
||||
|
||||
if ((err = mixerGetLineInfo((HMIXEROBJ)sMixerHandle, &mixerline, MIXER_GETLINEINFOF_DESTINATION))) {
|
||||
/* An error */
|
||||
fprintf(stderr, "Error #%d calling mixerGetLineInfo()\n", err);
|
||||
switch (err) {
|
||||
case MIXERR_INVALLINE:
|
||||
fprintf(stderr, "MIXERR_INVALLINE\n");
|
||||
break;
|
||||
case MMSYSERR_BADDEVICEID:
|
||||
fprintf(stderr, "MMSYSERR_BADDEVICEID\n");
|
||||
break;
|
||||
case MMSYSERR_INVALFLAG:
|
||||
fprintf(stderr, "MMSYSERR_INVALFLAG\n");
|
||||
break;
|
||||
case MMSYSERR_INVALHANDLE:
|
||||
fprintf(stderr, "MMSYSERR_INVALHANDLE\n");
|
||||
break;
|
||||
case MMSYSERR_INVALPARAM:
|
||||
fprintf(stderr, "MMSYSERR_INVALPARAM\n");
|
||||
break;
|
||||
case MMSYSERR_NODRIVER:
|
||||
fprintf(stderr, "MMSYSERR_NODRIVER\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Reason: unknown\n");
|
||||
}
|
||||
fflush(stderr);
|
||||
} else {
|
||||
fprintf(stderr, "Mixerline: %s\n", mixerline.szName);
|
||||
MIXERCONTROL mixerControlArray;
|
||||
MIXERLINECONTROLS mixerLineControls;
|
||||
mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
|
||||
/* Tell mixerGetLineControls() for which line we're retrieving info.
|
||||
We do this by putting the desired line's ID number in dwLineID */
|
||||
mixerLineControls.dwLineID = mixerline.dwLineID;
|
||||
/* We want to fetch info on only 1 control */
|
||||
mixerLineControls.cControls = 1;
|
||||
/* Tell mixerGetLineControls() for which type of control we're
|
||||
retrieving info. We do this by putting the desired control type
|
||||
in dwControlType */
|
||||
mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
|
||||
/* Give mixerGetLineControls() the address of the MIXERCONTROL
|
||||
struct to hold info */
|
||||
mixerLineControls.pamxctrl = &mixerControlArray;
|
||||
/* Tell mixerGetLineControls() how big the MIXERCONTROL is. This
|
||||
saves having to initialize the cbStruct of the MIXERCONTROL itself */
|
||||
mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
|
||||
/* Retrieve info on only any volume slider control for this line */
|
||||
if ((err = mixerGetLineControls((HMIXEROBJ)sMixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE))) {
|
||||
/* An error */
|
||||
fprintf(stderr, "Error #%d calling mixerGetLineControls()\n", err);
|
||||
fflush(stderr);
|
||||
} else {
|
||||
fprintf(stderr, "Mixercontrol: %s\n", mixerControlArray.szName);
|
||||
fflush(stderr);
|
||||
// try via mixer controls
|
||||
MIXERCONTROLDETAILS_UNSIGNED value;
|
||||
MIXERCONTROLDETAILS mixerControlDetails;
|
||||
MMRESULT err;
|
||||
mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
|
||||
/* Tell mixerSetControlDetails() which control whose value we
|
||||
want to set. We do this by putting the desired control's
|
||||
ID number in dwControlID. The "Speaker Out" line's
|
||||
volume slider has an ID of 0x00000000 */
|
||||
mixerControlDetails.dwControlID = mixerControlArray.dwControlID;
|
||||
/* This is always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control */
|
||||
mixerControlDetails.cChannels = 1;
|
||||
/* This is always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control */
|
||||
mixerControlDetails.cMultipleItems = 0;
|
||||
/* Give mixerSetControlDetails() the address of the
|
||||
MIXERCONTROLDETAILS_UNSIGNED struct into which we place the value */
|
||||
mixerControlDetails.paDetails = &value;
|
||||
/* Tell mixerSetControlDetails() how big the MIXERCONTROLDETAILS_UNSIGNED is */
|
||||
mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
|
||||
/* Store the value */
|
||||
value.dwValue = (sVolume & 0xFF) << 8;
|
||||
/* Set the value of the volume slider control for this line */
|
||||
if ((err = mixerSetControlDetails((HMIXEROBJ)sMixerHandle, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE))) {
|
||||
fprintf(stderr, "Error #%d calling mixerSetControlDetails() ", err);
|
||||
switch (err) {
|
||||
case MIXERR_INVALCONTROL:
|
||||
fprintf(stderr, "Invalid control\n");
|
||||
break;
|
||||
case MMSYSERR_BADDEVICEID:
|
||||
fprintf(stderr, "bad device id\n");
|
||||
break;
|
||||
case MMSYSERR_INVALFLAG:
|
||||
fprintf(stderr, "invalid flag\n");
|
||||
break;
|
||||
case MMSYSERR_INVALHANDLE:
|
||||
fprintf(stderr, "Invalid handle\n");
|
||||
break;
|
||||
case MMSYSERR_INVALPARAM:
|
||||
fprintf(stderr, "Invalid param\n");
|
||||
break;
|
||||
case MMSYSERR_NODRIVER:
|
||||
fprintf(stderr, "No driver\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
fprintf(stderr, "Will have to use windows application volume control.\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char data1, data2;
|
||||
fprintf(stderr, "read from stdin...");
|
||||
fflush(stderr);
|
||||
if (fread(&data1, sizeof(char), 1, stdin) != 1) {
|
||||
exit(0);
|
||||
} else {
|
||||
if (fread(&data2, sizeof(char), 1, stdin) != 1) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "got %x %x\n", data1, data2);
|
||||
fflush(stderr);
|
||||
if (data1 > 0) {
|
||||
// new filename
|
||||
short length = data1 | (data2 << 8);
|
||||
fprintf(stderr, "New filename sending: %d bytes\n", length);
|
||||
fflush(stderr);
|
||||
static char buf[0xffff];
|
||||
int lastRead, totalRead = 0;
|
||||
while ((lastRead = fread(buf + totalRead, sizeof(char), length - totalRead, stdin)) > 0 && totalRead < length) {
|
||||
totalRead += lastRead;
|
||||
}
|
||||
if (totalRead < length) {
|
||||
fprintf(stderr, "not enough data, got %d, expected %d bytes\n", totalRead, length);
|
||||
fflush(stderr);
|
||||
exit(0);
|
||||
}
|
||||
buf[length] = '\0';
|
||||
sFilename = buf;
|
||||
fprintf(stderr, "New filename received: %s\n", sFilename);
|
||||
fflush(stderr);
|
||||
SetEvent(event);
|
||||
} else {
|
||||
fprintf(stderr, "New volume received: %d\n", data2);
|
||||
fflush(stderr);
|
||||
sVolume = data2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if(argc != 3) {
|
||||
return printf("Usage: %s <volume 0-255> <filename.mid>\n", argv[0]);
|
||||
return fprintf(stderr, "Usage: %s <volume 0-255> <filename.mid>\n", argv[0]);
|
||||
}
|
||||
|
||||
sVolume = atoi(argv[1]);
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
hWaitingThread = CreateThread(NULL, 0, MyThreadFunction, NULL, 0, NULL);
|
||||
|
||||
sFilename = argv[2];
|
||||
|
||||
while (1) {
|
||||
example9(sFilename);
|
||||
fprintf(stdout, "1");
|
||||
fflush(stdout);
|
||||
WaitForSingleObject(event, INFINITE);
|
||||
}
|
||||
|
||||
example9(argv[2], atoi(argv[1]));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue