From 953b19e539048b3587dfb8dd3cbdd8e7084059a0 Mon Sep 17 00:00:00 2001 From: AyuanX Date: Sun, 11 Jul 2021 19:30:21 +1000 Subject: [PATCH] Implement an alternative method to control MIDI volume This should make MIDI volume scaling coefficients identical to CDDA/WAVE volume control. --- README.md | 7 +++++-- player.c | 8 ++++---- stubs.c | 43 +++++++++++++++++++++++++------------------ 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index eaacf27..950759c 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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. diff --git a/player.c b/player.c index 7249351..27dc2af 100644 --- a/player.c +++ b/player.c @@ -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() @@ -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) @@ -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)); diff --git a/stubs.c b/stubs.c index 3a70ae3..4b9feba 100644 --- a/stubs.c +++ b/stubs.c @@ -3,14 +3,14 @@ #include #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() { @@ -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); @@ -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); } @@ -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); } @@ -93,7 +96,7 @@ 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]; @@ -101,20 +104,24 @@ MMRESULT WINAPI fake_waveOutWrite(HWAVEOUT a0, LPWAVEHDR a1, UINT a2) 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: