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 0000000..99c9b52 Binary files /dev/null and b/bin/x64/bnk_common.bin differ diff --git a/bin/x64/inpoutx64.dll b/bin/x64/inpoutx64.dll new file mode 100644 index 0000000..23d830b Binary files /dev/null and b/bin/x64/inpoutx64.dll differ diff --git a/bin/x86/bnk_common.bin b/bin/x86/bnk_common.bin new file mode 100644 index 0000000..99c9b52 Binary files /dev/null and b/bin/x86/bnk_common.bin differ diff --git a/bin/x86/inpout32.dll b/bin/x86/inpout32.dll new file mode 100644 index 0000000..567bd08 Binary files /dev/null and b/bin/x86/inpout32.dll differ 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 0000000..1a3cecb Binary files /dev/null and b/src/essplaymid/iodriver/lib/inpout32.lib differ diff --git a/src/essplaymid/iodriver/lib/inpoutx64.lib b/src/essplaymid/iodriver/lib/inpoutx64.lib new file mode 100644 index 0000000..8fd65df Binary files /dev/null and b/src/essplaymid/iodriver/lib/inpoutx64.lib differ 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