Skip to content

Commit

Permalink
Implement an alternative method to control MIDI volume
Browse files Browse the repository at this point in the history
This should make MIDI volume scaling coefficients identical to CDDA/WAVE volume control.
  • Loading branch information
ayuanx committed Jul 11, 2021
1 parent 53e29bb commit 953b19e
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 24 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ ogg-winmm also provides separate volume control for CDDA/MIDI/WAVE, which has be
1. Rip the audio tracks from the CD and encode them to ogg files, following naming convention:
> **Track02.ogg, Track03.ogg, Track04.ogg, ...**
Note that numbering usually starts from 02 since the first track is a data track on mixed mode CD's.
Note the numbering usually starts from 02 since the first track is a data track on mixed mode CD's.

However some games may use a pure music CD with no data tracks in which case you should start numbering them from "Track01.ogg".
In rare cases the games may use a pure audio CD with no data tracks in which case you should start numbering them from "Track01.ogg".

2. Extract the dll file and the ini file to the game folder where the game's main executable file is located.

Expand All @@ -34,6 +34,9 @@ ogg-winmm also provides separate volume control for CDDA/MIDI/WAVE, which has be

# Revisions:

v.2021.07.11:
- Implement an alternative method to control MIDI volume, which should make scaling coefficients identical to CDDA/WAVE volume control.

v.2021.07.10:
- Implement separate volume control for CDDA/MIDI/WAVE.
This is necessary because Windows does not provide any method to separate the volume control of CDDA/MIDI/WAVE ever since Vista.
Expand Down
8 changes: 4 additions & 4 deletions player.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ HWAVEOUT plr_hwo = NULL;
OggVorbis_File plr_vf;
HANDLE plr_ev = NULL;
int plr_cnt = 0;
int plr_vol = -1;
float plr_vol = -1.0;
WAVEHDR *plr_buffers[3] = { NULL, NULL, NULL };

void plr_stop()
Expand Down Expand Up @@ -46,8 +46,8 @@ void plr_stop()

void plr_volume(int vol)
{
if (vol < 0 || vol > 100) plr_vol = -1;
else plr_vol = vol;
if (vol < 0 || vol > 99) plr_vol = -1.0;
else plr_vol = vol / 100.0;
}

int plr_length(const char *path)
Expand Down Expand Up @@ -160,7 +160,7 @@ int plr_pump()
if (plr_vol != -1) {
short *sbuf = (short *)buf;
for (int x = 0, end = pos / 2; x < end; x++)
sbuf[x] *= plr_vol / 100.0;
sbuf[x] *= plr_vol;
}

WAVEHDR *header = malloc(sizeof(WAVEHDR));
Expand Down
43 changes: 25 additions & 18 deletions stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
#include <ctype.h>
#include "player.h"

static int midiVol = -1;
static int waveVol = -1;
static float midiVol = -1.0;
static float waveVol = -1.0;
static int waveBits = -1;

static HINSTANCE realWinmmDLL = NULL;

void stub_midivol(int vol) { midiVol = vol < 0 || vol > 100 ? -1 : vol; }
void stub_wavevol(int vol) { waveVol = vol < 0 || vol > 100 ? -1 : vol; }
void stub_midivol(int vol) { midiVol = vol < 0 || vol > 99 ? -1.0 : vol / 100.0; }
void stub_wavevol(int vol) { waveVol = vol < 0 || vol > 99 ? -1.0 : vol / 100.0; }

HINSTANCE getWinmmHandle()
{
Expand Down Expand Up @@ -52,6 +52,7 @@ MMRESULT WINAPI fake_midiStreamOut(HMIDISTRM a0, LPMIDIHDR a1, UINT a2)
if (funcp == NULL)
funcp = (void*)GetProcAddress(loadRealDLL(), "midiStreamOut");

#ifdef MIDI_VELOCITY_SCALING
if (midiVol != -1 && a1 && a1->lpData) {
for (int i = 0, j = a1->dwBytesRecorded; i < j; i += sizeof(DWORD)*3) {
MIDIEVENT *pe = (MIDIEVENT *)(a1->lpData + i);
Expand All @@ -62,14 +63,15 @@ MMRESULT WINAPI fake_midiStreamOut(HMIDISTRM a0, LPMIDIHDR a1, UINT a2)
char *pv = (char *)&pe->dwEvent;
if (!(pe->dwEvent&0x80)) {
/* running status */
pv[1] *= midiVol / 100.0;
pv[1] *= midiVol;
} else if ((pe->dwEvent&0xF0) < 0xB0) {
/* new voice status */
pv[2] *= midiVol / 100.0;
pv[2] *= midiVol;
}
}
}
}
#endif

return (*funcp)(a0, a1, a2);
}
Expand All @@ -80,7 +82,8 @@ MMRESULT WINAPI fake_waveOutOpen(LPHWAVEOUT a0, UINT a1, LPCWAVEFORMATEX a2, DWO
if (funcp == NULL)
funcp = (void*)GetProcAddress(loadRealDLL(), "waveOutOpen");

/* FIXME: waveOutOpen can be called multiple times of different sample bits for sound mixing */
/* FIXME: waveOutOpen can be called multiple times with different sample bits for sound mixing */
/* However, it is almost always to be 16-bits; extremely rarely to be 8-bits or of a mixed-up */
if (a2) waveBits = a2->wBitsPerSample;
return (*funcp)(a0, a1, a2, a3, a4, a5);
}
Expand All @@ -93,28 +96,32 @@ MMRESULT WINAPI fake_waveOutWrite(HWAVEOUT a0, LPWAVEHDR a1, UINT a2)
funcp = (void*)GetProcAddress(loadRealDLL(), "waveOutWrite");

/* let owr own OGG wave pass through */
if (waveVol != -1 && a1 && a1->lpData && a1->dwUser != 0xBEEF7777) {
if ((waveVol != -1 || midiVol != -1) && a1 && a1->lpData && a1->dwUser != 0xBEEF7777) {
/* Windows is f**ked up. MIDI synth driver converts MIDI to WAVE and then calls winmm.waveOutWrite!!! */
void *addr = __builtin_return_address(0);
char caller[MAX_PATH];
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(addr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
GetModuleFileName(mbi.AllocationBase, caller, MAX_PATH);

if (!strstr(strrchr(caller, '\\'), ".drv")) {
char *wave8;
float vol;
if (strstr(strrchr(caller, '\\'), ".drv")) vol = midiVol;
else vol = waveVol;

if (vol != -1) {
short *wave16;
char *wave8;
switch (waveBits) {
case 8:
wave8 = (char *)a1->lpData;
for (int i = 0; i < a1->dwBufferLength; i++) {
wave8[i] *= waveVol / 100.0;
}
break;
case 16:
wave16 = (short *)a1->lpData;
for (int i = 0; i < a1->dwBufferLength/2; i++) {
wave16[i] *= waveVol / 100.0;
for (int i = 0, j = a1->dwBufferLength/2; i < j; i++) {
wave16[i] *= vol;
}
break;
case 8:
wave8 = (char *)a1->lpData;
for (int i = 0, j = a1->dwBufferLength; i < j; i++) {
wave8[i] *= vol;
}
break;
default:
Expand Down

0 comments on commit 953b19e

Please sign in to comment.