From 0fd17f8ec7d36c87d5b2207eb0071382fa228251 Mon Sep 17 00:00:00 2001 From: leecher1337 Date: Thu, 15 Jun 2023 00:41:15 +0200 Subject: [PATCH] Initial commit --- README.md | 147 +++ bin/x64/bnk_common.bin | Bin 0 -> 8288 bytes bin/x64/inpoutx64.dll | Bin 0 -> 41472 bytes bin/x86/bnk_common.bin | Bin 0 -> 8288 bytes bin/x86/inpout32.dll | Bin 0 -> 53760 bytes src/NATV.H | 23 + src/esdev.c | 96 ++ src/esdev.h | 25 + src/esfmbank.c | 1049 +++++++++++++++++++++ src/esfmbank.dsp | 206 ++++ src/esfmbank.dsw | 29 + src/esfmregs.h | 113 +++ src/essplaymid/DRIVER.H | 172 ++++ src/essplaymid/NATV.C | 808 ++++++++++++++++ src/essplaymid/NATV.H | 23 + src/essplaymid/esfm.c | 109 +++ src/essplaymid/esfm.h | 14 + src/essplaymid/ess.h | 163 ++++ src/essplaymid/iodriver.h | 9 + src/essplaymid/iodriver/drv_buttio.c | 21 + src/essplaymid/iodriver/drv_buttio.h | 30 + src/essplaymid/iodriver/drv_giveio.c | 14 + src/essplaymid/iodriver/drv_giveio.h | 14 + src/essplaymid/iodriver/drv_inpout32.c | 12 + src/essplaymid/iodriver/drv_inpout32.h | 16 + src/essplaymid/iodriver/lib/inpout32.lib | Bin 0 -> 13338 bytes src/essplaymid/iodriver/lib/inpoutx64.lib | Bin 0 -> 12776 bytes src/essplaymid/synth.h | 72 ++ src/ins_names.h | 43 + src/ins_names_data.h | 212 +++++ src/manifest.xml | 15 + src/res.rc | 377 ++++++++ src/resource.h | 82 ++ 33 files changed, 3894 insertions(+) create mode 100644 README.md create mode 100644 bin/x64/bnk_common.bin create mode 100644 bin/x64/inpoutx64.dll create mode 100644 bin/x86/bnk_common.bin create mode 100644 bin/x86/inpout32.dll create mode 100644 src/NATV.H create mode 100644 src/esdev.c create mode 100644 src/esdev.h create mode 100644 src/esfmbank.c create mode 100644 src/esfmbank.dsp create mode 100644 src/esfmbank.dsw create mode 100644 src/esfmregs.h create mode 100644 src/essplaymid/DRIVER.H create mode 100644 src/essplaymid/NATV.C create mode 100644 src/essplaymid/NATV.H create mode 100644 src/essplaymid/esfm.c create mode 100644 src/essplaymid/esfm.h create mode 100644 src/essplaymid/ess.h create mode 100644 src/essplaymid/iodriver.h create mode 100644 src/essplaymid/iodriver/drv_buttio.c create mode 100644 src/essplaymid/iodriver/drv_buttio.h create mode 100644 src/essplaymid/iodriver/drv_giveio.c create mode 100644 src/essplaymid/iodriver/drv_giveio.h create mode 100644 src/essplaymid/iodriver/drv_inpout32.c create mode 100644 src/essplaymid/iodriver/drv_inpout32.h create mode 100644 src/essplaymid/iodriver/lib/inpout32.lib create mode 100644 src/essplaymid/iodriver/lib/inpoutx64.lib create mode 100644 src/essplaymid/synth.h create mode 100644 src/ins_names.h create mode 100644 src/ins_names_data.h create mode 100644 src/manifest.xml create mode 100644 src/res.rc create mode 100644 src/resource.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..04b23c3 --- /dev/null +++ b/README.md @@ -0,0 +1,147 @@ +About ESFM bank editor +====================== + +[ESS](https://en.wikipedia.org/wiki/ESS_Technology) PCI Soundcards like +the ES1969 (Solo1), ES1946, Allegro PCI etc. have their own 4-channel +synthesizer which is more capable than [OPL3](https://moddingwiki.shikadi.net/wiki/OPL_chip), +which these card also are capable of emulating. +Their synthesizer is called ESFM and due to its enhanced capabilities, the +driver has its own Patches-table which takes into account the 4 oeprators with +individual frequency offsets, something the basic OPL is not capable of. +The Patches-table is embedded within the driver, but I reverse-engineeres the +ES1969 driver and ported it to also support 64bit, which is available +[here](https://github.com/leecher1337/es1969). +This copy of the driver also allows you to load your custom patch set from a file +so that you can adjust the instrument tables according to your needs and maybe +implement some additional effects that can again then be ported back into +the driver. + +As it is not convenient to edit the table with a hex-editor to experiment with +it, ESFM bank editor has been created. It runs on 32bit and 64bit Windows and +allows you to load and save the patches table and also test you settings by +playing an instrument note on the ESFM card (supported ESFM card required). + +Installation +============ +Depending on the Windows version you have, either download the 32bit or 64bit +package from the "Releases" link on the right side. +Unpack all files to a directory and run esfmbank.exe + +How to use +========== +The release package ships with the default patch set that gets used by all +drivers (including i.e. Allegro DOS sound system) except the NT4 driver +which has a slightly different patch set. +Both patch sets can be obtained from +[ESSplaymid repository](https://github.com/pachuco/ESSPlayMid/tree/master/bin). + +When opening the application, the default patch set is loaded when available. +The user interface is pretty similar to that of +[OPL3BankEditor](https://github.com/Wohlstand/OPL3BankEditor). +To the left, you can choose between General Midi Melodic (0-127) and +Percussion (128-255) instruments. +When clicking on an instrument, its parameters are shown to the right side. +If an instrument has a second voice, the check box `[x] Second voice` +needs to be checked, otherwise the Tab with the second voice will be readonly. +Per voice, there are the 4 Operators that can be tuned accordingly. +For an explanation on all the settings and values, please see the +[ESFM documentation](https://github.com/jwt27/esfm). +When you change a parameter for an instrument, the change will not be applied, +unless you click on the "Apply" button. + +An exception to that rule are the "Play note" etc. buttons on the right which +temporarily apply your settings when playing the note so that you can +experiment with the parameters. +Use "Reset" button to reset the parameters to the state of the last click +on "Apply" (or to initial state if there was no "Apply" click yet). +Apply/Reset are per operator. +For testing the chord, as mentioned, use the buttons under "Testing" tab. +For them to be enabled and to work properly, you need to connect to your +ESS soundcard and for this, you need to know the correct base address of the +SB base port. The application tries to already fill in the correct port, +if you have the ESS soundcard driver installed. +Look under "Soundcard connection" frame for it. In the dropdown menu, you +can select your ESFM soundcard and the correct port should be filled in, +so you just have to click onto "Connect" button to connect to your soundcard. +Be aware that chosing a wrong port can have unforseen consequences for the +stability of you machine, so ensure that you are not using a random port there! + +The "MIDI input" tab acts as a possibility to play a MIDI file with the current +patchset from an external MIDI player. In order to make use of it, you need +the [loopMIDI](https://www.tobias-erichsen.de/software/loopmidi.html) driver +and create a virtual MIDI cable. +Than connect to the loopMIDI MIDI input in the "MIDI input" tab and press +"Connect". On the other end of the "loopMIDI" cable, attach your MIDI player. +I recommend +[TMIDI: (Tom's MIDI Player)](https://www.grandgent.com/tom/projects/tmidi/) +for that purpose. +ESFM bank editor takes the MIDI data from the player, uses the current +bank settings (which need to be applied) for MIDI playback and sends the +signals to the Soundcard. + +Patch table format +================== +The format of the patch table is pretty straightforward: +Check out [ESFM docs](https://github.com/jwt27/esfm) for register definitions. + +The patch file starts with 128 USHORTs that specify the offset of the 128 +programs defined by [General Midi](https://de.wikipedia.org/wiki/General_MIDI). + +Then follows the table with 128 USHORTs that specify the offset of the +General midi Percussion instruments also defined by general midi. + +Afterwards the actual patch definitions start, consisting of: + +Byte 0 +------ +``` + ╔═══════╦═══════╤═══════╤═══════╤═══════╤═══════╤═══════╤═══════╤═══════╗ + ║ R↓ B→ ║ 7 │ 6 │ 5 │ 4 │ 3 │ 2 │ 1 │ 0 ║ + ╠═══════╬═══════╪═══════╪═══════╪═══════╪═══════╪═══════╧═══════╪═══════╣ + ║ ║ FP4 │ FP3 │ FP2 │ FP1 │ ? │ Operation │ PAT16 ║ + ╚═══════╩═══════╧═══════╧═══════╧═══════╧═══════╧═══════════════╧═══════╝ +``` + +PAT16: Use Patch16? +Operation: + 0 - Only use voice 1 + 1 - Also use second voice, if set + 2 - Also use second voice, if not set, steal voice +FP1..FP4: Set fixed pitch for operator 1-4 + +Byte 3 +------ +Relative velocity for operator 1-4 +``` + ╔═══════╦═══════╤═══════╤═══════╤═══════╤═══════╤═══════╤═══════╤═══════╗ + ║ R↓ B→ ║ 7 │ 6 │ 5 │ 4 │ 3 │ 2 │ 1 │ 0 ║ + ╠═══════╬═══════╧═══════╪═══════╧═══════╪═══════╧═══════╪═══════╧═══════╣ + ║ ║ Rel.veloc. 4 │ Rel.veloc. 3 │ Rel.veloc. 2 │ Rel.veloc. 1 ║ + ╚═══════╩═══════════════╧═══════════════╧═══════════════╧═══════════════╝ +``` + +Byte 4-11,12-19,20-27,28-35 +--------------------------- +Registers to set for each operator as documented under "Operator registers" +in ESFM documentation. + +If `Operation` in Byte 0 is > 0 then data for the second voice follows +(Starting with byte 0 from above again). + +Compiling +========= +The application has been developed and compile with Microsoft Visual +Studio 6 and it contains a .dsp file for it, but you should also be able +to compile it with newer versions of Visual Studio. + +References +========== +[ES1969 Windows driver](https://github.com/leecher1337/es1969) + +[ESFM documentation](https://github.com/jwt27/esfm) + +Author +====== +(c)oded by leecher@dose.0wnz.at +For support use the issue tracker on the project's page: +https://github.com/leecher1337/esfmbank diff --git a/bin/x64/bnk_common.bin b/bin/x64/bnk_common.bin new file mode 100644 index 0000000000000000000000000000000000000000..99c9b52141d194ac85acdcf4d0c84c1142ee76aa GIT binary patch literal 8288 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 literal 0 HcmV?d00001 diff --git a/bin/x64/inpoutx64.dll b/bin/x64/inpoutx64.dll new file mode 100644 index 0000000000000000000000000000000000000000..23d830b5c3cb0206a222acae10e564ed394ebde4 GIT binary patch literal 41472 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 literal 0 HcmV?d00001 diff --git a/bin/x86/bnk_common.bin b/bin/x86/bnk_common.bin new file mode 100644 index 0000000000000000000000000000000000000000..99c9b52141d194ac85acdcf4d0c84c1142ee76aa GIT binary patch literal 8288 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 literal 0 HcmV?d00001 diff --git a/bin/x86/inpout32.dll b/bin/x86/inpout32.dll new file mode 100644 index 0000000000000000000000000000000000000000..567bd08230baa4025cd903ab43e6982071e558d3 GIT binary patch literal 53760 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 literal 0 HcmV?d00001 diff --git a/src/NATV.H b/src/NATV.H new file mode 100644 index 0000000..54dd3b1 --- /dev/null +++ b/src/NATV.H @@ -0,0 +1,23 @@ +VOID FAR PASCAL fmwrite (WORD wAddress, BYTE bValue); +void FAR PASCAL fmreset(); + +SHORT NATV_CalcBend(USHORT detune, USHORT iBend, USHORT iBendRange); +BYTE NATV_CalcVolume(BYTE reg1, BYTE bVelocity, BYTE bChannel); +void NATV_CalcNewVolume(BYTE bChannel); + +void note_on(BYTE bChannel, BYTE bNote, BYTE bVelocity); +void note_off(BYTE bChannel, BYTE bNote); + +void voice_on(int voiceNr); +void voice_off(int voiceNr); + +void hold_controller(BYTE bChannel, BYTE bVelocity); +void find_voice(BOOL patch1617_allowed_voice1, BOOL patch1617_allowed_voice2, BYTE bChannel, BYTE bNote); +void setup_voice(int voicenr, int offset, int bChannel, int bNote, int bVelocity); +int steal_voice(int patch1617_allowed); + +VOID MidiAllNotesOff(void); +WORD NEAR PASCAL MidiCalcFAndB (DWORD dwPitch, BYTE bBlock); +VOID NEAR PASCAL MidiPitchBend (BYTE bChannel, USHORT iBend); +VOID NEAR PASCAL MidiMessage (DWORD dwData); + diff --git a/src/esdev.c b/src/esdev.c new file mode 100644 index 0000000..89937d2 --- /dev/null +++ b/src/esdev.c @@ -0,0 +1,96 @@ +/****************************************************************** + + esdev.c - Functions to find ES1969 sound card IO port address + + ESFM Bank editor + + Copyright (c) 2023, leecher@dose.0wnz.at All Rights Reserved. + +*******************************************************************/ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "esdev.h" + +#ifdef _MSC_VER +#pragma comment(lib, "Setupapi.lib") +#endif + +// ks.h +#define KSCATEGORY_RENDER {0x65E8773EL, 0x8F56, 0x11D0, 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96} + + +DWORD EnumESSDevices(HWND hWnd, void (*EnumCB)(ESS_DEVCFG *pCfg, void *pUser), void *pUser) +{ + GUID render = KSCATEGORY_RENDER; + HDEVINFO hDevInfo; + SP_DEVINFO_DATA infoData={0}; + LOG_CONF hFirstLogConf; + CONFIGRET rcCm; + ESS_DEVCFG devCfg; + DWORD i, n, ret = 0; + + hDevInfo = SetupDiGetClassDevsW(&render, NULL, hWnd, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (hDevInfo == INVALID_HANDLE_VALUE) return 0; + + infoData.cbSize = sizeof(SP_DEVINFO_DATA); + for (n = 0; SetupDiEnumDeviceInfo(hDevInfo, n, &infoData); ++n) + { + if (SetupDiGetDeviceRegistryProperty(hDevInfo, &infoData, SPDRP_DEVICEDESC, NULL, devCfg.szDevName, sizeof(devCfg.szDevName), NULL)) + { + rcCm = CM_Get_First_Log_Conf(&hFirstLogConf, infoData.DevInst, ALLOC_LOG_CONF); + if (rcCm != CR_SUCCESS) + rcCm = CM_Get_First_Log_Conf(&hFirstLogConf, infoData.DevInst, BOOT_LOG_CONF); + if (rcCm == CR_SUCCESS) + { + /* Get the first resource descriptor handle. */ + LOG_CONF hCurLogConf = 0; + rcCm = CM_Get_Next_Res_Des(&hCurLogConf, hFirstLogConf, ResType_IO, 0, 0); + if (rcCm == CR_SUCCESS) + { + for (i=0;iIOD_Count, pIoDesc->IOD_Type, pIoDesc->IOD_Alloc_Base, + pIoDesc->IOD_Alloc_End, pIoDesc->IOD_DesFlags);*/ + devCfg.Ports[i] = (USHORT)pIoDesc->IOD_Alloc_Base; + } + HeapFree(GetProcessHeap(), 0, pIoDesc); + } + + /* Next */ + hFreeResDesc = hCurLogConf; + rcCm = CM_Get_Next_Res_Des(&hCurLogConf, hCurLogConf, ResType_IO, 0, 0); + CM_Free_Res_Des_Handle(hFreeResDesc); + if (rcCm != CR_SUCCESS) + break; + } + if (i == sizeof(devCfg.Ports)/sizeof(devCfg.Ports[0])-1) + { + EnumCB(&devCfg, pUser); + ret++; + } + } + CM_Free_Log_Conf_Handle(hFirstLogConf); + } + } + } + return ret; +} + diff --git a/src/esdev.h b/src/esdev.h new file mode 100644 index 0000000..8a9a51e --- /dev/null +++ b/src/esdev.h @@ -0,0 +1,25 @@ +/****************************************************************** + + esdev.h - Functions to find ES1969 sound card IO port address + + ESFM Bank editor + + Copyright (c) 2023, leecher@dose.0wnz.at All Rights Reserved. + +*******************************************************************/ +enum +{ + ESSPortIO = 0, + ESSPortSB = 1, + ESSPortVc = 2, + ESSPortMPU = 3, + ESSPortGP = 4 +}; + +typedef struct +{ + char szDevName[128]; + USHORT Ports[5]; +} ESS_DEVCFG; + +DWORD EnumESSDevices(HWND hWnd, void (*EnumCB)(ESS_DEVCFG *pCfg, void *pUser), void *pUser); diff --git a/src/esfmbank.c b/src/esfmbank.c new file mode 100644 index 0000000..deb22ef --- /dev/null +++ b/src/esfmbank.c @@ -0,0 +1,1049 @@ +/****************************************************************** + + esfmbank.c - Main application logic and window handling + + ESFM Bank editor + + Copyright (c) 2023, leecher@dose.0wnz.at All Rights Reserved. + +*******************************************************************/ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include "resource.h" +#include "esfmregs.h" +#include "ins_names_data.h" +#include "essplaymid/esfm.h" +#include "essplaymid/natv.h" +#include "esdev.h" + +// Load from ntdll if you want to avoid the bloated CRT +#define RtlCompareMemory memcmp + +#pragma comment (lib, "comctl32.lib") // Common controls +#pragma comment (lib, "winmm.lib") +#pragma comment (lib, "shlwapi.lib") + + +#pragma pack(1) +typedef struct +{ + USHORT offs[256]; + PATCHSET patches[256]; +} PATCHMEM; +#pragma pack() + +HINSTANCE g_hInstance; +HWND g_hMainWnd; + +PATCHMEM m_patches={0}; +PBYTE gBankMem = (PBYTE)&m_patches; + +void TellError (HWND hWnd, char *pszFormat, ...) +{ + va_list ap; + char szMsg[1024]; + int iLen; + + va_start(ap, pszFormat); + iLen = wvsprintf(szMsg, pszFormat, ap); + va_end(ap); + + szMsg[iLen++]=':'; + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)szMsg+iLen, sizeof(szMsg)-iLen, NULL); + MessageBox(hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP); +} + +// Avoid sprintf, as linking it in bloats code by 30KB!! +static char * _float_to_char(double x, char *p) +{ + char *s = p + 8; + USHORT decimals; + int units; + decimals = (int)(x * 100) % 100; + units = (int)x; + + *s = 0; + *--s = (decimals % 10) + '0'; + decimals /= 10; + *--s = (decimals % 10) + '0'; + *--s = '.'; + if (units == 0) *--s='0'; + else + while (units > 0) { + *--s = (units % 10) + '0'; + units /= 10; + } + while (s>p) *--s=' '; + return s; +} + +void CalcFreq(HWND hWnd) +{ + int MULT = GetDlgItemInt(hWnd, IDC_MUL, NULL, FALSE); + int FNUM = GetDlgItemInt(hWnd, IDC_FNUM, NULL, FALSE); + int BLOCK = GetDlgItemInt(hWnd, IDC_BLOCK, NULL, FALSE); + double FREQ = MULT * FNUM * ((double)1.00 / ((int)1<<(20 - BLOCK))) * 49716; + char szFreq[16]; + + // BLOAT BLOAT BLOAT: + //sprintf (szFreq, "%d kHz", FREQ); + _float_to_char(FREQ, szFreq); + lstrcat(szFreq, " kHz"); + SetDlgItemText(hWnd, IDC_KHZ, szFreq); +} + +void OpDlgToReg(HWND hWnd, PATCH *pat, DWORD opno) +{ + USHORT FNUM; + OPREG *op = &pat->o[opno]; + BYTE mask; + + op->r2.ATTACK = GetDlgItemInt(hWnd, IDC_ATTACK, NULL, FALSE); + op->r2.DECAY = GetDlgItemInt(hWnd, IDC_DECAY, NULL, FALSE); + op->r3.SUSTAIN = GetDlgItemInt(hWnd, IDC_SUSTAIN, NULL, FALSE); + op->r3.RELEASE = GetDlgItemInt(hWnd, IDC_RELEASE, NULL, FALSE); + op->r1.ATTENUATION = GetDlgItemInt(hWnd, IDC_ATTENUATION, NULL, FALSE); + op->r0.MULT = GetDlgItemInt(hWnd, IDC_MUL, NULL, FALSE); + op->r5.BLOCK = GetDlgItemInt(hWnd, IDC_BLOCK, NULL, FALSE); + + FNUM = GetDlgItemInt(hWnd, IDC_FNUM, NULL, FALSE); + op->r4.FNUMlo = FNUM&0xFF; + op->r5.FNUMhi = FNUM>>8; + + op->r7.WAVE = (UCHAR)SendDlgItemMessage(hWnd, IDC_WAVEFORM, CB_GETCURSEL, 0, 0); + op->r1.KSL = (UCHAR)SendDlgItemMessage(hWnd, IDC_KSL, CB_GETCURSEL, 0, 0); + op->r7.OU = (UCHAR)SendDlgItemMessage(hWnd, IDC_OUT, CB_GETCURSEL, 0, 0); + op->r7.NOISE = (UCHAR)SendDlgItemMessage(hWnd, IDC_NOISE, CB_GETCURSEL, 0, 0); + op->r5.DELAY = (UCHAR)SendDlgItemMessage(hWnd, IDC_DELAY, CB_GETCURSEL, 0, 0); + op->r6.MOD = (UCHAR)SendDlgItemMessage(hWnd, IDC_MOD, CB_GETCURSEL, 0, 0); + pat->h3 &= ~(3 << (opno<<1)); + pat->h3 |= SendDlgItemMessage(hWnd, IDC_RELVEL, CB_GETCURSEL, 0, 0) << (opno<<1); + + mask = IsDlgButtonChecked(hWnd, IDC_FP) << (4+opno); + pat->h0 &= ~mask; + pat->h0 |= mask; + op->r0.TRM = IsDlgButtonChecked(hWnd, IDC_TREMOLO); + op->r0.VIB = IsDlgButtonChecked(hWnd, IDC_VIBRATO); + op->r6.TRMD = IsDlgButtonChecked(hWnd, IDC_TREMOLOD); + op->r6.VIBD = IsDlgButtonChecked(hWnd, IDC_VIBRATOD); + op->r0.EGT = IsDlgButtonChecked(hWnd, IDC_EG); + op->r0.KSR = IsDlgButtonChecked(hWnd, IDC_KSR); + op->r6.L = IsDlgButtonChecked(hWnd, IDC_L); + op->r6.R = IsDlgButtonChecked(hWnd, IDC_R); +} + +void RegToOpDlg(HWND hWnd, PATCH *pat, DWORD opno) +{ + USHORT FNUM; + OPREG *op = &pat->o[opno]; + + SetDlgItemInt(hWnd, IDC_ATTACK, op->r2.ATTACK, FALSE); + SetDlgItemInt(hWnd, IDC_DECAY, op->r2.DECAY, FALSE); + SetDlgItemInt(hWnd, IDC_SUSTAIN, op->r3.SUSTAIN, FALSE); + SetDlgItemInt(hWnd, IDC_RELEASE, op->r3.RELEASE, FALSE); + SetDlgItemInt(hWnd, IDC_ATTENUATION, op->r1.ATTENUATION, FALSE); + SetDlgItemInt(hWnd, IDC_MUL, op->r0.MULT, FALSE); + SetDlgItemInt(hWnd, IDC_BLOCK, op->r5.BLOCK, FALSE); + + FNUM = op->r4.FNUMlo | op->r5.FNUMhi<<8; + SetDlgItemInt(hWnd, IDC_FNUM, FNUM, FALSE); + + SendDlgItemMessage(hWnd, IDC_WAVEFORM, CB_SETCURSEL, op->r7.WAVE, 0); + SendDlgItemMessage(hWnd, IDC_KSL, CB_SETCURSEL, op->r1.KSL, 0); + SendDlgItemMessage(hWnd, IDC_OUT, CB_SETCURSEL, op->r7.OU, 0); + SendDlgItemMessage(hWnd, IDC_NOISE, CB_SETCURSEL, op->r7.NOISE, 0); + SendDlgItemMessage(hWnd, IDC_DELAY, CB_SETCURSEL, op->r5.DELAY, 0); + SendDlgItemMessage(hWnd, IDC_MOD, CB_SETCURSEL, op->r6.MOD, 0); + SendDlgItemMessage(hWnd, IDC_RELVEL, CB_SETCURSEL, pat->h3>>(opno<<1) & 3 , 0); + + CheckDlgButton(hWnd, IDC_FP, (pat->h0 >> (4+opno))&1); + CheckDlgButton(hWnd, IDC_TREMOLO, op->r0.TRM); + CheckDlgButton(hWnd, IDC_VIBRATO, op->r0.VIB); + CheckDlgButton(hWnd, IDC_TREMOLOD, op->r6.TRMD); + CheckDlgButton(hWnd, IDC_VIBRATOD, op->r6.VIBD); + CheckDlgButton(hWnd, IDC_EG, op->r0.EGT); + CheckDlgButton(hWnd, IDC_KSR, op->r0.KSR); + CheckDlgButton(hWnd, IDC_L, op->r6.L); + CheckDlgButton(hWnd, IDC_R, op->r6.R); + + CalcFreq(hWnd); +} + +BOOL LoadPatchSet(HWND hWnd, char *pszFile) +{ + HANDLE hFile, hMap; + BYTE *lpMem; + BOOL bRet = FALSE; + PUSHORT pTbl; + PATCHSET *ps; + DWORD dwSize; + int i; + + if ((hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) + { + dwSize = GetFileSize(hFile, NULL); + if (dwSize > 512 && (hMap = CreateFileMapping (hFile, NULL, PAGE_READONLY, 0, 0, NULL))) + { + if (lpMem = MapViewOfFile (hMap, FILE_MAP_READ, 0, 0, 0)) + { + pTbl = (PUSHORT)lpMem; + for (i=0; i<256; i++) + { + if (pTbl[i] && pTbl[i] + sizeof(PATCH)<= dwSize) + { + ps = (PATCHSET*)&lpMem[pTbl[i]]; + m_patches.offs[i] = 512 + (i*(USHORT)sizeof(m_patches.patches[0])); + m_patches.patches[i].p[0] = ps->p[0]; + if (((HDR0*)&ps->p[0].h0)->OP > 0 && pTbl[i] + (sizeof(PATCH) * 2) <= dwSize) + m_patches.patches[i].p[1] = ps->p[1]; + } else + m_patches.offs[i] = 0; + } + bRet = TRUE; + UnmapViewOfFile (lpMem); + } + else + TellError(hWnd, "Cannot map view of file %s: ", pszFile); + CloseHandle(hMap); + } + else + TellError(hWnd, "Cannot map file %s: ", pszFile); + CloseHandle(hFile); + } + else + TellError(hWnd, "Cannot open file %s: ", pszFile); + + return bRet; +} + +BOOL SavePatchSet(HWND hWnd, char *pszFile) +{ + HANDLE hFile; + int i; + DWORD dwWritten; + USHORT offs[256]={0}; + BOOL bRet = FALSE; + + if ((hFile = CreateFile(pszFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL)) != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, offs, sizeof(offs), &dwWritten, NULL); + for (i=0; i<256; i++) + { + if (m_patches.offs[i]) + { + offs[i] = (USHORT)SetFilePointer(hFile, 0, NULL, FILE_CURRENT); + if (!WriteFile(hFile, &m_patches.patches[i].p[0], sizeof(m_patches.patches[i].p[0]), &dwWritten, NULL)) + break; + if (((HDR0*)&m_patches.patches[i].p[0].h0)->OP > 0 && + !WriteFile(hFile, &m_patches.patches[i].p[1], sizeof(m_patches.patches[i].p[1]), &dwWritten, NULL)) + break; + } + } + SetFilePointer(hFile, 0, NULL, FILE_BEGIN); + if (!WriteFile(hFile, &offs, sizeof(offs), &dwWritten, NULL)) i=0; + if (i == 256) bRet = TRUE; + else TellError(hWnd, "Error writing file %s:"); + + CloseHandle(hFile); + } + else + TellError(hWnd, "Cannot open file %s: ", pszFile); + + return bRet; +} + +// +// The MIDI listener +// +void CALLBACK midiCB(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) +{ + if(wMsg == MIM_DATA) MidiMessage((DWORD)dwParam1); +} + +HMIDIIN MIDIlstnStart(HWND hWnd, UINT devIndex) +{ + HMIDIIN hmi; + MIDIINCAPSA caps; + MMRESULT res; + + if ((res = midiInGetDevCapsA(devIndex, &caps, sizeof(MIDIINCAPSA))) != MMSYSERR_NOERROR || + (res = midiInOpen(&hmi, devIndex, (DWORD_PTR)&midiCB, 0, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR || + (res = midiInStart(hmi)) != MMSYSERR_NOERROR) + { + char szMsg[128]; + + wsprintf (szMsg, "Cannot open MIDI device, failed with error: %d", res); + MessageBox(hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP); + return NULL; + } + return hmi; +} + +void MIDIlstnStop(HMIDIIN hmi) +{ + midiInStop(hmi); + midiInClose(hmi); +} + +void RegToVoiceDlg(HWND hWnd, PATCH *pat) +{ + TCITEM tci={0}; + int i, nItems; + HWND hWndTab = GetDlgItem(hWnd, IDC_TABCHANNELS); + + CheckDlgButton(hWnd, IDC_PAT16, ((HDR0*)&pat->h0)->PAT16); + nItems = TabCtrl_GetItemCount(hWndTab); + tci.mask = TCIF_PARAM; + for (i=0; i 0); + CheckDlgButton(hWnd, IDC_STEAL, OP > 1); + } + EnableWindow(GetDlgItem(hWnd, IDC_STEAL), OP > 0); + tci.mask = TCIF_PARAM; + TabCtrl_GetItem (hWndTab, 1, &tci); + EnableWindow((HWND)tci.lParam, OP > 0); +} + +void PatchSetToMask(HWND hWnd, PATCHSET *ps) +{ + TCITEM tci={0}; + int i, nItems; + HWND hWndTab = GetDlgItem(hWnd, IDC_TABVOICE); + + nItems = TabCtrl_GetItemCount(hWndTab); + tci.mask = TCIF_PARAM; + for (i=0; ip[i]); + } + Change2ndVoice(hWnd, ((HDR0*)&ps->p[0].h0)->OP, TRUE); +} + +void ApplyMainDlg(HWND hWndMain, PATCHSET *ps, DWORD dwVoice) +{ + HDR0 *h0; + + h0 = (HDR0*)&ps->p[dwVoice].h0; + if (h0->OP = IsDlgButtonChecked(hWndMain, IDC_2NDVOICE)) + h0->OP += IsDlgButtonChecked(hWndMain, IDC_STEAL); +} + +void ApplyVoiceDlg(HWND hWndVoice, PATCHSET *ps, DWORD dwVoice) +{ + ((HDR0*)&ps->p[dwVoice].h0)->PAT16 = IsDlgButtonChecked(hWndVoice, IDC_PAT16); +} + +void ApplyOp(HWND hWndOp, PATCHSET *ps) +{ + DWORD dwOp, dwVoice; + HWND hWndVoice = GetParent(GetParent(hWndOp)), hWndMain; + HWND hWndTab = GetDlgItem(hWndVoice, IDC_TABCHANNELS); + + dwOp = TabCtrl_GetCurSel(hWndTab); + + hWndMain = GetParent(GetParent(hWndVoice)); + hWndTab = GetDlgItem(hWndMain, IDC_TABVOICE); + dwVoice = TabCtrl_GetCurSel(hWndTab); + OpDlgToReg(hWndOp, &ps->p[dwVoice], dwOp); + ApplyVoiceDlg(hWndVoice, ps, dwVoice); + ApplyMainDlg(hWndMain, ps, dwVoice); +} + +void ResetOp(HWND hWndOp, PATCHSET *ps) +{ + DWORD dwOp, dwVoice; + HWND hWndVoice = GetParent(GetParent(hWndOp)); + HWND hWndTab = GetDlgItem(hWndVoice, IDC_TABCHANNELS), hWndMain; + + dwOp = TabCtrl_GetCurSel(hWndTab); + + hWndMain = GetParent(GetParent(hWndVoice)); + hWndTab = GetDlgItem(hWndMain, IDC_TABVOICE); + dwVoice = TabCtrl_GetCurSel(hWndTab); + RegToOpDlg(hWndOp, &ps->p[dwVoice], dwOp); + Change2ndVoice(hWndOp, ((HDR0*)&ps->p[0].h0)->OP, TRUE); +} + +BOOL VoiceChanged(HWND hWndMain, HWND hWndVoice, DWORD dwVoice, PATCHSET *ps, BOOL fApply) +{ + TCITEM tci={0}; + int i, nItems; + HWND hWndTab = GetDlgItem(hWndVoice, IDC_TABCHANNELS); + PATCHSET curPs; + + curPs = *ps; + nItems = TabCtrl_GetItemCount(hWndTab); + tci.mask = TCIF_PARAM; + for (i=0; ip[dwVoice], sizeof(curPs.p[dwVoice]))) + { + if (fApply) RtlCopyMemory(&ps->p[dwVoice], &curPs.p[dwVoice], sizeof(curPs.p[dwVoice])); + else return TRUE; + } + + return FALSE; +} + +BOOL PatchsetChanged(HWND hWnd, PATCHSET *ps, BOOL fApply) +{ + TCITEM tci={0}; + int i, nItems; + HWND hWndTab = GetDlgItem(hWnd, IDC_TABVOICE); + BOOL bRet = FALSE; + + nItems = TabCtrl_GetItemCount(hWndTab); + tci.mask = TCIF_PARAM; + for (i=0; icode) + { + case TCN_SELCHANGING: + case TCN_SELCHANGE: + { + TCITEM tci={0}; + int nCurSel; + + nCurSel = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom); + tci.mask = TCIF_PARAM; + + TabCtrl_GetItem (((LPNMHDR)lParam)->hwndFrom, nCurSel, &tci); + ShowWindow ((HWND)tci.lParam, ((LPNMHDR)lParam)->code==TCN_SELCHANGING?SW_HIDE:SW_SHOW); + SetFocus ((HWND)tci.lParam); + break; + } + } + break; + } + } + return FALSE; + +} + +//=============================== MAIN DIALOG ====================================// + +void EnablePlayButtons(HWND hWnd, BOOL fEnable) +{ + UINT auMidiButt[] = {IDC_PLAY, IDC_PLAYMJCHORD, IDC_PLAYMNCHORD, IDC_PLAYAUGCHORD, + IDC_PLAYDIMCHORD, IDC_PLAYMJ7CHORD, IDC_PLAYMN7CHORD, IDC_SHUT}, i; + + for (i=0; iszDevName), + MAKELPARAM(pCfg->Ports[ESSPortIO], pCfg->Ports[ESSPortSB])); +} + + +LRESULT CALLBACK MainDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static int iLastSel = 0; + static char szCurrentFileName[MAX_PATH]={0}; + static HMIDIIN hmi = NULL; + + switch (message) + { + case WM_INITDIALOG: + { + char *pszTabs[] = {TEXT("Voice 1"), TEXT("Voice 2")}; + + SendDlgItemMessage (hWnd, IDC_SPINNOTE, UDM_SETRANGE, 0, MAKELONG(127,0)); + SetDlgItemInt(hWnd, IDC_NOTE, 60, FALSE); + CreateTabs(hWnd, IDC_TABVOICE, IDD_PROPPAGE_VOICES, VoiceDlgProc, pszTabs, sizeof(pszTabs)/sizeof(pszTabs[0])); + SendDlgItemMessage (hWnd, IDC_INSTRUMENTS, LB_INITSTORAGE, 128, 128*20); + CheckDlgButton(hWnd, IDC_MELODIC, BST_CHECKED); + SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_MELODIC, BN_CLICKED), 0); + SendMessage((HWND)lParam, LB_SETCURSEL, 0, 0); + if (EnumESSDevices(hWnd, EnumDevCB, hWnd)) + { + HWND hWndCB = GetDlgItem(hWnd, IDC_DEVICE); + SendMessage(hWndCB, CB_SETCURSEL, 0, 0); + SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_DEVICE, CBN_SELCHANGE), (LPARAM)hWndCB); + } + EnumMIDIDevices(hWnd); + if (GetFileAttributes("bnk_common.bin") != 0xFFFFFFFF) + { + LoadPatchSet(hWnd, "bnk_common.bin"); + PatchSetToMask(hWnd, &m_patches.patches[0]); + } + return TRUE; + } + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDC_PERCUSSION: + case IDC_MELODIC: + if (HIWORD(wParam) == BN_CLICKED) + { + int i, j; + char kind = LOWORD(wParam) == IDC_MELODIC?'M':'P', szInstrument[32]; + HWND hWndInstr = GetDlgItem(hWnd, IDC_INSTRUMENTS); + + SendMessage(hWndInstr, LB_RESETCONTENT, 0, 0); + for (i=0, j=0; i", j); + SendMessage(hWndInstr, LB_ADDSTRING, 0, (LPARAM)szInstrument); + i--; + } + j++; + } + } + UpdateWindow (hWndInstr); + + } + case IDC_INSTRUMENTS: + if (HIWORD(wParam) == LBN_SELCHANGE) + { + int iCurSel = GetCurrentInstrument(hWnd); + int iMsgReturn = IDNO; + if (iCurSel != LB_ERR && iCurSel != iLastSel) + { + if (PatchsetChanged(hWnd, &m_patches.patches[iLastSel], FALSE)) + { + switch (iMsgReturn = MessageBox(hWnd, + "You have not applies changes made to the instrument yet. Do you want to save the changes now?", "Save changes?", + MB_YESNOCANCEL | MB_ICONQUESTION)) + { + case IDYES: + PatchsetChanged(hWnd, &m_patches.patches[iLastSel], TRUE); + break; + case IDCANCEL: + iCurSel = iLastSel; + SendMessage((HWND)lParam, LB_SETCURSEL, iCurSel, 0); + break; + } + } + if (iMsgReturn != IDCANCEL) + { + PatchSetToMask(hWnd, &m_patches.patches[iCurSel]); + iLastSel = iCurSel; + } + } + } + break; + case IDC_DEVICE: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + char szPort[8]; + LPARAM lPorts = SendMessage((HWND)lParam, CB_GETITEMDATA, SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0), 0); + wsprintf(szPort, "%x", HIWORD(lPorts)); + SetDlgItemText(hWnd, IDC_SBBASE, szPort); + } + break; + case IDC_2NDVOICE: + Change2ndVoice(hWnd, SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_CHECKED, FALSE); + break; + case IDC_CONNECTDEV: + if (SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_CHECKED) + { + char szPort[16] = {'0','x',0}; + int iPort; + + // Connect + if (!GetDlgItemText(hWnd, IDC_SBBASE, szPort+2, sizeof(szPort)-2) || + !StrToIntEx(szPort, STIF_SUPPORT_HEX, &iPort)) + { + MessageBox(hWnd, "Invalid IO Address given, has to be hexadecimal address, please correct.", "Error", + MB_OK | MB_ICONSTOP); + SendMessage((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0); + break; + } + if (!esfm_init((USHORT)iPort)) + { + MessageBox(hWnd, "Cannot initialize Port IO driver, are you sure that it is installed and running and you started this application as administrator?", "Error", + MB_OK | MB_ICONSTOP); + SendMessage((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0); + break; + } + else + { + EnablePlayButtons(hWnd, TRUE); + } + break; + } + else + { + esfm_exit(); + EnablePlayButtons(hWnd, FALSE); + } + break; + case IDC_CONNECTMIDI: + if (SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_CHECKED) + { + // Connect + HWND hWndCB = GetDlgItem(hWnd, IDC_MIDIDEV); + int iSel = (int)SendMessage(hWndCB, CB_GETCURSEL, 0, 0); + + if (iSel != CB_ERR) + { + hmi = MIDIlstnStart(hWnd, (UINT)SendMessage(hWndCB, CB_GETITEMDATA, iSel, 0)); + if (hmi) break; + } + else + { + MessageBox(hWnd, "No MIDI input device selected", "Error", MB_OK | MB_ICONSTOP); + } + SendMessage((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0); + break; + } + else + { + if (hmi) + { + MIDIlstnStop(hmi); + hmi = NULL; + } + } + break; + + case IDC_PLAY: + case IDC_PLAYMJCHORD: + case IDC_PLAYMNCHORD: + case IDC_PLAYAUGCHORD: + case IDC_PLAYDIMCHORD: + case IDC_PLAYMJ7CHORD: + case IDC_PLAYMN7CHORD: + { + int n = GetDlgItemInt(hWnd, IDC_NOTE, NULL, FALSE); + int iInstrument = GetCurrentInstrument(hWnd); + PATCHSET psBak = m_patches.patches[iInstrument]; + + // Temporarily apply current settings for preview + PatchsetChanged(hWnd, &m_patches.patches[iInstrument], TRUE); + MidiMessage(0xc0 | (iInstrument << 8)); + MidiMessage(0x7f0090 | (n << 8)); + switch (LOWORD(wParam)) + { + case IDC_PLAYMJCHORD: + MidiMessage(0x7f0090 | ((n-12) << 8)); + MidiMessage(0x7f0090 | ((n+4) << 8)); + MidiMessage(0x7f0090 | ((n-5) << 8)); + break; + case IDC_PLAYMNCHORD: + MidiMessage(0x7f0090 | ((n-12) << 8)); + MidiMessage(0x7f0090 | ((n+3) << 8)); + MidiMessage(0x7f0090 | ((n-5) << 8)); + break; + case IDC_PLAYAUGCHORD: + MidiMessage(0x7f0090 | ((n-12) << 8)); + MidiMessage(0x7f0090 | ((n+3) << 8)); + MidiMessage(0x7f0090 | ((n-4) << 8)); + break; + case IDC_PLAYDIMCHORD: + MidiMessage(0x7f0090 | ((n-12) << 8)); + MidiMessage(0x7f0090 | ((n+3) << 8)); + MidiMessage(0x7f0090 | ((n-6) << 8)); + break; + case IDC_PLAYMJ7CHORD: + MidiMessage(0x7f0090 | ((n-12) << 8)); + MidiMessage(0x7f0090 | ((n-2) << 8)); + MidiMessage(0x7f0090 | ((n+4) << 8)); + MidiMessage(0x7f0090 | ((n-5) << 8)); + break; + case IDC_PLAYMN7CHORD: + MidiMessage(0x7f0090 | ((n-12) << 8)); + MidiMessage(0x7f0090 | ((n-2) << 8)); + MidiMessage(0x7f0090 | ((n+3) << 8)); + MidiMessage(0x7f0090 | ((n-5) << 8)); + break; + } + m_patches.patches[iInstrument] = psBak; + break; + } + case IDC_SHUT: + MidiAllNotesOff(); + break; + case IDM_SAVEAS: + case IDM_OPEN: + { + OPENFILENAME ofn={0}; + char szFileName[MAX_PATH]={0}; + + ofn.lStructSize=sizeof(ofn); + ofn.hwndOwner=hWnd; + ofn.nMaxFile=MAX_PATH; + ofn.lpstrFile=szFileName; + ofn.Flags=OFN_NOCHANGEDIR | OFN_FILEMUSTEXIST; + ofn.lpstrDefExt="bin"; + ofn.lpstrFilter=".bin files\0*.bin\0All files (*.*)\0*.*\0\0"; + ofn.lpstrTitle="Load patches"; + if (LOWORD(wParam) == IDM_OPEN) + { + lstrcpy(szFileName, szCurrentFileName); + if (GetOpenFileName(&ofn) && *szFileName) + { + if (LoadPatchSet(hWnd, szFileName)) + { + int iCurSel = GetCurrentInstrument(hWnd); + + lstrcpy(szCurrentFileName, szFileName); + EnableMenuItem(GetMenu(hWnd), IDM_SAVE, MF_ENABLED); + if (iCurSel == LB_ERR) + { + iCurSel = 0; + } + PatchSetToMask(hWnd, &m_patches.patches[iCurSel]); + } + } + } + else + { + ofn.Flags=OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn) && *szFileName) + { + if (SavePatchSet(hWnd, szFileName)) + { + lstrcpy(szCurrentFileName, szFileName); + EnableMenuItem(GetMenu(hWnd), IDM_SAVE, MF_ENABLED); + } + } + } + break; + } + case IDM_SAVE: + SavePatchSet(hWnd, szCurrentFileName); + break; + case IDM_EXIT: + SendMessage(hWnd, WM_CLOSE, 0, 0); + break; + } + break; + } + case WM_NOTIFY: + { + switch(((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGING: + case TCN_SELCHANGE: + { + TCITEM tci={0}; + int nCurSel; + + nCurSel = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom); + tci.mask = TCIF_PARAM; + + TabCtrl_GetItem (((LPNMHDR)lParam)->hwndFrom, nCurSel, &tci); + ShowWindow ((HWND)tci.lParam, ((LPNMHDR)lParam)->code==TCN_SELCHANGING?SW_HIDE:SW_SHOW); + SetFocus ((HWND)tci.lParam); + break; + } + } + break; + } + case WM_CLOSE: + { + DestroyWindow (hWnd); + break; + } + case WM_DESTROY: + { + PostQuitMessage(0); + break; + } + } + return FALSE; +} + +WPARAM MessagePump (HWND hWnd) +{ + MSG msg; + + while (GetMessage (&msg, NULL, 0, 0)>0) + { + if(!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + return msg.wParam; +} + +WPARAM ProcessPendingMessages(HWND hWnd) +{ + MSG msg; + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)>0) + { + if(!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + return msg.wParam; +} + + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow ) +{ + int iRetVal, i; + HWND hWndSplash; + + g_hInstance = hInstance; + InitCommonControls(); + + // Init our bloated virtual patches-table + for (i=0; i127+35 && i<127+81)) + m_patches.offs[i] = 512 + (i*(USHORT)sizeof(m_patches.patches[0])); + + hWndSplash = CreateDialog (hInstance, MAKEINTRESOURCE(IDD_SPLASH), NULL, (DLGPROC)SplashDlgProc); + ProcessPendingMessages(hWndSplash); + g_hMainWnd = CreateDialog (hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)MainDlgProc); + ProcessPendingMessages(g_hMainWnd); + DestroyWindow(hWndSplash); + if (g_hMainWnd) + { + iRetVal = (int)MessagePump (g_hMainWnd); + } + else + { + TellError (NULL, "Cannot create main window"); + } + + return iRetVal; +} diff --git a/src/esfmbank.dsp b/src/esfmbank.dsp new file mode 100644 index 0000000..27c56e4 --- /dev/null +++ b/src/esfmbank.dsp @@ -0,0 +1,206 @@ +# Microsoft Developer Studio Project File - Name="esfmbank" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** NICHT BEARBEITEN ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=esfmbank - Win32 Debug +!MESSAGE Dies ist kein gltiges Makefile. Zum Erstellen dieses Projekts mit NMAKE +!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und fhren Sie den Befehl +!MESSAGE +!MESSAGE NMAKE /f "esfmbank.mak". +!MESSAGE +!MESSAGE Sie knnen beim Ausfhren von NMAKE eine Konfiguration angeben +!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: +!MESSAGE +!MESSAGE NMAKE /f "esfmbank.mak" CFG="esfmbank - Win32 Debug" +!MESSAGE +!MESSAGE Fr die Konfiguration stehen zur Auswahl: +!MESSAGE +!MESSAGE "esfmbank - Win32 Release" (basierend auf "Win32 (x86) Application") +!MESSAGE "esfmbank - Win32 Debug" (basierend auf "Win32 (x86) Application") +!MESSAGE "esfmbank - Win32 Release x64" (basierend auf "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=xicl6.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "esfmbank - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc07 /d "NDEBUG" +# ADD RSC /l 0xc07 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /libpath:"essplaymid\iodriver\lib" + +!ELSEIF "$(CFG)" == "esfmbank - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc07 /d "_DEBUG" +# ADD RSC /l 0xc07 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept /libpath:"essplaymid\iodriver\lib" +# SUBTRACT LINK32 /nodefaultlib + +!ELSEIF "$(CFG)" == "esfmbank - Win32 Release x64" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "esfmbank___Win32_Release_x64" +# PROP BASE Intermediate_Dir "esfmbank___Win32_Release_x64" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "esfmbank___Win32_Release_x64" +# PROP Intermediate_Dir "esfmbank___Win32_Release_x64" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WIN64" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc07 /d "NDEBUG" +# ADD RSC /l 0xc07 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /libpath:"essplaymid\iodriver\lib" +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib bufferoverflowU.lib /nologo /subsystem:windows /machine:I386 /libpath:"essplaymid\iodriver\lib" /machine:AMD64 +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "esfmbank - Win32 Release" +# Name "esfmbank - Win32 Debug" +# Name "esfmbank - Win32 Release x64" +# Begin Group "Quellcodedateien" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\essplaymid\iodriver\drv_inpout32.c +# End Source File +# Begin Source File + +SOURCE=.\esdev.c +# End Source File +# Begin Source File + +SOURCE=.\essplaymid\esfm.c +# End Source File +# Begin Source File + +SOURCE=.\esfmbank.c +# End Source File +# Begin Source File + +SOURCE=.\essplaymid\NATV.C +# End Source File +# End Group +# Begin Group "Header-Dateien" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\essplaymid\DRIVER.H +# End Source File +# Begin Source File + +SOURCE=.\essplaymid\iodriver\drv_inpout32.h +# End Source File +# Begin Source File + +SOURCE=.\esdev.h +# End Source File +# Begin Source File + +SOURCE=.\essplaymid\esfm.h +# End Source File +# Begin Source File + +SOURCE=.\esfmregs.h +# End Source File +# Begin Source File + +SOURCE=.\essplaymid\ess.h +# End Source File +# Begin Source File + +SOURCE=.\ins_names.h +# End Source File +# Begin Source File + +SOURCE=.\ins_names_data.h +# End Source File +# Begin Source File + +SOURCE=.\essplaymid\iodriver.h +# End Source File +# Begin Source File + +SOURCE=.\essplaymid\NATV.H +# End Source File +# Begin Source File + +SOURCE=.\essplaymid\synth.h +# End Source File +# End Group +# Begin Group "Ressourcendateien" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res.rc +# End Source File +# End Group +# Begin Source File + +SOURCE=.\manifest.xml +# End Source File +# End Target +# End Project diff --git a/src/esfmbank.dsw b/src/esfmbank.dsw new file mode 100644 index 0000000..c298b1e --- /dev/null +++ b/src/esfmbank.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELSCHT WERDEN! + +############################################################################### + +Project: "esfmbank"=".\esfmbank.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/src/esfmregs.h b/src/esfmregs.h new file mode 100644 index 0000000..f511390 --- /dev/null +++ b/src/esfmregs.h @@ -0,0 +1,113 @@ +/****************************************************************** + + esfmregs.h - Structures for ESFM registers and patches file + + ESFM Bank editor + + Copyright (c) 2023, leecher@dose.0wnz.at All Rights Reserved. + +*******************************************************************/ +#pragma pack(1) + +typedef struct +{ + unsigned char MULT:4; + unsigned char KSR:1; + unsigned char EGT:1; + unsigned char VIB:1; + unsigned char TRM:1; +} OPREG0; + +typedef struct +{ + unsigned char ATTENUATION:6; + unsigned char KSL:2; +} OPREG1; + +typedef struct +{ + unsigned char DECAY:4; + unsigned char ATTACK:4; +} OPREG2; + +typedef struct +{ + unsigned char RELEASE:4; + unsigned char SUSTAIN:4; +} OPREG3; + +typedef struct +{ + unsigned char FNUMlo; +} OPREG4; + +typedef struct +{ + unsigned char FNUMhi:2; + unsigned char BLOCK:3; + unsigned char DELAY:3; +} OPREG5; + +typedef struct +{ + unsigned char unk:1; + unsigned char MOD:3; + unsigned char L:1; + unsigned char R:1; + unsigned char VIBD:1; + unsigned char TRMD:1; +} OPREG6; + +typedef struct +{ + unsigned char WAVE:3; + unsigned char NOISE:2; + unsigned char OU:3; +} OPREG7; + +typedef struct +{ + unsigned char PAT16:1; + unsigned char OP:2; + unsigned char unk1:1; + unsigned char FP1:1; + unsigned char FP2:1; + unsigned char FP3:1; + unsigned char FP4:1; +} HDR0; + +typedef struct { + unsigned char RV1:2; + unsigned char RV2:2; + unsigned char RV3:2; + unsigned char RV4:2; +} HDR3; + +typedef struct +{ + OPREG0 r0; + OPREG1 r1; + OPREG2 r2; + OPREG3 r3; + OPREG4 r4; + OPREG5 r5; + OPREG6 r6; + OPREG7 r7; +} OPREG; + +typedef struct +{ + unsigned char h0; + unsigned char h1; + unsigned char h2; + unsigned char h3; + + OPREG o[4]; +} PATCH; + +typedef struct +{ + PATCH p[2]; +} PATCHSET; + +#pragma pack() diff --git a/src/essplaymid/DRIVER.H b/src/essplaymid/DRIVER.H new file mode 100644 index 0000000..37f5676 --- /dev/null +++ b/src/essplaymid/DRIVER.H @@ -0,0 +1,172 @@ +/**************************************************************************** + * + * driver.h + * + * Copyright (c) 1991 Microsoft Corporation. All Rights Reserved. + * + ***************************************************************************/ + +//#include +#include "synth.h" + + + + +#define SR_ALERT_NORESOURCE 11 +#define DATA_FMPATCHES 1234 + +#ifndef RC_INVOKED +#define RT_BINARY MAKEINTRESOURCE( 256 ) +#else +#define RT_BINARY 256 +#endif + + + +// +// Porting stuff +// + +#define BCODE + +#define fEnabled TRUE + +#define AsULMUL(a, b) ((DWORD)((DWORD)(a) * (DWORD)(b))) +#define AsLSHL(a, b) ((DWORD)((DWORD)(a) << (DWORD)(b))) +#define AsULSHR(a, b) ((DWORD)((DWORD)(a) >> (DWORD)(b))) + +#define AsMemCopy CopyMemory + +extern HANDLE MidiDeviceHandle; +extern SYNTH_DATA DeviceData[]; +extern int MidiPosition; +extern VOID MidiFlush(VOID); +extern VOID MidiCloseDevice(HANDLE DeviceHandle); +extern MMRESULT MidiOpenDevice(LPHANDLE lpHandle, BOOL Write); +extern MMRESULT MidiSetVolume(DWORD Left, DWORD Right); +extern VOID MidiCheckVolume(VOID); +extern MMRESULT MidiGetVolume(LPDWORD lpVolume); + +#define SYNTH_DATA_SIZE 80 + +#define NUM2VOICES (18) /* # 2operator voices */ +#define OPS_PER_CHAN (4) /* Operators per channel */ + +extern VOID FAR PASCAL MidiSendFM (DWORD wAddress, BYTE bValue); +extern VOID FAR PASCAL MidiNewVolume (WORD wLeft, WORD wRight); +extern WORD FAR PASCAL MidiInit (VOID); + +extern BYTE gbVelocityAtten[32]; + +// +// End of porting stuff +// + +/* + * midi device type - determined by kernel driver + */ +extern UINT gMidiType; +/* + * values for gMidiType - set in MidiOpenDevice + */ +#define TYPE_ADLIB 1 +#define TYPE_OPL3 2 + + +/* + * String IDs + * NOTE - these are shared with the drivers and should be made COMMON + * definitions + */ + +#define SR_ALERT 1 +#define SR_ALERT_NOPATCH 10 + +#define SYSEX_ERROR 0xFF // internal error code for sysexes on input + +#define STRINGLEN (100) + +/* volume defines */ +#define VOL_MIDI (0) +#define VOL_NUMVOL (1) + +#define VOL_LEFT (0) +#define VOL_RIGHT (1) + +/* MIDI defines */ + +#define NUMCHANNELS (16) +#define NUMPATCHES (256) +#define DRUMCHANNEL (9) /* midi channel 10 */ + +/**************************************************************************** + + typedefs + + ***************************************************************************/ + + +// per allocation structure for midi +typedef struct portalloc_tag { + DWORD dwCallback; // client's callback + DWORD dwInstance; // client's instance data + HMIDIOUT hMidi; // handle for stream + DWORD dwFlags; // allocation flags +}PORTALLOC, NEAR *NPPORTALLOC; + +typedef struct _voiceStruct { + BYTE flags1; + USHORT wTime; /* time that was turned on/off; + 0 time indicates that its not in use */ + BYTE bChannel; /* channel played on */ + BYTE bNote; /* note played */ + BYTE bVelocity; /* velocity */ + USHORT detune[OPS_PER_CHAN]; /* original pitch, for pitch bend */ + BYTE bPatch; /* what patch is the note, + drums patch = drum note + 128 */ + BYTE reg5[OPS_PER_CHAN]; /* Delay, block */ + BYTE reg1[OPS_PER_CHAN]; /* KSL and Attenuation*/ +} voiceStruct; + + + +/**************************************************************************** + + strings + + ***************************************************************************/ + +#define INI_STR_PATCHLIB TEXT("Patches") +#define INI_SOUND TEXT("synth.ini") +#define INI_DRIVER TEXT("Driver") + + +/**************************************************************************** + + globals + + ***************************************************************************/ + +/* midi.c */ +extern BYTE gbMidiInUse; /* if MIDI is in use */ + +extern HMODULE hDriverModule1; // our module handle + +/*************************************************************************** + + prototypes + +***************************************************************************/ + +/* midi.c */ +VOID NEAR PASCAL MidiMessage (DWORD dwData); +DWORD APIENTRY modSynthMessage(UINT id, + UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2); +UINT MidiOpen (VOID); +VOID MidiClose (VOID); +void MidiReset(void); + + + + + diff --git a/src/essplaymid/NATV.C b/src/essplaymid/NATV.C new file mode 100644 index 0000000..05fe282 --- /dev/null +++ b/src/essplaymid/NATV.C @@ -0,0 +1,808 @@ +/****************************************************************** + + natv.c - native Soundcard functions + + + Copyright (c) 2023, leecher@dose.0wnz.at All Rights Reserved. + +*******************************************************************/ + +#include +#include +#include "driver.h" +#include "natv.h" + +/* --- typedefs ----------------------------------------------- */ + +/* MIDI */ + +extern BYTE * gBankMem; +extern VOID FAR PASCAL fmwrite (WORD wAddress, BYTE bValue); + +/* transformation of linear velocity value to + logarithmic attenuation */ +BYTE gbVelocityAtten[32] = { + 40, 36, 32, 28, 23, 21, 19, 17, + 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 5, 4, 4, 3, 3, + 2, 2, 1, 1, 1, 0, 0, 0 }; + +BYTE pmask_MidiPitchBend[4] = { + 0x10, 0x20, 0x40, 0x80 }; + +BYTE pan_mask[NUMCHANNELS]; +BYTE gbVelLevel[NUMCHANNELS]; + +/* channel volumes */ +BYTE gbChanAtten[NUMCHANNELS]; /* attenuation of each channel, in .75 db steps */ +short giBend[NUMCHANNELS]; /* bend for each channel */ + +BYTE hold_table[NUMCHANNELS]; +BYTE gbChanVolume[NUMCHANNELS]; +BYTE program_table[NUMCHANNELS]; +BYTE gbChanExpr[NUMCHANNELS]; +BYTE gbChanBendRange[NUMCHANNELS]; +BYTE note_offs[NUMCHANNELS]; +voiceStruct voice_table[NUM2VOICES]; +USHORT gwTimer; +DWORD voice1, voice2; + +USHORT NATV_table1[64] = { + 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1030, 1031, 1032, + 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, + 1043, 1044, 1045, 1045, 1046, 1047, 1048, 1049, 1050, 1051, + 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, + 1062, 1063, 1064, 1065, 1065, 1066, 1067, 1068, 1069, 1070, + 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, + 1081, 1082, 1083, 1084, +}; +USHORT NATV_table2[49] = { + 256, 271, 287, 304, 323, 342, 362, 384, 406, 431, + 456, 483, 512, 542, 575, 609, 645, 683, 724, 767, + 813, 861, 912, 967, 1024, 1085, 1149, 1218, 1290, 1367, + 1448, 1534, 1625, 1722, 1825, 1933, 2048, 2170, 2299, 2435, + 2580, 2734, 2896, 3069, 3251, 3444, 3649, 3866, 4096, +}; + +int td_adjust_setup_operator[12] = { + 256, 242, 228, 215, 203, 192, + 181, 171, 161, 152, 144, 136 +}; + +SHORT fnum[12] = { + 514, 544, 577, 611, /* G , G#, A , A# */ + 647, 686, 727, 770, /* B , C , C#, D */ + 816, 864, 916, 970 /* D#, E , F, F# */ +}; + + +/************************************************************** +MidiAllNotesOff - switch off all active voices. + +inputs - none +returns - none +*/ +VOID MidiAllNotesOff(void) +{ + BYTE i; + + for (i = 0; i < NUM2VOICES; i++) { + note_off (voice_table[i].bChannel, voice_table[i].bNote); + } +} + +/***************************************************************** +MidiCalcFAndB - Calculates the FNumber and Block given + a frequency. + +inputs + DWORD dwPitch - pitch +returns + WORD - High byte contains the 0xb0 section of the + block and fNum, and the low byte contains the + 0xa0 section of the fNumber +*/ +WORD NEAR PASCAL MidiCalcFAndB (WORD wPitch, BYTE bBlock) +{ + // D1(("MidiCalcFAndB")); + /* bBlock is like an exponential to dwPitch (or FNumber) */ + for (; wPitch >= 0x400; wPitch >>= 1, bBlock++) + ; + + if (bBlock > 0x07) + bBlock = 0x07; /* we cant do anything about this */ + + /* put in high two bits of F-num into bBlock */ + return ((WORD) bBlock << 10) | (WORD) wPitch; +} + + +//------------------------------------------------------------------------ +// VOID MidiPitchBend +// +// Description: +// This pitch bends a channel. +// +// Parameters: +// BYTE bChannel +// channel +// +// short iBend +// values from -32768 to 32767, being -2 to +2 half steps +// +// Return Value: +// Nothing. +// +// +//------------------------------------------------------------------------ + +VOID NEAR PASCAL MidiPitchBend +( + BYTE bChannel, + USHORT iBend +) +{ + USHORT i, j; + SHORT bnd; + + // D1( "\nMidiPitchBend" ) ; + + // Remember the current bend.. + + giBend[ bChannel ] = iBend ; + + // Loop through all the notes looking for the right + // channel. Anything with the right channel gets its + // pitch bent... + + for (i = 0; i < NUM2VOICES; i++) + if (voice_table[ i ].bChannel == bChannel && (voice_table[ i ].flags1 & 1)) + { + for (j = 0 ; j < OPS_PER_CHAN; j++ ) + { + if ((pmask_MidiPitchBend[j] & voice_table[ i ].bPatch)) continue; + bnd = NATV_CalcBend( voice_table[ i ].detune[ j ], iBend, gbChanBendRange[bChannel] ) ; + bnd = MidiCalcFAndB( bnd, (BYTE)((voice_table[ i ].reg5[ j ] >> 2) & 7)) ; + fmwrite( (WORD)(32 * i + 8 * j + 5), (BYTE)(HIBYTE(bnd) | (voice_table[ i ].reg5[ j ] & 0xE0)) ); + fmwrite( (WORD)(32 * i + 8 * j + 4), (BYTE)(bnd & 0xFF)); + } + } + +} // end of MidiPitchBend() + + + +/* --- midi interpretation -------------------------------------*/ + + +/*************************************************************** +MidiMessage - This handles a MIDI message. This + does not do running status. + +inputs + DWORD dwData - up to 4 bytes of MIDI data + depending upon the message. +returns + none +*/ +VOID NEAR PASCAL MidiMessage (DWORD dwData) +{ + BYTE bChannel, data2, data1; + int i; + + // D1("\nMidiMessage"); + bChannel = (BYTE) dwData & (BYTE)0x0f; + data2 = (BYTE) (dwData >> 16) & (BYTE)0x7f; + data1 = (BYTE) ((WORD) dwData >> 8) & (BYTE)0x7f; + + switch ((BYTE)dwData & 0xf0) { + case 0x90: +#ifdef DEBUG + { + char szTemp[4], szDebug[ 80 ]; + char ; + szTemp[0] = "0123456789abcdef"[data1 >> 4]; + szTemp[1] = "0123456789abcdef"[data1 & 0x0f]; + szTemp[2] = ' '; + szTemp[3] = 0; + if ((bChannel == 9) && data2) D1(szTemp); + + wsprintf( szDebug, "bChannel = %d, data1 = %d", + bChannel, data1 ) ; + D1( szDebug ) ; + } +#endif + + /* turn key on, or key off if volume == 0 */ + if (data2) + { + note_on(bChannel, data1, data2); + break; + } + + /* else, continue through and turn key off */ + case 0x80: + /* turn key off */ + note_off( bChannel, data1 ); + break; + + case 0xb0: + // D1("\nChangeControl"); + /* change control */ + switch (data1) { + case 6: + if ( (hold_table[bChannel] & 6) == 6 ) + gbChanBendRange[bChannel] = data2; + break; + case 7: + gbChanAtten[bChannel] = gbVelocityAtten[data2 >> 1]; + gbChanVolume[bChannel] = data2; + NATV_CalcNewVolume(bChannel); + break; + case 8: + case 10: + /* change the pan level */ + if ( data2 <= 80 ) + { + if ( data2 >= 48 ) + pan_mask[bChannel] = 0x30; + else + pan_mask[bChannel] = 0x10; + } + else + { + pan_mask[bChannel] = 0x20; + } + break; + case 11: + /* change expression */ + gbChanExpr[bChannel] = data2; + NATV_CalcNewVolume(bChannel); + break; + case 64: + /* sustain */ + hold_controller(bChannel, data2); + break; + case 100: + if ( data2 == 0 ) + { + hold_table[bChannel] |= 2; + break; + } + case 98: + hold_table[bChannel] &= ~2; + break; + case 101: + if ( data2 == 0 ) + { + hold_table[bChannel] |= 4; + break; + } + case 99: + hold_table[bChannel] &= ~4; + break; + case 120: + case 124: + case 125: + for (i = 0; i < NUM2VOICES; i++) + { + if ((voice_table[i].flags1 & 1) && voice_table[i].bChannel == bChannel) + voice_off(i); + } + break; + case 121: + /* reset all controllers */ + if (hold_table[bChannel] & 1) + { + for (i = 0; i < NUM2VOICES; i++) + { + if ((voice_table[i].flags1 & 1) && voice_table[i].bChannel == bChannel && (voice_table[i].flags1 & 4)) + voice_off(i); + } + } + hold_table[bChannel] &= ~1u; + gbChanVolume[bChannel] = 100; + gbChanExpr[bChannel] = 127; + giBend[bChannel] = 0x2000; + pan_mask[bChannel] = 0x30; + gbChanBendRange[bChannel] = 2; + break; + case 123: + case 126: + case 127: + /* All notes off */ + for (i = 0; i < NUM2VOICES; i++) + { + if ((voice_table[i].flags1 & 1) && voice_table[i].bChannel == bChannel && (voice_table[i].flags1 & 4) == 0) + voice_off(i); + } + break; + }; + break; + + case 0xc0: + program_table[bChannel] = data1; + break; + + case 0xe0: + // D1("\nBend"); + /* pitch bend */ + MidiPitchBend (bChannel, (USHORT)data1 | ((USHORT)data2 << 7)); + + break; + }; + + return; +} + + + +/* + * fmreset - silence the board and set all voices off. + */ +void FAR PASCAL fmreset() +{ + UINT i; + + for (i=0; i<16; i++) + { + giBend[i] = 0x2000; + gbChanBendRange[i] = 0x02; + hold_table[i] = 0x00; + gbChanExpr[i] = 0x7F; + gbChanVolume[i] = 0x64; + gbChanAtten[i] = 0x04; + pan_mask[i] = 0x30; + } + + for (i=0; i < 18; i++) + { + voice_table[i].wTime = 0; + voice_table[i].flags1 = 0; + } + + gwTimer = 0; +} + +SHORT NATV_CalcBend(USHORT detune, USHORT iBend, USHORT iBendRange) +{ + //!WARN iBend is int16 in OPL midi driver sample + if ( iBend == 0x2000 ) + return detune; + else + { + int v5; + if ( iBend >= 0x3F80 ) iBend = 0x4000; + v5 = ((iBendRange * (((int)iBend - 0x2000))) >> 5) + 0x1800; + return (detune * (USHORT)((NATV_table1[(v5>>2)&0x3F] * NATV_table2[v5>>8]) >> 10) + 512) >> 10; + } +} + +BYTE NATV_CalcVolume(BYTE reg1, BYTE bVelocity, BYTE bChannel) +{ + BYTE vol; + + if ( !gbChanVolume[bChannel] ) return 63; + + switch ( bVelocity ) + { + case 0: + default: + vol = 0; + break; + case 1: + vol = ((127 - gbChanExpr[bChannel]) >> 4 ) + ((127 - gbChanVolume[bChannel]) >> 4); + break; + case 2: + vol = ((127 - gbChanExpr[bChannel]) >> 3) + ((127 - gbChanVolume[bChannel]) >> 3); + break; + case 3: + vol = gbChanVolume[bChannel]; + if ( vol < 64 ) + vol = ((63 - vol) >> 1) + 16; + else + vol = (127 - vol) >> 2; + if ( gbChanExpr[bChannel] < 64 ) + { + vol += ((63 - gbChanExpr[bChannel]) >> 1) + 16; + } + else + { + vol += ((127 - gbChanExpr[bChannel]) >> 2); + } + break; + } + vol += (reg1 & 0x3F); // ATTENUATION + if ( vol > 63 ) vol = 63; + return vol | reg1 & 0xC0; // KSL +} + +void NATV_CalcNewVolume(BYTE bChannel) +{ + WORD i, j; + + for (i=0; i < NUM2VOICES; i++) + { + voiceStruct *voice = &voice_table[i]; + if ((voice->flags1 & 1) && (voice->bChannel == bChannel || bChannel == 0xFF)) + { + for (j=0; j < OPS_PER_CHAN; j++) + fmwrite(i * 32 + j * 8 + 1, NATV_CalcVolume(voice->reg1[j], (voice->bVelocity & 3), voice->bChannel)); + } + } +} + + +void note_on(BYTE bChannel, BYTE bNote, BYTE bVelocity) +{ + int patch; + int offset; + BYTE flags_voice1; + int fixed_pitch; + + if ( bChannel == 9 ) + patch = bNote + 128; + else + patch = program_table[bChannel]; + offset = gBankMem[2 * patch] + ((int)gBankMem[2 * patch + 1] << 8); + if ( offset ) + { + flags_voice1 = gBankMem[offset]; + fixed_pitch = (flags_voice1 >> 1) & 3; + switch (fixed_pitch) + { + case 0: + find_voice(flags_voice1 & 1, 0, bChannel, bNote); + if ( voice1 == 255 ) voice1 = steal_voice(gBankMem[offset] & 1); + setup_voice(voice1, offset, bChannel, bNote, bVelocity); + voice_on(voice1); + break; + case 1: + find_voice(flags_voice1 & 1, gBankMem[offset + 36] & 1, bChannel, bNote); + if (voice1 == 255) voice1 = steal_voice(gBankMem[offset] & 1); + setup_voice(voice1, offset, bChannel, bNote, bVelocity); + if (voice2 != 255) + { + setup_voice(voice2, offset + 36, bChannel, bNote, bVelocity); + voice_table[voice2].flags1 |= 8u; + voice_on(voice2); + } + voice_on(voice1); + break; + case 2: + find_voice(flags_voice1 & 1, gBankMem[offset + 36] & 1, bChannel, bNote); + if ( voice1 == 255 ) + voice1 = steal_voice(gBankMem[offset] & 1); + if ( voice2 == 255 ) + voice2 = steal_voice(gBankMem[offset + 36] & 1); + setup_voice(voice1, offset, bChannel, bNote, bVelocity); + setup_voice(voice2, offset + 36, bChannel, bNote, bVelocity); + voice_on(voice1); + voice_on(voice2); + break; + } + gbVelLevel[bChannel] = bVelocity; + if (note_offs[bChannel] == 255) note_offs[bChannel]=0; else note_offs[bChannel]++; + } +} + +void note_off(BYTE bChannel, BYTE bNote) +{ + int i; + + for (i=0; i<18; i++) + { + voiceStruct *voice = &voice_table[i]; + if ((voice->flags1 & 1) && voice->bChannel == bChannel && voice->bNote == bNote) + { + if ( hold_table[bChannel] & 1 ) + { + voice->flags1 |= 4; + } + else + { + voice_off(i); + } + } + } +} + +void hold_controller(BYTE bChannel, BYTE bVelocity) +{ + if ( bVelocity < 64 ) + { + int i; + + hold_table[bChannel] &= ~1; + + for (i = 0; i< NUM2VOICES; i++) + { + if ((voice_table[i].flags1 & 4) && voice_table[i].bChannel == bChannel) + voice_off(i); + } + } else { + hold_table[bChannel] |= 1; + } +} + +void voice_on(int voiceNr) +{ + if ( voiceNr >= 16 ) + { + if ( voiceNr == 16 ) + { + fmwrite(0x250, 1); + fmwrite(0x251, 1); + } + else + { + fmwrite(0x252, 1); + fmwrite(0x253, 1); + } + } + else + { + fmwrite((USHORT)voiceNr + 0x240, 1); + } +} + +void voice_off(int voiceNr) +{ + if ( voiceNr >= 16 ) + { + if ( voiceNr == 16 ) + { + fmwrite(0x250, 0); + fmwrite(0x251, 0); + } + else + { + fmwrite(0x252, 0); + fmwrite(0x253, 0); + } + } + else + { + fmwrite((USHORT)voiceNr + 0x240, 0); + } + voice_table[voiceNr].flags1 = 2; + voice_table[voiceNr].wTime = (USHORT)gwTimer; + gwTimer++; +} + +void find_voice(BOOL patch1617_allowed_voice1, BOOL patch1617_allowed_voice2, BYTE bChannel, BYTE bNote) +{ + int i; + USHORT td, timediff1=0, timediff2=0; + + voice1 = voice2 = 255; + + // Patch 0-15 + for (i=0; i<16; i++) + { + voiceStruct *voice = &voice_table[i]; + if (voice->flags1 & 1) + { + if (voice->bChannel == bChannel && voice->bNote == bNote) + voice_off(i); + } + else + { + td = gwTimer - voice->wTime; + if (td < timediff1) + { + if (td >= timediff2) + { + timediff2 = td; + voice2 = i; + } + } + else + { + timediff2 = timediff1; + voice2 = voice1; + timediff1 = td; + voice1 = i; + } + } + } + + // Patch 16 + if (voice_table[16].flags1 & 1) + { + if (voice_table[16].bChannel == bChannel && voice_table[16].bNote == bNote) + voice_off(16); + } + else + { + td = gwTimer - voice_table[16].wTime; + if (patch1617_allowed_voice1 || td < timediff1) + { + if (!patch1617_allowed_voice2 && td >= timediff2) + { + timediff2 = gwTimer - voice_table[16].wTime; + voice2 = 16; + } + } + else + { + timediff2 = timediff1; + voice2 = voice1; + timediff1 = td; + voice1 = 16; + } + } + + // Patch 17 + if (voice_table[17].flags1 & 1) + { + if (voice_table[17].bChannel == bChannel && voice_table[17].bNote == bNote) + voice_off(17); + } + else + { + td = gwTimer - voice_table[17].wTime; + if (patch1617_allowed_voice1 || td < timediff1) + { + if (!patch1617_allowed_voice2 && td >= timediff2) + voice2 = 17; + } + else + { + if (voice1 != 16 || !patch1617_allowed_voice2) + voice2 = voice1; + voice1 = 17; + } + } +} + +int steal_voice(int patch1617_allowed) +{ + UINT i, last_voice=0, max_voices = (patch1617_allowed?18:16); + BYTE chn, chncmp = 0, bit3 = 0; + USHORT timediff = 0; + + for (i=0; i 114 ) + note -= 12 * (((note - 115) / 12u) + 1); + block = (note - 19) / 12; + notemod12 = (note - 19) % 12; + + fmwrite(reg + 0, gBankMem[offset]); + + switch ( rel_velocity ) + { + case 0: + default: + reg1 = 0; + break; + case 1: + reg1 = (127 - bVelocity) >> 4; + break; + case 2: + reg1 = (127 - bVelocity) >> 3; + break; + case 3: + if ( bVelocity < 64 ) + reg1 = ((63 - bVelocity) >> 1) + 16; + else + reg1 = (127 - bVelocity) >> 2; + break; + } + reg1 += (gBankMem[offset + 1] & 0x3F); // Attenuation + if (reg1 > 63) reg1 = 63; + reg1 += (gBankMem[offset + 1] & 0xC0); // KSL + voice_table[voicenr].reg1[oper] = (BYTE)reg1; + + fmwrite(reg + 1, NATV_CalcVolume((BYTE)reg1, (BYTE)rel_velocity, (BYTE)bChannel)); + fmwrite(reg + 2, gBankMem[offset + 2]); + fmwrite(reg + 3, gBankMem[offset + 3]); + + if ( fixed_pitch ) + { + reg4 = gBankMem[offset + 4]; + reg5 = gBankMem[offset + 5]; + } + else + { + detune = ((int)*((char*)&gBankMem[offset + 4])) & (~3); + if (detune) + { + detune = ((detune >> 2) * td_adjust_setup_operator[notemod12]) >> 8; + if (block > 1) + detune >>= block - 1; + } + detune += fnum[notemod12]; + voice_table[voicenr].reg5[oper] = (BYTE)((HIBYTE(detune) & 3) | (gBankMem[offset + 5] & 0xE0) | (block << 2)); // detune | delay | block + fnum_block = MidiCalcFAndB(NATV_CalcBend((USHORT)detune, giBend[bChannel], (USHORT)gbChanBendRange[bChannel]), (BYTE)block); + reg4 = LOBYTE(fnum_block); + reg5 = HIBYTE(fnum_block) | (voice_table[voicenr].reg5[oper] & 0xE0); + voice_table[voicenr].detune[oper] = (USHORT)detune; + } + reg6 = gBankMem[offset + 6]; + if ((reg6 & 0x30) && panmask != 0x30) reg6 = panmask | (reg6 & 0xCF); + fmwrite(reg + 4, reg4); + fmwrite(reg + 5, reg5); + fmwrite(reg + 6, reg6); + fmwrite(reg + 7, gBankMem[offset + 7]); +} + +void setup_voice(int voicenr, int offset, int bChannel, int bNote, int bVelocity) +{ + BYTE rel_vel, bPatch; + + bPatch = gBankMem[offset]; + rel_vel = gBankMem[offset + 3]; + offset += 4; + setup_operator(offset , bNote, bVelocity, 32 * (USHORT)voicenr + 0 , bPatch & 0x10, (rel_vel >> 0) & 3, bChannel, 0, voicenr); + setup_operator(offset + 8 , bNote, bVelocity, 32 * (USHORT)voicenr + 8 , bPatch & 0x20, (rel_vel >> 2) & 3, bChannel, 1, voicenr); + setup_operator(offset + 16, bNote, bVelocity, 32 * (USHORT)voicenr + 16, bPatch & 0x40, (rel_vel >> 4) & 3, bChannel, 2, voicenr); + setup_operator(offset + 24, bNote, bVelocity, 32 * (USHORT)voicenr + 24, bPatch & 0x80, (rel_vel >> 6) & 3, bChannel, 3, voicenr); + + voice_table[voicenr].bPatch = bPatch; + voice_table[voicenr].bVelocity = rel_vel; + voice_table[voicenr].wTime = gwTimer; + voice_table[voicenr].bNote = (BYTE)bNote; + voice_table[voicenr].flags1 = 1; + voice_table[voicenr].bChannel = (BYTE)bChannel; + + gwTimer++; +} + diff --git a/src/essplaymid/NATV.H b/src/essplaymid/NATV.H new file mode 100644 index 0000000..5216cd9 --- /dev/null +++ b/src/essplaymid/NATV.H @@ -0,0 +1,23 @@ +void FAR PASCAL fmreset(); + +SHORT NATV_CalcBend(USHORT detune, USHORT iBend, USHORT iBendRange); +BYTE NATV_CalcVolume(BYTE reg1, BYTE bVelocity, BYTE bChannel); +void NATV_CalcNewVolume(BYTE bChannel); + +void note_on(BYTE bChannel, BYTE bNote, BYTE bVelocity); +void note_off(BYTE bChannel, BYTE bNote); + +void voice_on(int voiceNr); +void voice_off(int voiceNr); + +void hold_controller(BYTE bChannel, BYTE bVelocity); +void find_voice(BOOL patch1617_allowed_voice1, BOOL patch1617_allowed_voice2, BYTE bChannel, BYTE bNote); +void setup_voice(int voicenr, int offset, int bChannel, int bNote, int bVelocity); +int steal_voice(int patch1617_allowed); + +VOID MidiAllNotesOff(void); +WORD NEAR PASCAL MidiCalcFAndB (WORD wPitch, BYTE bBlock); +VOID NEAR PASCAL MidiPitchBend (BYTE bChannel, USHORT iBend); +VOID NEAR PASCAL MidiMessage (DWORD dwData); + +void FAR PASCAL fmreset(); diff --git a/src/essplaymid/esfm.c b/src/essplaymid/esfm.c new file mode 100644 index 0000000..67faabf --- /dev/null +++ b/src/essplaymid/esfm.c @@ -0,0 +1,109 @@ +/****************************************************************** + + esfm.c - Functions for communicating with the ES1969 soundcard + + ESFM Bank editor + + Copyright (c) 2023, leecher@dose.0wnz.at All Rights Reserved. + +*******************************************************************/ +#define WIN32_LEAN_AND_MEAN +#include +#include "iodriver.h" +#include "ess.h" +#include "natv.h" + +static PUCHAR m_pSBBase = NULL; + +void KeStallExecutionProcessor(DWORD uSecTime) { + static LONGLONG freq=0; + LONGLONG start=0, cur=0, wait=0; + + if (freq == 0) QueryPerformanceFrequency((PLARGE_INTEGER)&freq); + if (freq != 0) { + wait = ((LONGLONG)uSecTime * freq)/(LONGLONG)1000000; + QueryPerformanceCounter((PLARGE_INTEGER)&start); + while (cur < (start + wait)) { + QueryPerformanceCounter((PLARGE_INTEGER)&cur); + } + } else { + //TODO: alternate timing mechanism + } +} + +VOID FAR PASCAL fmwrite (WORD wAddress, BYTE bValue) +{ + WRITE_PORT_UCHAR(m_pSBBase + 2, LOBYTE(wAddress)); + KeStallExecutionProcessor(10); + WRITE_PORT_UCHAR(m_pSBBase + 3, HIBYTE(wAddress)); + KeStallExecutionProcessor(10); + WRITE_PORT_UCHAR(m_pSBBase + 1, bValue); + KeStallExecutionProcessor(10); +} + + +UCHAR dspReadMixer(UCHAR Address) +{ + WRITE_PORT_UCHAR(m_pSBBase + ESSSB_REG_MIXERADDR, Address); + return READ_PORT_UCHAR(m_pSBBase + ESSSB_REG_MIXERDATA); +} + +void dspWriteMixer(UCHAR Address, UCHAR Value) +{ + WRITE_PORT_UCHAR(m_pSBBase + ESSSB_REG_MIXERADDR, Address); + WRITE_PORT_UCHAR(m_pSBBase + ESSSB_REG_MIXERDATA, Value); +} + +void SetDacToMidi(BOOL MidiActive) +{ + if ( MidiActive ) + { + dspWriteMixer(ESM_MIXER_MDR, dspReadMixer(ESM_MIXER_MDR) & (~0x01)); + dspWriteMixer(ESM_MIXER_FM_VOL, 119); + dspWriteMixer(ESM_MIXER_DAC_RECVOL, 0); + } +} + +void StartESFM (BOOL MidiActive) +{ + BYTE SerialMode; + + SerialMode = dspReadMixer(ESM_MIXER_SERIALMODE_CTL); + if ( MidiActive ) + SerialMode &= ~0x10; + else + SerialMode |= 0x10; + dspWriteMixer(ESM_MIXER_SERIALMODE_CTL, SerialMode); + + SetDacToMidi(MidiActive); + + if ( MidiActive ) + { + WRITE_PORT_UCHAR(m_pSBBase + ESSSB_REG_POWER, READ_PORT_UCHAR((PUCHAR)ESSSB_REG_POWER) & (~0x20)); + WRITE_PORT_UCHAR(m_pSBBase + ESSSB_REG_FMHIGHADDR, 5); + KeStallExecutionProcessor(25); + // We want ESFM mode! + WRITE_PORT_UCHAR(m_pSBBase + ESSSB_REG_FMLOWADDR + 1, 0x80); + KeStallExecutionProcessor(25); + } + else + { + WRITE_PORT_UCHAR(m_pSBBase + ESSSB_REG_POWER, READ_PORT_UCHAR((PUCHAR)ESSSB_REG_POWER) | 0x20); + } +} + +BOOL esfm_init(USHORT SBBase) +{ + m_pSBBase = (PUCHAR)SBBase; + if (!IODriver_Init(SBBase, SBBase+0xF)) + return FALSE; + StartESFM(TRUE); + fmreset(); + return TRUE; +} + +void esfm_exit() +{ + StartESFM(FALSE); + IODriver_Exit(); +} diff --git a/src/essplaymid/esfm.h b/src/essplaymid/esfm.h new file mode 100644 index 0000000..f96bfba --- /dev/null +++ b/src/essplaymid/esfm.h @@ -0,0 +1,14 @@ +/****************************************************************** + + esfm.h - Functions for communicating with the ES1969 soundcard + + ESFM Bank editor + + Copyright (c) 2023, leecher@dose.0wnz.at All Rights Reserved. + +*******************************************************************/ + +BOOL esfm_init(USHORT SBBase); +void esfm_exit(); +VOID NEAR PASCAL MidiMessage (DWORD dwData); + diff --git a/src/essplaymid/ess.h b/src/essplaymid/ess.h new file mode 100644 index 0000000..2c067e7 --- /dev/null +++ b/src/essplaymid/ess.h @@ -0,0 +1,163 @@ +/****************************************************************** + + ess.h - IO Ports and commands for ES1969 sound card + + ESFM Bank editor + + Copyright (c) 2023, leecher@dose.0wnz.at All Rights Reserved. + +*******************************************************************/ + +// Reference: https://www.alsa-project.org/files/pub/manuals/ess/DsSolo1.pdf + +/* IO device offsets */ +#define ESSIO_REG_AUDIO2DMAADDR 0 +#define ESSIO_REG_AUDIO2DMACOUNT 4 +#define ESSIO_REG_AUDIO2MODE 6 +#define ESSIO_REG_IRQCONTROL 7 + +/* DMA offsets */ +#define ESSDM_REG_DMAADDR 0x00 +#define ESSDM_REG_DMACOUNT 0x04 +#define ESSDM_REG_DMACOMMAND 0x08 +#define ESSDM_REG_DMASTATUS 0x08 +#define ESSDM_REG_DMAMODE 0x0b +#define ESSDM_REG_DMACLEAR 0x0d +#define ESSDM_REG_DMAMASK 0x0f + +/* DSP offsets */ +#define ESSSB_REG_FMLOWADDR 0x00 /* 20-voice FM synthesizer. */ +#define ESSSB_REG_FMHIGHADDR 0x02 +#define ESSSB_REG_MIXERADDR 0x04 /* Mixer Address register (port for address of mixer controller registers). */ +#define ESSSB_REG_MIXERDATA 0x05 /* Mixer Data register (port for data to/from mixer controller registers). */ + +#define ESSSB_REG_RESET 0x06 /* Audio reset and status flags */ +#define ESSSB_REG_POWER 0x07 /* Power Management register. Suspend request and FM reset. */ + +#define ESSSB_REG_READDATA 0x0a /* Input data from read buffer for command/data I/O. */ +#define ESSSB_REG_WRITEDATA 0x0c /* Output data to write buffer for command/data I/O. */ +#define ESSSB_REG_READSTATUS 0x0c /* Read embedded processor status */ + +#define ESSSB_REG_STATUS 0x0e /* Data available flag from embedded processor */ + +/* SB Commands */ +#define ESS_CMD_EXTSAMPLERATE 0xa1 +#define ESS_CMD_FILTERDIV 0xa2 +#define ESS_CMD_DMACNTRELOADL 0xa4 +#define ESS_CMD_DMACNTRELOADH 0xa5 +#define ESS_CMD_ANALOGCONTROL 0xa8 +#define ESS_CMD_IRQCONTROL 0xb1 +#define ESS_CMD_DRQCONTROL 0xb2 +#define ESS_CMD_RECLEVEL 0xb4 +#define ESS_CMD_SETFORMAT 0xb6 +#define ESS_CMD_SETFORMAT2 0xb7 +#define ESS_CMD_DMACONTROL 0xb8 +#define ESS_CMD_DMATYPE 0xb9 +#define ESS_CMD_OFFSETLEFT 0xba +#define ESS_CMD_OFFSETRIGHT 0xbb +#define ESS_CMD_READREG 0xc0 +#define ESS_CMD_ENABLEEXT 0xc6 +#define ESS_CMD_PAUSEDMA 0xd0 +#define ESS_CMD_ENABLEAUDIO1 0xd1 +#define ESS_CMD_STOPAUDIO1 0xd3 +#define ESS_CMD_AUDIO1STATUS 0xd8 +#define ESS_CMD_CONTDMA 0xd4 +#define ESS_CMD_TESTIRQ 0xf2 + +// +// MPU401 ports (page 31) +// +#define MPU401_REG_STATUS 0x01 // Status register +#define MPU401_DRR 0x40 // Output ready (for command or data) +#define MPU401_DSR 0x80 // Input ready (for data) +#define MPU401_ACK 0xFE // ACKnowledge + +#define MPU401_REG_DATA 0x00 // Data in +#define MPU401_REG_COMMAND 0x01 // Commands +#define MPU401_CMD_RESET 0xFF // Reset command +#define MPU401_CMD_UART 0x3F // Switch to UART mod + + + + +/* PCI Register (page 20) */ +#define ESM_CMD 0x04 /* Command */ +#define ESM_GAMEPORT 0x20 /* Game port I/O space base address for native PCI audio */ +#define ESM_IRQLINE 0x3C /* Interrput line */ +#define ESM_LEGACY_AUDIO_CONTROL 0x40 /* Legacy audio control */ +#define ESM_CONFIG 0x50 /* Solo-1 configuration */ +#define ESM_ACPI_COMMAND 0x54 +#define ESM_DDMA 0x60 /* Distributed DMA control */ +#define ESM_PMSTATUS 0xC4 /* Power-Management control/status */ + +/* MIXER Register (page 51) */ +#define ESM_MIXER_AUDIO1_VOL 0x14 /* Audio 1 play volume */ +#define ESM_MIXER_MICMIX_VOL 0x1A /* Mic mix volume */ +#define ESM_MIXER_EXTENDEDRECSRC 0x1C /* Extended record source */ +#define ESM_MIXER_MASTER_VOL 0x32 /* Master volume */ +#define ESM_MIXER_FM_VOL 0x36 /* FM volume */ +#define ESM_MIXER_AUXA_VOL 0x38 /* AuxA(CD) volume */ +#define ESM_MIXER_AUXB_VOL 0x3A /* AuxB volume */ +#define ESM_MIXER_PCSPKR_VOL 0x3C /* PC Speaker volume */ +#define ESM_MIXER_LINE_VOL 0x3E /* Line volume */ + +#define ESM_MIXER_SERIALMODE_CTL 0x48 /* Serial mode control */ +#define ESM_MIXER_SPATIALIZER_EN 0x50 /* Spatializer enable and mode control */ +#define ESM_MIXER_SPATIALIZER_LV 0x52 /* Spatializer level */ +#define ESM_MIXER_LEFT_MASTER_VOL 0x60 /* Left master volume and mute */ +#define ESM_MIXER_RIGHT_MASTER_VOL 0x62 /* Right master volume and mute */ +#define ESM_MIXER_MASTER_VOL_CTL 0x64 /* Master Volume control */ +#define ESM_MIXER_OPAMP_CALIB 0x65 /* Opamp Calibration Control */ +#define ESM_MIXER_CLRHWVOLIRQ 0x66 /* Clear Hardware Volume Interrupt Request */ +#define ESM_MIXER_MIC_RECVOL 0x68 /* Audio 2 record volume */ +#define ESM_MIXER_AUDIO2_RECVOL 0x69 /* Audio 2 record volume */ +#define ESM_MIXER_AUXA_RECVOL 0x6A /* AuxA record volume */ +#define ESM_MIXER_DAC_RECVOL 0x6B /* Music DAC record volume */ +#define ESM_MIXER_AUXB_RECVOL 0x6C /* AuxB record volume */ +#define ESM_MIXER_MONOIN_PLAYMIX 0x6D /* Mono_In play mix */ +#define ESM_MIXER_LINE_RECVOL 0x6E /* Line record volume */ +#define ESM_MIXER_MONOIN_RECVOL 0x6F /* Mono_In record volume */ +#define ESM_MIXER_AUDIO2_SR 0x70 /* Audio 2 Sample rate */ +#define ESM_MIXER_AUDIO2_MODE 0x71 /* Audio 2 mode */ +#define ESM_MIXER_AUDIO2_CLKRATE 0x72 /* Audio 2 clock rate */ +#define ESM_MIXER_AUDIO2_TCOUNT 0x74 /* Audio 2 transfer count reload */ +#define ESM_MIXER_AUDIO2_CTL1 0x78 /* Audio 2 Control 1 */ +#define ESM_MIXER_AUDIO2_CTL2 0x7A /* Audio 2 Control 2 */ +#define ESM_MIXER_AUDIO2_VOL 0x7C /* Audio 2 DAC mixer volume */ +#define ESM_MIXER_MIC_PREAMP 0x7D /* Mic preamp, Mono_In and Mono_Out */ +#define ESM_MIXER_MDR 0x7F /* Music digital record */ + +/* Values for ESSIO_REG_AUDIO2MODE */ +#define ESSA2M_DIR 0x01 /* Audio 2 DMA Direction. 0 = Memory to DAC. */ +#define ESSA2M_DMAEN 0x02 /* Audio 2 DMA enable */ +#define ESSA2M_BCLKEN 0x04 /* BCLK select */ +#define ESSA2M_AIEN 0x08 /* Auto-Initialize enable for Audio 2 DMA */ + +/* Values for the ESM_LEGACY_AUDIO_CONTROL */ +#define ESS_DISABLE_AUDIO 0x8000 +#define ESS_ENABLE_SERIAL_IRQ 0x4000 +#define IO_ADRESS_ALIAS 0x0020 +#define MPU401_IRQ_ENABLE 0x0010 +#define MPU401_IO_ENABLE 0x0008 +#define GAME_IO_ENABLE 0x0004 +#define FM_IO_ENABLE 0x0002 +#define SB_IO_ENABLE 0x0001 + +/* Values for ESSIO_REG_IRQCONTROL */ +#define MPUIRQ 0x80 +#define HVIRQ 0x40 +#define A2IRQ 0x20 +#define A1IRQ 0x10 +#define ESS_ALLIRQ (MPUIRQ | HVIRQ | A2IRQ | A1IRQ) + +/* Values for ESSDM_REG_DMAMODE */ +#define ESSDM_DMAMODE_TTYPE_VERIFY 0x00 /* Verify transfer */ +#define ESSDM_DMAMODE_TTYPE_WRITE 0x04 /* Write transfer */ +#define ESSDM_DMAMODE_TTYPE_READ 0x08 /* Read transfer */ +#define ESSDM_DMAMODE_AI 0x10 /* Auto Initialize */ +#define ESSDM_DMAMODE_DIR 0x20 /* Transfer direction */ +#define ESSDM_DMAMODE_TMODE_DEMAND 0x00 /* Demand transfer */ +#define ESSDM_DMAMODE_TMODE_SINGLE 0x40 /* Single transfer */ +#define ESSDM_DMAMODE_TMODE_BLOCK 0x80 /* Block transfer */ + +#define ESS_FMBASE 0x388 diff --git a/src/essplaymid/iodriver.h b/src/essplaymid/iodriver.h new file mode 100644 index 0000000..b4c8e38 --- /dev/null +++ b/src/essplaymid/iodriver.h @@ -0,0 +1,9 @@ +#ifdef IODRIVER_BUTTIO +#include "iodriver/drv_buttio.h" +#elif defined(IODRIVER_GIVEIO) +#include "iodriver/drv_giveio.h" +#else +#include "iodriver/drv_inpout32.h" +#endif + +BOOL IODriver_Init(USHORT first, USHORT last); diff --git a/src/essplaymid/iodriver/drv_buttio.c b/src/essplaymid/iodriver/drv_buttio.c new file mode 100644 index 0000000..9228fb4 --- /dev/null +++ b/src/essplaymid/iodriver/drv_buttio.c @@ -0,0 +1,21 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include "drv_buttio.h" + +IOHandler buttioHand = {0}; + + +BOOL IODriver_Init(USHORT first, USHORT last) +{ + if (!buttio_init(&buttioHand, NULL, BUTTIO_MET_IOPM)) + return FALSE; + iopm_fillRange(buttioHand.iopm, first, last, TRUE); + buttio_flushIOPMChanges(&buttioHand); + return TRUE; +} + +void IODriver_Exit(void) +{ + buttio_shutdown(&buttioHand); +} + diff --git a/src/essplaymid/iodriver/drv_buttio.h b/src/essplaymid/iodriver/drv_buttio.h new file mode 100644 index 0000000..85f4c8f --- /dev/null +++ b/src/essplaymid/iodriver/drv_buttio.h @@ -0,0 +1,30 @@ +// Buttio +#include "buttio.h" + +extern IOHandler buttioHand; +__forceinline +VOID +WRITE_PORT_UCHAR ( + USHORT Port, + UCHAR Value + ) + +{ + buttio_wu8(&buttioHand, (Port), (Value)); + return; +} + +__forceinline +UCHAR +READ_PORT_UCHAR ( + USHORT Port + ) + +{ + BYTE b; + buttio_ru8(&buttioHand, (Port), &b); + return b; +} +void IODriver_Exit(void); + +#pragma comment(lib, "buttio.lib") diff --git a/src/essplaymid/iodriver/drv_giveio.c b/src/essplaymid/iodriver/drv_giveio.c new file mode 100644 index 0000000..e1ecaa5 --- /dev/null +++ b/src/essplaymid/iodriver/drv_giveio.c @@ -0,0 +1,14 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include "drv_giveio.h" + +// GiveIO +BOOL IODriver_Init(USHORT first, USHORT last) +{ + HANDLE h; + + h = CreateFile("\\\\.\\giveio", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + CloseHandle(h); + + return h != INVALID_HANDLE_VALUE; +} diff --git a/src/essplaymid/iodriver/drv_giveio.h b/src/essplaymid/iodriver/drv_giveio.h new file mode 100644 index 0000000..7bf0583 --- /dev/null +++ b/src/essplaymid/iodriver/drv_giveio.h @@ -0,0 +1,14 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include + +// GiveIO +#define READ_PORT_UCHAR(Port) _inp ((USHORT)Port) +#define READ_PORT_USHORT(Port) _inpw ((USHORT)Port) +#define READ_PORT_ULONG(Port) _inpd ((USHORT)Port) +#define WRITE_PORT_UCHAR(Port, Value) _outp (((USHORT)Port), (Value)) +#define WRITE_PORT_USHORT(Port, Value) _outpw (((USHORT)Port), (Value)) +#define WRITE_PORT_ULONG(Port, Value) _outpd (((USHORT)Port), (Value)) + +#define IODriver_Exit() + diff --git a/src/essplaymid/iodriver/drv_inpout32.c b/src/essplaymid/iodriver/drv_inpout32.c new file mode 100644 index 0000000..0b4c162 --- /dev/null +++ b/src/essplaymid/iodriver/drv_inpout32.c @@ -0,0 +1,12 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include "drv_inpout32.h" + +BOOL __stdcall IsInpOutDriverOpen(); + +BOOL IODriver_Init(USHORT first, USHORT last) +{ + return IsInpOutDriverOpen(); +} + + diff --git a/src/essplaymid/iodriver/drv_inpout32.h b/src/essplaymid/iodriver/drv_inpout32.h new file mode 100644 index 0000000..bdfe78f --- /dev/null +++ b/src/essplaymid/iodriver/drv_inpout32.h @@ -0,0 +1,16 @@ +// inpout 32 + +VOID __stdcall Out32(USHORT, UCHAR); +UCHAR __stdcall Inp32(USHORT); + +#define WRITE_PORT_UCHAR(Port, Value) Out32((USHORT)Port, Value) +#define READ_PORT_UCHAR(Port) Inp32((USHORT)Port) + +#define IODriver_Exit() + +#ifdef _WIN64 +#pragma comment(lib, "inpoutx64.lib") +#else +#pragma comment(lib, "inpout32.lib") +#endif + diff --git a/src/essplaymid/iodriver/lib/inpout32.lib b/src/essplaymid/iodriver/lib/inpout32.lib new file mode 100644 index 0000000000000000000000000000000000000000..1a3cecb41e8aed00572f533029b62215fe9eec92 GIT binary patch literal 13338 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 U=(vLw5wgPBZ?4N{L9;Xe1J!43VgLXD literal 0 HcmV?d00001 diff --git a/src/essplaymid/synth.h b/src/essplaymid/synth.h new file mode 100644 index 0000000..cf8b9e6 --- /dev/null +++ b/src/essplaymid/synth.h @@ -0,0 +1,72 @@ +/*++ BUILD Version: 0001 // Increment this if a change has global effects + + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + synth.h + +Abstract: + + This include file defines constants and types for + the Microsoft midi synthesizer driver + + This header file is shared between the low level driver and the + kernel mode driver. + +Author: + + Robin Speed (RobinSp) 20-Oct-92 + +Revision History: + +--*/ + +#define STR_DRIVERNAME L"synth" +#define STR_MV_DRIVERNAME L"mvopl3" +#define STR_OPL3_DEVICENAME L"\\Device\\opl3.mid" +#define STR_ADLIB_DEVICENAME L"\\Device\\adlib.mid" + +/* + * Stucture for passing synth data + * Why on earth isn't there a type for sharing short data? + */ + + typedef struct { + unsigned short IoPort; + unsigned short PortData; + } SYNTH_DATA, *PSYNTH_DATA; + +/* positions within FM */ +#define AD_LSI (0x000) +#define AD_LSI2 (0x101) +#define AD_TIMER1 (0x001) +#define AD_TIMER2 (0x002) +#define AD_MASK (0x004) +#define AD_CONNECTION (0x104) +#define AD_NEW (0x105) +#define AD_NTS (0x008) +#define AD_MULT (0x020) +#define AD_MULT2 (0x120) +#define AD_LEVEL (0x040) +#define AD_LEVEL2 (0x140) +#define AD_AD (0x060) +#define AD_AD2 (0x160) +#define AD_SR (0x080) +#define AD_SR2 (0x180) +#define AD_FNUMBER (0x0a0) +#define AD_FNUMBER2 (0x1a0) +#define AD_BLOCK (0x0b0) +#define AD_BLOCK2 (0x1b0) +#define AD_DRUM (0x0bd) +#define AD_FEEDBACK (0x0c0) +#define AD_FEEDBACK2 (0x1c0) +#define AD_WAVE (0x0e0) +#define AD_WAVE2 (0x1e0) + +/* +** Special IOCTL +*/ + +#define IOCTL_MIDI_SET_OPL3_MODE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x000A, METHOD_BUFFERED, FILE_WRITE_ACCESS) \ No newline at end of file diff --git a/src/ins_names.h b/src/ins_names.h new file mode 100644 index 0000000..a5b19a6 --- /dev/null +++ b/src/ins_names.h @@ -0,0 +1,43 @@ +/* + * OPL Bank Editor by Wohlstand, a free tool for music bank editing + * Copyright (c) 2016-2023 Vitaly Novichkov + * + * OPN2 Bank Editor by Wohlstand, a free tool for music bank editing + * Copyright (c) 2017-2023 Vitaly Novichkov + * + * 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 3 of the License, or + * 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, see . + */ + +#ifndef INSTRUMENTNAMES_H +#define INSTRUMENTNAMES_H + +#pragma pack(push, 1) +struct MidiProgram +{ + //! Kind of instrument. 'M' melodic 'P' percussive + char kind; + //! Bank identifier MSB + unsigned char bankMsb; + //! Bank identifier LSB. (if percussive, this is the program number) + unsigned char bankLsb; + //! Program number (if percussive, this is the key number) + unsigned char program; + //! Bank name + const char *bankName; + //! Patch name + const char *patchName; +}; +#pragma pack(pop) + +#endif // INSTRUMENTNAMES_H diff --git a/src/ins_names_data.h b/src/ins_names_data.h new file mode 100644 index 0000000..84f50ee --- /dev/null +++ b/src/ins_names_data.h @@ -0,0 +1,212 @@ +/* + * OPN2 Bank Editor by Wohlstand, a free tool for music bank editing + * Copyright (c) 2017-2023 Vitaly Novichkov + * + * 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 3 of the License, or + * 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, see . + */ + +#include "ins_names.h" + +static const struct MidiProgram Gm1Set[] = +{ + {'M', 0, 0, 0, "GM1 Melodic", "Acoustic Grand Piano"}, + {'M', 0, 0, 1, "GM1 Melodic", "Bright Acoustic Piano"}, + {'M', 0, 0, 2, "GM1 Melodic", "Electric Grand Piano"}, + {'M', 0, 0, 3, "GM1 Melodic", "Honky-tonk Piano"}, + {'M', 0, 0, 4, "GM1 Melodic", "Electric Piano 1"}, + {'M', 0, 0, 5, "GM1 Melodic", "Electric Piano 2"}, + {'M', 0, 0, 6, "GM1 Melodic", "Harpsichord"}, + {'M', 0, 0, 7, "GM1 Melodic", "Clavi"}, + {'M', 0, 0, 8, "GM1 Melodic", "Celesta"}, + {'M', 0, 0, 9, "GM1 Melodic", "Glockenspiel"}, + {'M', 0, 0, 10, "GM1 Melodic", "Music Box"}, + {'M', 0, 0, 11, "GM1 Melodic", "Vibraphone"}, + {'M', 0, 0, 12, "GM1 Melodic", "Marimba"}, + {'M', 0, 0, 13, "GM1 Melodic", "Xylophone"}, + {'M', 0, 0, 14, "GM1 Melodic", "Tubular Bells"}, + {'M', 0, 0, 15, "GM1 Melodic", "Dulcimer"}, + {'M', 0, 0, 16, "GM1 Melodic", "Drawbar Organ"}, + {'M', 0, 0, 17, "GM1 Melodic", "Percussive Organ"}, + {'M', 0, 0, 18, "GM1 Melodic", "Rock Organ"}, + {'M', 0, 0, 19, "GM1 Melodic", "Church Organ"}, + {'M', 0, 0, 20, "GM1 Melodic", "Reed Organ"}, + {'M', 0, 0, 21, "GM1 Melodic", "Accordion"}, + {'M', 0, 0, 22, "GM1 Melodic", "Harmonica"}, + {'M', 0, 0, 23, "GM1 Melodic", "Tango Accordion"}, + {'M', 0, 0, 24, "GM1 Melodic", "Acoustic Guitar (nylon)"}, + {'M', 0, 0, 25, "GM1 Melodic", "Acoustic Guitar (steel)"}, + {'M', 0, 0, 26, "GM1 Melodic", "Electric Guitar (jazz)"}, + {'M', 0, 0, 27, "GM1 Melodic", "Electric Guitar (clean)"}, + {'M', 0, 0, 28, "GM1 Melodic", "Electric Guitar (muted)"}, + {'M', 0, 0, 29, "GM1 Melodic", "Overdriven Guitar"}, + {'M', 0, 0, 30, "GM1 Melodic", "Distortion Guitar"}, + {'M', 0, 0, 31, "GM1 Melodic", "Guitar Harmonics"}, + {'M', 0, 0, 32, "GM1 Melodic", "Acoustic Bass"}, + {'M', 0, 0, 33, "GM1 Melodic", "Electric Bass (finger)"}, + {'M', 0, 0, 34, "GM1 Melodic", "Electric Bass (pick)"}, + {'M', 0, 0, 35, "GM1 Melodic", "Fretless Bass"}, + {'M', 0, 0, 36, "GM1 Melodic", "Slap Bass 1"}, + {'M', 0, 0, 37, "GM1 Melodic", "Slap Bass 2"}, + {'M', 0, 0, 38, "GM1 Melodic", "Synth Bass 1"}, + {'M', 0, 0, 39, "GM1 Melodic", "Synth Bass 2"}, + {'M', 0, 0, 40, "GM1 Melodic", "Violin"}, + {'M', 0, 0, 41, "GM1 Melodic", "Viola"}, + {'M', 0, 0, 42, "GM1 Melodic", "Cello"}, + {'M', 0, 0, 43, "GM1 Melodic", "Contrabass"}, + {'M', 0, 0, 44, "GM1 Melodic", "Tremolo Strings"}, + {'M', 0, 0, 45, "GM1 Melodic", "Pizzicato Strings"}, + {'M', 0, 0, 46, "GM1 Melodic", "Orchestral Harp"}, + {'M', 0, 0, 47, "GM1 Melodic", "Timpani"}, + {'M', 0, 0, 48, "GM1 Melodic", "String Ensembles 1"}, + {'M', 0, 0, 49, "GM1 Melodic", "String Ensembles 2"}, + {'M', 0, 0, 50, "GM1 Melodic", "SynthStrings 1"}, + {'M', 0, 0, 51, "GM1 Melodic", "SynthStrings 2"}, + {'M', 0, 0, 52, "GM1 Melodic", "Choir Aahs"}, + {'M', 0, 0, 53, "GM1 Melodic", "Voice Oohs"}, + {'M', 0, 0, 54, "GM1 Melodic", "Synth Voice"}, + {'M', 0, 0, 55, "GM1 Melodic", "Orchestra Hit"}, + {'M', 0, 0, 56, "GM1 Melodic", "Trumpet"}, + {'M', 0, 0, 57, "GM1 Melodic", "Trombone"}, + {'M', 0, 0, 58, "GM1 Melodic", "Tuba"}, + {'M', 0, 0, 59, "GM1 Melodic", "Muted Trumpet"}, + {'M', 0, 0, 60, "GM1 Melodic", "French Horn"}, + {'M', 0, 0, 61, "GM1 Melodic", "Brass Section"}, + {'M', 0, 0, 62, "GM1 Melodic", "Synth Brass 1"}, + {'M', 0, 0, 63, "GM1 Melodic", "Synth Brass 2"}, + {'M', 0, 0, 64, "GM1 Melodic", "Soprano Sax"}, + {'M', 0, 0, 65, "GM1 Melodic", "Alto Sax"}, + {'M', 0, 0, 66, "GM1 Melodic", "Tenor Sax"}, + {'M', 0, 0, 67, "GM1 Melodic", "Baritone Sax"}, + {'M', 0, 0, 68, "GM1 Melodic", "Oboe"}, + {'M', 0, 0, 69, "GM1 Melodic", "English Horn"}, + {'M', 0, 0, 70, "GM1 Melodic", "Bassoon"}, + {'M', 0, 0, 71, "GM1 Melodic", "Clarinet"}, + {'M', 0, 0, 72, "GM1 Melodic", "Piccolo"}, + {'M', 0, 0, 73, "GM1 Melodic", "Flute"}, + {'M', 0, 0, 74, "GM1 Melodic", "Recorder"}, + {'M', 0, 0, 75, "GM1 Melodic", "Pan Flute"}, + {'M', 0, 0, 76, "GM1 Melodic", "Blown Bottle"}, + {'M', 0, 0, 77, "GM1 Melodic", "Shakuhachi"}, + {'M', 0, 0, 78, "GM1 Melodic", "Whistle"}, + {'M', 0, 0, 79, "GM1 Melodic", "Ocarina"}, + {'M', 0, 0, 80, "GM1 Melodic", "Lead 1 (square)"}, + {'M', 0, 0, 81, "GM1 Melodic", "Lead 2 (sawtooth)"}, + {'M', 0, 0, 82, "GM1 Melodic", "Lead 3 (calliope)"}, + {'M', 0, 0, 83, "GM1 Melodic", "Lead 4 (chiff)"}, + {'M', 0, 0, 84, "GM1 Melodic", "Lead 5 (charang)"}, + {'M', 0, 0, 85, "GM1 Melodic", "Lead 6 (voice)"}, + {'M', 0, 0, 86, "GM1 Melodic", "Lead 7 (fifths)"}, + {'M', 0, 0, 87, "GM1 Melodic", "Lead 8 (bass + lead)"}, + {'M', 0, 0, 88, "GM1 Melodic", "Pad 1 (new age)"}, + {'M', 0, 0, 89, "GM1 Melodic", "Pad 2 (warm)"}, + {'M', 0, 0, 90, "GM1 Melodic", "Pad 3 (polysynth)"}, + {'M', 0, 0, 91, "GM1 Melodic", "Pad 4 (choir)"}, + {'M', 0, 0, 92, "GM1 Melodic", "Pad 5 (bowed)"}, + {'M', 0, 0, 93, "GM1 Melodic", "Pad 6 (metallic)"}, + {'M', 0, 0, 94, "GM1 Melodic", "Pad 7 (halo)"}, + {'M', 0, 0, 95, "GM1 Melodic", "Pad 8 (sweep)"}, + {'M', 0, 0, 96, "GM1 Melodic", "FX 1 (rain)"}, + {'M', 0, 0, 97, "GM1 Melodic", "FX 2 (soundtrack)"}, + {'M', 0, 0, 98, "GM1 Melodic", "FX 3 (crystal)"}, + {'M', 0, 0, 99, "GM1 Melodic", "FX 4 (atmosphere)"}, + {'M', 0, 0, 100, "GM1 Melodic", "FX 5 (brightness)"}, + {'M', 0, 0, 101, "GM1 Melodic", "FX 6 (goblins)"}, + {'M', 0, 0, 102, "GM1 Melodic", "FX 7 (echoes)"}, + {'M', 0, 0, 103, "GM1 Melodic", "FX 8 (sci-fi)"}, + {'M', 0, 0, 104, "GM1 Melodic", "Sitar"}, + {'M', 0, 0, 105, "GM1 Melodic", "Banjo"}, + {'M', 0, 0, 106, "GM1 Melodic", "Shamisen"}, + {'M', 0, 0, 107, "GM1 Melodic", "Koto"}, + {'M', 0, 0, 108, "GM1 Melodic", "Kalimba"}, + {'M', 0, 0, 109, "GM1 Melodic", "Bag pipe"}, + {'M', 0, 0, 110, "GM1 Melodic", "Fiddle"}, + {'M', 0, 0, 111, "GM1 Melodic", "Shanai"}, + {'M', 0, 0, 112, "GM1 Melodic", "Tinkle Bell"}, + {'M', 0, 0, 113, "GM1 Melodic", "Agogo"}, + {'M', 0, 0, 114, "GM1 Melodic", "Steel Drums"}, + {'M', 0, 0, 115, "GM1 Melodic", "Woodblock"}, + {'M', 0, 0, 116, "GM1 Melodic", "Taiko Drum"}, + {'M', 0, 0, 117, "GM1 Melodic", "Melodic Tom"}, + {'M', 0, 0, 118, "GM1 Melodic", "Synth Drum"}, + {'M', 0, 0, 119, "GM1 Melodic", "Reverse Cymbal"}, + {'M', 0, 0, 120, "GM1 Melodic", "Guitar Fret Noise"}, + {'M', 0, 0, 121, "GM1 Melodic", "Breath Noise"}, + {'M', 0, 0, 122, "GM1 Melodic", "Seashore"}, + {'M', 0, 0, 123, "GM1 Melodic", "Bird Tweet"}, + {'M', 0, 0, 124, "GM1 Melodic", "Telephone Ring"}, + {'M', 0, 0, 125, "GM1 Melodic", "Helicopter"}, + {'M', 0, 0, 126, "GM1 Melodic", "Applause"}, + {'M', 0, 0, 127, "GM1 Melodic", "Gunshot"}, + {'P', 0, 0, 35, "GM1 Percussion", "Acoustic Bass Drum"}, + {'P', 0, 0, 36, "GM1 Percussion", "Bass Drum 1"}, + {'P', 0, 0, 37, "GM1 Percussion", "Side Stick"}, + {'P', 0, 0, 38, "GM1 Percussion", "Acoustic Snare"}, + {'P', 0, 0, 39, "GM1 Percussion", "Hand Clap"}, + {'P', 0, 0, 40, "GM1 Percussion", "Electric Snare"}, + {'P', 0, 0, 41, "GM1 Percussion", "Low Floor Tom"}, + {'P', 0, 0, 42, "GM1 Percussion", "Closed Hi-hat"}, + {'P', 0, 0, 43, "GM1 Percussion", "High Floor Tom"}, + {'P', 0, 0, 44, "GM1 Percussion", "Pedal Hi-hat"}, + {'P', 0, 0, 45, "GM1 Percussion", "Low Tom"}, + {'P', 0, 0, 46, "GM1 Percussion", "Open Hi-hat"}, + {'P', 0, 0, 47, "GM1 Percussion", "Low-Mid Tom"}, + {'P', 0, 0, 48, "GM1 Percussion", "High Mid Tom"}, + {'P', 0, 0, 49, "GM1 Percussion", "Crash Cymbal 1"}, + {'P', 0, 0, 50, "GM1 Percussion", "High Tom"}, + {'P', 0, 0, 51, "GM1 Percussion", "Ride Cymbal 1"}, + {'P', 0, 0, 52, "GM1 Percussion", "Chinese Cymbal"}, + {'P', 0, 0, 53, "GM1 Percussion", "Ride Bell"}, + {'P', 0, 0, 54, "GM1 Percussion", "Tambourine"}, + {'P', 0, 0, 55, "GM1 Percussion", "Splash Cymbal"}, + {'P', 0, 0, 56, "GM1 Percussion", "Cowbell"}, + {'P', 0, 0, 57, "GM1 Percussion", "Crash Cymbal 2"}, + {'P', 0, 0, 58, "GM1 Percussion", "Vibra-slap"}, + {'P', 0, 0, 59, "GM1 Percussion", "Ride Cymbal 2"}, + {'P', 0, 0, 60, "GM1 Percussion", "High Bongo"}, + {'P', 0, 0, 61, "GM1 Percussion", "Low Bongo"}, + {'P', 0, 0, 62, "GM1 Percussion", "Mute Hi Conga"}, + {'P', 0, 0, 63, "GM1 Percussion", "Open Hi Conga"}, + {'P', 0, 0, 64, "GM1 Percussion", "Low Conga"}, + {'P', 0, 0, 65, "GM1 Percussion", "High Timbale"}, + {'P', 0, 0, 66, "GM1 Percussion", "Low Timbale"}, + {'P', 0, 0, 67, "GM1 Percussion", "High Agogo"}, + {'P', 0, 0, 68, "GM1 Percussion", "Low Agogo"}, + {'P', 0, 0, 69, "GM1 Percussion", "Cabasa"}, + {'P', 0, 0, 70, "GM1 Percussion", "Maracas"}, + {'P', 0, 0, 71, "GM1 Percussion", "Short Whistle"}, + {'P', 0, 0, 72, "GM1 Percussion", "Long Whistle"}, + {'P', 0, 0, 73, "GM1 Percussion", "Short Guiro"}, + {'P', 0, 0, 74, "GM1 Percussion", "Long Guiro"}, + {'P', 0, 0, 75, "GM1 Percussion", "Claves"}, + {'P', 0, 0, 76, "GM1 Percussion", "Hi Wood Block"}, + {'P', 0, 0, 77, "GM1 Percussion", "Low Wood Block"}, + {'P', 0, 0, 78, "GM1 Percussion", "Mute Cuica"}, + {'P', 0, 0, 79, "GM1 Percussion", "Open Cuica"}, + {'P', 0, 0, 80, "GM1 Percussion", "Mute Triangle"}, + {'P', 0, 0, 81, "GM1 Percussion", "Open Triangle"}, + {'P', 0, 0, 27, "GM Percussion Fallback", "High Q"}, + {'P', 0, 0, 28, "GM Percussion Fallback", "Slap"}, + {'P', 0, 0, 29, "GM Percussion Fallback", "Scratch Push"}, + {'P', 0, 0, 30, "GM Percussion Fallback", "Scratch Pull"}, + {'P', 0, 0, 31, "GM Percussion Fallback", "Sticks"}, + {'P', 0, 0, 32, "GM Percussion Fallback", "Square Click"}, + {'P', 0, 0, 33, "GM Percussion Fallback", "Metronome Click"}, + {'P', 0, 0, 34, "GM Percussion Fallback", "Metronome Bell"}, + {'P', 0, 0, 82, "GM Percussion Fallback", "Shaker"}, + {'P', 0, 0, 83, "GM Percussion Fallback", "Jingle Bell"}, + {'P', 0, 0, 84, "GM Percussion Fallback", "Bell Tree"}, + {'P', 0, 0, 85, "GM Percussion Fallback", "Castanets"}, + {'P', 0, 0, 86, "GM Percussion Fallback", "Mute Surdo"}, + {'P', 0, 0, 87, "GM Percussion Fallback", "Open Surdo"}, +}; diff --git a/src/manifest.xml b/src/manifest.xml new file mode 100644 index 0000000..c0c89c7 --- /dev/null +++ b/src/manifest.xml @@ -0,0 +1,15 @@ + + + +elevate execution level + + + + + + + + diff --git a/src/res.rc b/src/res.rc new file mode 100644 index 0000000..25faae3 --- /dev/null +++ b/src/res.rc @@ -0,0 +1,377 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Deutsch (Deutschland) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROPPAGE_OPERATOR DIALOG DISCARDABLE 0, 0, 196, 204 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Attack:",IDC_STATIC,4,5,30,9 + EDITTEXT IDC_ATTACK,35,3,23,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINATTACK,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | + UDS_NOTHOUSANDS,58,5,6,9 + LTEXT "Decay:",IDC_STATIC,72,5,30,9 + EDITTEXT IDC_DECAY,103,3,23,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINDECAY,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | + UDS_NOTHOUSANDS,126,5,11,9 + LTEXT "Sustain:",IDC_STATIC,4,19,30,9 + EDITTEXT IDC_SUSTAIN,36,17,23,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINSUSTAIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS | UDS_NOTHOUSANDS,58,19,11,9 + LTEXT "Release:",IDC_STATIC,72,19,30,9 + EDITTEXT IDC_RELEASE,104,17,23,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINRELEASE,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS | UDS_NOTHOUSANDS,126,19,11,9 + LTEXT "Waveform select:",IDC_STATIC,4,34,61,9 + COMBOBOX IDC_WAVEFORM,65,32,128,92,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Tremolo (AM)",IDC_TREMOLO,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,51,57,10 + CONTROL "Vibrato (VIB)",IDC_VIBRATO,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,64,56,9 + CONTROL "Tremolo depth",IDC_TREMOLOD,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,78,51,59,10 + CONTROL "Vibrato depth",IDC_VIBRATOD,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,78,64,56,9 + GROUPBOX "Spkr chan",IDC_STATIC,144,46,50,27 + CONTROL "L",IDC_L,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,59, + 19,9 + CONTROL "R",IDC_R,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,172,59, + 17,9 + CONTROL "Sustaining voice (EG)",IDC_EG,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,76,82,9 + CONTROL "Envelope scale (KSR)",IDC_KSR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,110,76,82,9 + LTEXT "Key scale level:",IDC_STATIC,4,92,55,9 + LTEXT "Attenuation level:",IDC_STATIC,126,85,36,16 + EDITTEXT IDC_ATTENUATION,169,90,23,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINATTENUATION,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,189,92,6,9 + LTEXT "Frequency\nmultiplication:",IDC_STATIC,4,115,54,18 + EDITTEXT IDC_MUL,65,118,23,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINMUL,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | + UDS_NOTHOUSANDS,87,120,11,9 + LTEXT "Output level:",IDC_STATIC,4,105,55,9 + COMBOBOX IDC_OUT,65,104,45,90,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Noise:",IDC_STATIC,4,134,55,9 + COMBOBOX IDC_NOISE,65,131,91,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Delay:",IDC_STATIC,4,148,55,9 + COMBOBOX IDC_DELAY,65,145,119,89,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Frequency number:",IDC_STATIC,4,162,61,9 + EDITTEXT IDC_FNUM,65,160,33,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINFNUM,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | + UDS_NOTHOUSANDS,99,162,11,9 + LTEXT "Block:",IDC_STATIC,135,162,24,9 + EDITTEXT IDC_BLOCK,161,160,23,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINBLOCK,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | + UDS_NOTHOUSANDS,183,163,11,9 + LTEXT "Freq:",IDC_STATIC,105,120,19,9 + LTEXT "0 kHz",IDC_KHZ,125,120,66,10,SS_SUNKEN + CONTROL "Fixed pitch",IDC_FP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,140,5,48,8 + LTEXT "Rel. velocity:",IDC_STATIC,111,106,42,9 + COMBOBOX IDC_RELVEL,154,104,40,90,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Modulator input\n(Feedback):",IDC_STATIC,4,170,55,16 + COMBOBOX IDC_MOD,65,174,45,97,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "by previous op",IDC_STATIC,114,176,55,9 + COMBOBOX IDC_KSL,65,90,58,70,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "Apply",IDOK,46,190,44,12 + PUSHBUTTON "Reset",IDCANCEL,106,190,44,12 +END + +IDD_PROPPAGE_VOICES DIALOG DISCARDABLE 0, 0, 211, 236 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Tab1",IDC_TABCHANNELS,"SysTabControl32",0x0,0,11,210, + 225 + CONTROL "Use channel 16+17",IDC_PAT16,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,0,1,99,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_PROPPAGE_OPERATOR, DIALOG + BEGIN + RIGHTMARGIN, 194 + BOTTOMMARGIN, 202 + END +END +#endif // APSTUDIO_INVOKED + +#endif // Deutsch (Deutschland) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Deutsch (sterreich) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEA) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOG DISCARDABLE 0, 0, 466, 298 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "ESFM Bank editor" +MENU IDR_MAINMENU +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Tab1",IDC_TABVOICE,"SysTabControl32",0x0,114,31,219,258 + CONTROL "Second voice",IDC_2NDVOICE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,116,20,57,9 + CONTROL "Steal second voice if not found",IDC_STEAL,"Button", + BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,182,20,114,9 + GROUPBOX "Instrument",IDC_STATIC,7,7,100,284 + CONTROL "Melodic",IDC_MELODIC,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,9,19,40,10 + CONTROL "Percussion",IDC_PERCUSSION,"Button",BS_AUTORADIOBUTTON, + 53,19,48,9 + LISTBOX IDC_INSTRUMENTS,9,33,95,256,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + GROUPBOX "Soundcard connection",IDC_STATIC,346,7,113,63 + COMBOBOX IDC_DEVICE,350,18,104,64,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "SB base port:",IDC_STATIC,350,35,49,9 + EDITTEXT IDC_SBBASE,405,35,25,12,ES_AUTOHSCROLL + CONTROL "Connect",IDC_CONNECTDEV,"Button",BS_AUTOCHECKBOX | + BS_PUSHLIKE | WS_TABSTOP,378,52,53,13 + GROUPBOX "MIDI input",IDC_STATIC,346,71,113,50 + COMBOBOX IDC_MIDIDEV,350,82,103,96,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Connect",IDC_CONNECTMIDI,"Button",BS_AUTOCHECKBOX | + BS_PUSHLIKE | WS_TABSTOP,377,100,53,13 + GROUPBOX "Testing",IDC_STATIC,345,123,114,158 + LTEXT "Test note #(0...127)",IDC_STATIC,352,134,64,8 + EDITTEXT IDC_NOTE,417,132,30,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPINNOTE,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,442,135, + 11,9 + PUSHBUTTON "Play note",IDC_PLAY,353,146,100,13,WS_DISABLED + PUSHBUTTON "Shut up!",IDC_SHUT,353,257,100,13,WS_DISABLED + PUSHBUTTON "Major chord",IDC_PLAYMJCHORD,353,162,100,13,WS_DISABLED + PUSHBUTTON "Minor chord",IDC_PLAYMNCHORD,353,177,100,13,WS_DISABLED + PUSHBUTTON "Augmented chord",IDC_PLAYAUGCHORD,353,193,100,13, + WS_DISABLED + PUSHBUTTON "Dminished chord",IDC_PLAYDIMCHORD,353,209,100,13, + WS_DISABLED + PUSHBUTTON "Major 7-chord",IDC_PLAYMJ7CHORD,353,225,100,13, + WS_DISABLED + PUSHBUTTON "Minor 7-chord",IDC_PLAYMN7CHORD,353,241,100,13, + WS_DISABLED +END + +IDD_SPLASH DIALOG DISCARDABLE 0, 0, 186, 94 +STYLE DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "ESFM Bank Editor",IDC_TITLE,7,13,172,24 + CTEXT "(c) leecher@dose.0wnz.at 2023",IDC_STATIC,7,42,172,12 + CTEXT "Loading....",IDC_STATIC,7,76,172,11 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 459 + TOPMARGIN, 7 + BOTTOMMARGIN, 291 + END + + IDD_SPLASH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 87 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINMENU MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Open...", IDM_OPEN + MENUITEM "&Save", IDM_SAVE, GRAYED + MENUITEM "Save &as...", IDM_SAVEAS + MENUITEM SEPARATOR + MENUITEM "E&xit", IDM_EXIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// 24 +// + +1 24 DISCARDABLE "manifest.xml" + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0704b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "leecher1337\0" + VALUE "FileDescription", "esfmbank\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "esfmbank\0" + VALUE "LegalCopyright", "Copyright 2023 leecher@dose.0wnz.at\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "esfmbank.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ESFM bank editor\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc07, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDM_OPEN "Open a file with an ESFM bank defintion" + IDM_SAVE "Saves currently opened ESFM bank file" + IDM_SAVEAS "Save currently edited ESFM bank to a new file" + IDM_EXIT "Exits the application" +END + +#endif // Deutsch (sterreich) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/resource.h b/src/resource.h new file mode 100644 index 0000000..d81a06f --- /dev/null +++ b/src/resource.h @@ -0,0 +1,82 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by res.rc +// +#define IDD_MAIN 101 +#define IDD_PROPPAGE_OPERATOR 102 +#define IDD_SPLASH 103 +#define IDR_MAINMENU 104 +#define IDR_241 106 +#define IDD_PROPPAGE_VOICES 107 +#define IDC_TABVOICE 1000 +#define IDC_ATTACK 1005 +#define IDC_SPINATTACK 1006 +#define IDC_DECAY 1007 +#define IDC_SPINDECAY 1008 +#define IDC_SUSTAIN 1009 +#define IDC_SPINSUSTAIN 1010 +#define IDC_RELEASE 1011 +#define IDC_SPINRELEASE 1012 +#define IDC_WAVEFORM 1013 +#define IDC_TREMOLO 1014 +#define IDC_VIBRATO 1015 +#define IDC_TREMOLOD 1016 +#define IDC_VIBRATOD 1017 +#define IDC_L 1018 +#define IDC_R 1019 +#define IDC_EG 1020 +#define IDC_KSR 1021 +#define IDC_ATTENUATION 1024 +#define IDC_SPINATTENUATION 1025 +#define IDC_MUL 1026 +#define IDC_SPINMUL 1027 +#define IDC_OUT 1028 +#define IDC_NOISE 1029 +#define IDC_DELAY 1030 +#define IDC_FNUM 1031 +#define IDC_SPINFNUM 1032 +#define IDC_BLOCK 1033 +#define IDC_SPINBLOCK 1034 +#define IDC_KHZ 1035 +#define IDC_RELVEL 1036 +#define IDC_FP 1037 +#define IDC_MOD 1038 +#define IDC_PAT16 1038 +#define IDC_KSL 1039 +#define IDC_2NDVOICE 1039 +#define IDC_STEAL 1040 +#define IDC_TABCHANNELS 1041 +#define IDC_MELODIC 1044 +#define IDC_PERCUSSION 1045 +#define IDC_INSTRUMENTS 1048 +#define IDC_TITLE 1050 +#define IDC_DEVICE 1051 +#define IDC_SBBASE 1053 +#define IDC_CONNECTDEV 1055 +#define IDC_MIDIDEV 1056 +#define IDC_CONNECTMIDI 1057 +#define IDC_PLAY 1058 +#define IDC_NOTE 1059 +#define IDC_SPINNOTE 1060 +#define IDC_SHUT 1061 +#define IDC_PLAYMJCHORD 1062 +#define IDC_PLAYMNCHORD 1063 +#define IDC_PLAYAUGCHORD 1064 +#define IDC_PLAYDIMCHORD 1065 +#define IDC_PLAYMJ7CHORD 1066 +#define IDC_PLAYMN7CHORD 1067 +#define IDM_OPEN 40001 +#define IDM_SAVE 40002 +#define IDM_SAVEAS 40003 +#define IDM_EXIT 40004 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 107 +#define _APS_NEXT_COMMAND_VALUE 40005 +#define _APS_NEXT_CONTROL_VALUE 1061 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif