From: <sag...@us...> - 2011-07-30 22:14:12
|
Revision: 947 http://modplug.svn.sourceforge.net/modplug/?rev=947&view=rev Author: saga-games Date: 2011-07-30 22:14:04 +0000 (Sat, 30 Jul 2011) Log Message: ----------- [New/Fix/Imp/Mod/etc] Completely overhauled macro handling system. Hopefully 99% compatible with IT's MIDI macros as defined in MIDI.TXT. Some macro letters have been changed / added, see the "macro help" button in the macro settings. Macros can now also make use of volume / panning envelopes, however this does not work very well yet (neither with Zxx, nor with \xx) - big TODO! Handling of those things is expected to change in the future, so don't rely on the buggy behaviour. [Mod] OpenMPT: Version is now 1.20.00.00 Modified Paths: -------------- trunk/OpenMPT/mptrack/Moddoc.cpp trunk/OpenMPT/mptrack/Moddoc.h trunk/OpenMPT/mptrack/TrackerSettings.cpp trunk/OpenMPT/mptrack/dlg_misc.cpp trunk/OpenMPT/mptrack/dlg_misc.h trunk/OpenMPT/mptrack/mptrack.rc trunk/OpenMPT/mptrack/version.h trunk/OpenMPT/soundlib/Snd_defs.h trunk/OpenMPT/soundlib/Snd_fx.cpp trunk/OpenMPT/soundlib/Sndfile.cpp trunk/OpenMPT/soundlib/Sndfile.h trunk/OpenMPT/soundlib/Sndmix.cpp Modified: trunk/OpenMPT/mptrack/Moddoc.cpp =================================================================== --- trunk/OpenMPT/mptrack/Moddoc.cpp 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/mptrack/Moddoc.cpp 2011-07-30 22:14:04 UTC (rev 947) @@ -3227,13 +3227,40 @@ if (value.Compare("F0F001z")==0) return sfx_reso; if (value.Compare("F0F002z")==0) return sfx_mode; if (value.Compare("F0F003z")==0) return sfx_drywet; - if (value.Compare("BK00z")>=0 && value.Compare("BKFFz")<=0 && value.GetLength()==5) + if (value.Compare("Bc00z")>=0 && value.Compare("BcFFz")<=0 && value.GetLength()==5) return sfx_cc; if (value.Compare("F0F079z")>0 && value.Compare("F0F1G")<0 && value.GetLength()==7) return sfx_plug; return sfx_custom; //custom/unknown } + +CString CModDoc::GetMacroName(enmParameteredMacroType macro) +//---------------------------------------------------------- +{ + switch(macro) + { + case sfx_unused: + return _T("Unused"); + case sfx_cutoff: + return _T("Set Filter Cutoff"); + case sfx_reso: + return _T("Set Filter Resonance"); + case sfx_mode: + return _T("Set Filter Mode"); + case sfx_drywet: + return _T("Set Plugin Dry/Wet Ratio"); + case sfx_plug: + return _T("Control Plugin Param..."); + case sfx_cc: + return _T("MIDI CC..."); + case sfx_custom: + default: + return _T("Custom"); + } +} + + int CModDoc::MacroToPlugParam(CString macro) //------------------------------------------ { @@ -3291,14 +3318,14 @@ // Retrieve Zxx (Z80-ZFF) type from current macro configuration -enmFixedMacroType CModDoc::GetZxxType(const CHAR (&szMidiZXXExt)[128][MACRO_LENGTH]) +enmFixedMacroType CModDoc::GetZxxType(const char (&szMidiZXXExt)[128][MACRO_LENGTH]) //---------------------------------------------------------------------------------- { // Compare with all possible preset patterns for(size_t i = 1; i < zxx_max; i++) { // Prepare pattern to compare - CHAR szPatterns[128][MACRO_LENGTH]; + char szPatterns[128][MACRO_LENGTH]; CreateZxxFromType(szPatterns, static_cast<enmFixedMacroType>(i)); bool bFound = true; @@ -3317,7 +3344,7 @@ // Create Zxx (Z80 - ZFF) from one out of five presets -void CModDoc::CreateZxxFromType(CHAR (&szMidiZXXExt)[128][MACRO_LENGTH], enmFixedMacroType iZxxType) +void CModDoc::CreateZxxFromType(char (&szMidiZXXExt)[128][MACRO_LENGTH], enmFixedMacroType iZxxType) //-------------------------------------------------------------------------------------------------- { for(size_t i = 0; i < 128; i++) @@ -3759,7 +3786,7 @@ } //set new macro - CHAR *pMacroToSet = GetSoundFile()->m_MidiCfg.szMidiSFXExt[macroToSet]; + char *pMacroToSet = GetSoundFile()->m_MidiCfg.szMidiSFXExt[macroToSet]; if (paramToUse < 128) { wsprintf(pMacroToSet, "F0F0%Xz",paramToUse+128); Modified: trunk/OpenMPT/mptrack/Moddoc.h =================================================================== --- trunk/OpenMPT/mptrack/Moddoc.h 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/mptrack/Moddoc.h 2011-07-30 22:14:04 UTC (rev 947) @@ -256,10 +256,11 @@ // Various MIDI Macro helpers static enmParameteredMacroType GetMacroType(CString value); //rewbs.xinfo + static CString GetMacroName(enmParameteredMacroType macro); static int MacroToPlugParam(CString value); //rewbs.xinfo static int MacroToMidiCC(CString value); - static enmFixedMacroType GetZxxType(const CHAR (&szMidiZXXExt)[128][MACRO_LENGTH]); - static void CreateZxxFromType(CHAR (&szMidiZXXExt)[128][MACRO_LENGTH], enmFixedMacroType iZxxType); + static enmFixedMacroType GetZxxType(const char (&szMidiZXXExt)[128][MACRO_LENGTH]); + static void CreateZxxFromType(char (&szMidiZXXExt)[128][MACRO_LENGTH], enmFixedMacroType iZxxType); bool IsMacroDefaultSetupUsed() const; int FindMacroForParam(long param) const; Modified: trunk/OpenMPT/mptrack/TrackerSettings.cpp =================================================================== --- trunk/OpenMPT/mptrack/TrackerSettings.cpp 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/mptrack/TrackerSettings.cpp 2011-07-30 22:14:04 UTC (rev 947) @@ -211,6 +211,11 @@ GetPrivateProfileString("Zxx Macros", snam, macros.szMidiZXXExt[izxx], macros.szMidiZXXExt[izxx], CountOf(macros.szMidiZXXExt[izxx]), iniFile); SetNullTerminator(macros.szMidiZXXExt[izxx]); } + // Fix old nasty broken (non-standard) MIDI configs in INI file. + if(storedVersion >= "1.17" && storedVersion < "1.20") + { + CSoundFile::FixMIDIConfigStrings(macros); + } theApp.SetDefaultMidiMacro(¯os); // Default directory location @@ -435,7 +440,7 @@ #define SETTINGS_REGKEY_BASE "Software\\Olivier Lapicque\\" -#define SETTINGS_REGKEY_DEFAULT "ModPlug Tracker" +#define SETTINGS_REGKEY_DEFAULT "ModPlug Tracker" #define SETTINGS_REGEXT_WINDOW "\\Window" #define SETTINGS_REGEXT_SETTINGS "\\Settings" Modified: trunk/OpenMPT/mptrack/dlg_misc.cpp =================================================================== --- trunk/OpenMPT/mptrack/dlg_misc.cpp 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/mptrack/dlg_misc.cpp 2011-07-30 22:14:04 UTC (rev 947) @@ -680,6 +680,7 @@ ON_COMMAND(IDC_CHECK1, OnEmbedMidiCfg) ON_COMMAND(IDC_BUTTON1, OnSetAsDefault) ON_COMMAND(IDC_BUTTON2, OnResetCfg) + ON_COMMAND(IDC_BUTTON3, OnMacroHelp) ON_CBN_SELCHANGE(IDC_COMBO1, OnSFxChanged) ON_CBN_SELCHANGE(IDC_COMBO2, OnSFxPresetChanged) ON_CBN_SELCHANGE(IDC_COMBO3, OnZxxPresetChanged) @@ -916,6 +917,26 @@ } +void CMidiMacroSetup::OnMacroHelp() +//--------------------------------- +{ + MessageBox(_T("Valid characters in macros:\n\n" + "0-9, A-F - Raw hex data (4-Bit value)\n" + "c - MIDI channel (4-Bit value)\n" + "n - Note value\n\n" + "v - Note velocity\n" + "u - Computed note volume (including envelopes)\n\n" + "x - Note panning\n" + "y - Computed panning (including envelopes)\n\n" + "a - High byte of bank select\n" + "b - Low byte of bank select\n" + "p - Program select\n\n" + "z - Zxx parameter (00-7F)\n\n" + "Macros can be up to 31 characters long and contain multiple MIDI messages. SysEx messages are automatically terminated if not specified by the user."), + _T("OpenMPT MIDI Macro quick reference"), MB_OK | MB_ICONINFORMATION); +} + + void CMidiMacroSetup::OnEmbedMidiCfg() //------------------------------------ { @@ -946,7 +967,7 @@ if (sfx < 16) { - CHAR *pmacro = m_MidiCfg.szMidiSFXExt[sfx]; + char *pmacro = m_MidiCfg.szMidiSFXExt[sfx]; switch(sfx_preset) { case sfx_unused: strcpy(pmacro, ""); break; // unused @@ -954,7 +975,7 @@ case sfx_reso: strcpy(pmacro, "F0F001z"); break; // reso case sfx_mode: strcpy(pmacro, "F0F002z"); break; // mode case sfx_drywet: strcpy(pmacro, "F0F003z"); break; - case sfx_cc: strcpy(pmacro, "BK00z"); break; // MIDI cc - TODO: get value from other menus + case sfx_cc: strcpy(pmacro, "Bc00z"); break; // MIDI cc - TODO: get value from other menus case sfx_plug: strcpy(pmacro, "F0F080z"); break; // plug param - TODO: get value from other menus case sfx_custom: /*strcpy(pmacro, "z");*/ break; // custom - leave as is. } @@ -983,28 +1004,20 @@ UINT sfx = m_CbnSFx.GetCurSel(); if (sfx < 16) { - memset(s, 0, sizeof(s)); - m_EditSFx.GetWindowText(s, MACRO_LENGTH - 1); - s[MACRO_LENGTH - 1] = 0; - // Fix letter case - for(size_t i = 0; i < MACRO_LENGTH; i++) + if(ValidateMacroString(m_EditSFx, m_MidiCfg.szMidiSFXExt[sfx], true)) { - if(s[i] >= 'd' && s[i] <= 'f') // a,b,c have special meanings - { - s[i] = s[i] - 'a' + 'A'; - } else if(s[i] == 'N' || s[i] == 'V' || s[i] == 'U' || s[i] == 'X' || s[i] == 'Y' || s[i] == 'Z' || s[i] == 'P') - { - s[i] = s[i] - 'A' + 'a'; - } + MemsetZero(s); + m_EditSFx.GetWindowText(s, MACRO_LENGTH); + SetNullTerminator(s); + memcpy(m_MidiCfg.szMidiSFXExt[sfx], s, MACRO_LENGTH); + + int sfx_preset = m_pModDoc->GetMacroType(m_MidiCfg.szMidiSFXExt[sfx]); + //int param = m_pModDoc->MacroToPlugParam(m_MidiCfg.szMidiSFXExt[sfx]); + + m_CbnSFxPreset.SetCurSel(sfx_preset); + ToggleBoxes(sfx_preset, sfx); + UpdateMacroList(sfx); } - - memcpy(m_MidiCfg.szMidiSFXExt[sfx], s, MACRO_LENGTH); - int sfx_preset = m_pModDoc->GetMacroType(m_MidiCfg.szMidiSFXExt[sfx]); - //int param = m_pModDoc->MacroToPlugParam(m_MidiCfg.szMidiSFXExt[sfx]); - - m_CbnSFxPreset.SetCurSel(sfx_preset); - ToggleBoxes(sfx_preset, sfx); - UpdateMacroList(sfx); } } @@ -1016,10 +1029,13 @@ UINT zxx = m_CbnZxx.GetCurSel(); if (zxx < 128) { - memset(s, 0, sizeof(s)); - m_EditZxx.GetWindowText(s, MACRO_LENGTH - 1); - s[MACRO_LENGTH - 1] = 0; - memcpy(m_MidiCfg.szMidiZXXExt[zxx], s, MACRO_LENGTH); + if(ValidateMacroString(m_EditZxx, m_MidiCfg.szMidiZXXExt[zxx], false)) + { + MemsetZero(s); + m_EditZxx.GetWindowText(s, MACRO_LENGTH); + SetNullTerminator(s); + memcpy(m_MidiCfg.szMidiZXXExt[zxx], s, MACRO_LENGTH); + } } } @@ -1116,7 +1132,7 @@ { CString macroText; UINT cc = m_CbnMacroCC.GetItemData(m_CbnMacroCC.GetCurSel()); - macroText.Format("BK%02Xz", cc&0xFF); + macroText.Format("Bc%02Xz", cc & 0xFF); m_EditSFx.SetWindowText(macroText); } @@ -1157,6 +1173,62 @@ } +bool CMidiMacroSetup::ValidateMacroString(CEdit &wnd, char *lastMacro, bool isParametric) +//--------------------------------------------------------------------------------------- +{ + CString macroStr; + wnd.GetWindowText(macroStr); + + bool allowed = true, caseChange = false; + for(int i = 0; i < macroStr.GetLength(); i++) + { + char c = macroStr.GetAt(i); + if(c == 'k' || c == 'K') // Previously, 'K' was used for MIDI channel + { + caseChange = true; + macroStr.SetAt(i, 'c'); + } else if (c >= 'd' && c <= 'f') // abc have special meanings, but def can be fixed + { + caseChange = true; + macroStr.SetAt(i, c - 'a' + 'A'); + } else if(c == 'N' || c == 'V' || c == 'U' || c == 'X' || c == 'Y' || c == 'Z' || c == 'P') + { + caseChange = true; + macroStr.SetAt(i, c - 'A' + 'a'); + } else if(!( + (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'c') || + (c == 'v' || c == 'u' || c == 'x' || c == 'y' || c == 'p' || c == 'n' || c == ' ') || + (c == 'z' && isParametric))) + { + allowed = false; + break; + } + } + + if(!allowed) + { + // Replace text and keep cursor position if we just typed in an invalid character + int start, end; + wnd.GetSel(start, end); + wnd.SetWindowText(lastMacro); + wnd.SetSel(start - 1, end - 1, true); + MessageBeep(MB_OK); + return false; + } + else + { + if(caseChange) + { + // Replace text and keep cursor position if there was a case conversion + int start, end; + wnd.GetSel(start, end); + wnd.SetWindowText(macroStr); + wnd.SetSel(start, end, true); + } + return true; + } +} + //////////////////////////////////////////////////////////////////////////////////////////// // Keyboard Control Modified: trunk/OpenMPT/mptrack/dlg_misc.h =================================================================== --- trunk/OpenMPT/mptrack/dlg_misc.h 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/mptrack/dlg_misc.h 2011-07-30 22:14:04 UTC (rev 947) @@ -152,6 +152,8 @@ CSoundFile *m_pSndFile; CModDoc *m_pModDoc; + bool ValidateMacroString(CEdit &wnd, char *lastMacro, bool isParametric); + void UpdateMacroList(int macro=-1); void ToggleBoxes(UINT preset, UINT sfx); virtual BOOL OnInitDialog(); @@ -159,6 +161,7 @@ afx_msg void UpdateDialog(); afx_msg void OnSetAsDefault(); afx_msg void OnResetCfg(); + afx_msg void OnMacroHelp(); afx_msg void OnEmbedMidiCfg(); afx_msg void OnSFxChanged(); afx_msg void OnSFxPresetChanged(); Modified: trunk/OpenMPT/mptrack/mptrack.rc =================================================================== --- trunk/OpenMPT/mptrack/mptrack.rc 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/mptrack/mptrack.rc 2011-07-30 22:14:04 UTC (rev 947) @@ -299,12 +299,12 @@ END #endif // APSTUDIO_INVOKED -#endif // German (Germany) resources +#endif // Deutsch (Deutschland) resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources +// Englisch (USA) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 @@ -1228,6 +1228,7 @@ CONTROL "Embed macro configuration in file when saving",IDC_CHECK1, "Button",BS_AUTOCHECKBOX | BS_TOP | WS_TABSTOP,6,338,288,10 COMBOBOX IDC_MACROCC,63,257,111,111,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Macro Help",IDC_BUTTON3,300,84,50,14 END IDD_CHORDEDIT DIALOGEX 0, 0, 245, 148 @@ -2524,12 +2525,12 @@ IDC_SAMPLE_XFADE "Crossfade Loop Points\nCrossfade between loop start and loop end to create seamless sample loops." END -#endif // English (U.S.) resources +#endif // Englisch (USA) resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// -// English (U.K.) resources +// Englisch (GB) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) #ifdef _WIN32 @@ -2902,7 +2903,7 @@ // IDR_BUILTIN_TUNINGS TUNING "res\\built-inTunings.tc" -#endif // English (U.K.) resources +#endif // Englisch (GB) resources ///////////////////////////////////////////////////////////////////////////// Modified: trunk/OpenMPT/mptrack/version.h =================================================================== --- trunk/OpenMPT/mptrack/version.h 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/mptrack/version.h 2011-07-30 22:14:04 UTC (rev 947) @@ -13,8 +13,8 @@ //Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 -#define VER_MAJOR 19 -#define VER_MINOR 03 +#define VER_MAJOR 20 +#define VER_MINOR 00 #define VER_MINORMINOR 00 //Creates version number from version parts that appears in version string. Modified: trunk/OpenMPT/soundlib/Snd_defs.h =================================================================== --- trunk/OpenMPT/soundlib/Snd_defs.h 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/soundlib/Snd_defs.h 2011-07-30 22:14:04 UTC (rev 947) @@ -403,10 +403,6 @@ INST_NUMFILTERMODES };*/ -// MIDI Macros -#define MACRO_MASK 0x7F5F7F5F -#define MACRO_INTERNAL 0x30463046 // internal macro (F0F0), controls lower 8 bits (f.e. cutoff, resonance, low plugin params) -#define MACRO_INTERNALEX 0x31463046 // internal extended macro (F0F1), controls higher 8 bits (high plugin params) // Vibrato Types enum VibratoType Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp =================================================================== --- trunk/OpenMPT/soundlib/Snd_fx.cpp 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2011-07-30 22:14:04 UTC (rev 947) @@ -457,7 +457,14 @@ for (CHANNELINDEX n = 0; n < m_nChannels; n++) { Chn[n].nGlobalVol = chnvols[n]; - if (notes[n]) Chn[n].nNewNote = notes[n]; + if (notes[n]) + { + Chn[n].nNewNote = notes[n]; + if(NOTE_IS_VALID(notes[n])) + { + Chn[n].nLastNote = notes[n]; + } + } if (instr[n]) Chn[n].nNewIns = instr[n]; if (vols[n] != 0xFF) { @@ -1539,7 +1546,10 @@ // Note Cut/Off/Fade => ignore instrument if (note >= NOTE_MIN_SPECIAL) instr = 0; - if ((note) && (note <= NOTE_MAX)) pChn->nNewNote = note; + if ((note) && (note <= NOTE_MAX)) + { + pChn->nNewNote = pChn->nLastNote = note; + } // New Note Action ? if ((note) && (note <= NOTE_MAX) && (!bPorta)) @@ -2125,14 +2135,14 @@ break; // Midi Controller - case CMD_MIDI: // Midi Controller (on first tick only) - case CMD_SMOOTHMIDI: // Midi Controller (smooth, i.e. on every tick) + case CMD_MIDI: // MIDI Controller (on first tick only) + case CMD_SMOOTHMIDI: // MIDI Controller (smooth, i.e. on every tick) - if((cmd == CMD_MIDI) && !(m_dwSongFlags & SONG_FIRSTTICK)) break; + /*if((cmd == CMD_MIDI) && !(m_dwSongFlags & SONG_FIRSTTICK)) break; if (param < 0x80) - ProcessMidiMacro(nChn, (cmd == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro], param); + ProcessMIDIMacro(nChn, (cmd == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro], param); else - ProcessMidiMacro(nChn, (cmd == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(param & 0x7F)], 0); + ProcessMIDIMacro(nChn, (cmd == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(param & 0x7F)], 0);*/ break; // IMF Commands @@ -3049,121 +3059,219 @@ } -// Process a Midi Macro. +// Process a MIDI Macro. // Parameters: // [in] nChn: Mod channel to apply macro on // [in] isSmooth: If true, internal macros are interpolated between two rows -// [in] pszMidiMacro: Actual Midi Macro -// [in] param: Parameter for parametric macros -void CSoundFile::ProcessMidiMacro(UINT nChn, bool isSmooth, LPCSTR pszMidiMacro, UINT param) -//------------------------------------------------------------------------------------------ +// [in] macro: Actual MIDI Macro string +// [in] param: Parameter for parametric macros (Z00 - Z7F) +// [in] plugin: Plugin to send MIDI message to (if not specified but needed, it is autodetected) +void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, char *macro, uint8 param, PLUGINDEX plugin) +//------------------------------------------------------------------------------------------------------------- { - MODCHANNEL *pChn = &Chn[nChn]; - DWORD dwMacro = LittleEndian(*((DWORD *)pszMidiMacro)) & MACRO_MASK; - int nInternalCode; + const MODCHANNEL *pChn = &Chn[nChn]; + const MODINSTRUMENT *pIns = GetNumInstruments() ? pChn->pModInstrument : nullptr; - // Not Internal Device ? - if (dwMacro != MACRO_INTERNAL && dwMacro != MACRO_INTERNALEX) + unsigned char out[MACRO_LENGTH]; + size_t outPos = 0; // output buffer position, which also equals the number of complete bytes + bool firstNibble = true; + + for(size_t pos = 0; pos < MACRO_LENGTH && macro[pos]; pos++) { - // we don't cater for external devices at tick resolution. - if(isSmooth && !(m_dwSongFlags & SONG_FIRSTTICK)) + bool isNibble = false; // did we parse a nibble or a byte value? + unsigned char data = 0; // data that has just been parsed + + // Parse next macro byte... See Impulse Tracker's MIDI.TXT for detailed information on each possible character. + if(macro[pos] >= '0' && macro[pos] <= '9') { - return; + isNibble = true; + data = (unsigned char)macro[pos] - '0'; } + else if(macro[pos] >= 'A' && macro[pos] <= 'F') + { + isNibble = true; + data = (unsigned char)macro[pos] - 'A' + 0x0A; + } else if(macro[pos] == 'c') // c: MIDI channel + { + isNibble = true; + data = (unsigned char)GetBestMidiChan(pChn); + } else if(macro[pos] == 'n') // n: note value (last triggered note) + { + if(pChn->nLastNote != NOTE_NONE && NOTE_IS_VALID(pChn->nLastNote)) + { + data = (unsigned char)(pChn->nLastNote - NOTE_MIN); + } + } else if(macro[pos] == 'v') // v: velocity + { + data = (unsigned char)min(pChn->nVolume / 2, 127); - UINT pos = 0, nNib = 0, nBytes = 0; - DWORD dwMidiCode = 0, dwByteCode = 0; + } else if(macro[pos] == 'u') // u: volume (calculated) + { + data = (unsigned char)min(pChn->nCalcVolume >> 7, 127); + } else if(macro[pos] == 'x') // x: pan set + { + data = (unsigned char)min(pChn->nPan / 2, 127); + } else if(macro[pos] == 'y') // y: calculated pan + { + data = (unsigned char)min(pChn->nRealPan / 2, 127); + } else if(macro[pos] == 'a') // a: high byte of bank select + { + if(pIns && pIns->wMidiBank) + { + data = (unsigned char)(((pIns->wMidiBank - 1) >> 7) & 0x7F); + } + } else if(macro[pos] == 'b') // b: low byte of bank select + { + if(pIns && pIns->wMidiBank) + { + data = (unsigned char)((pIns->wMidiBank - 1) & 0x7F); + } + } else if(macro[pos] == 'p') // p: program select + { + if(pIns && pIns->nMidiProgram) + { + data = (unsigned char)((pIns->nMidiProgram - 1) & 0x7F); + } + } else if(macro[pos] == 'z') // z: macro data + { + data = (unsigned char)(param & 0x7F); + } else // unrecognized byte (f.e. space char) + { + continue; + } - while (pos + 6 <= 32) + // Append parsed data + if(isNibble) // parsed a nibble (constant or 'c' variable) { - const CHAR cData = pszMidiMacro[pos++]; - if (!cData) break; - if ((cData >= '0') && (cData <= '9')) { dwByteCode = (dwByteCode << 4) | (cData - '0'); nNib++; } else - if ((cData >= 'A') && (cData <= 'F')) { dwByteCode = (dwByteCode << 4) | (cData - 'A' + 10); nNib++; } else - if ((cData >= 'a') && (cData <= 'f')) { dwByteCode = (dwByteCode << 4) | (cData - 'a' + 10); nNib++; } else - if ((cData == 'z') || (cData == 'Z')) { dwByteCode = param & 0x7F; nNib = 2; } else - if ((cData == 'x') || (cData == 'X')) { dwByteCode = param & 0x70; nNib = 2; } else - if ((cData == 'y') || (cData == 'Y')) { dwByteCode = (param & 0x0F) << 3; nNib = 2; } - if ((cData == 'k') || (cData == 'K')) { dwByteCode = (dwByteCode << 4) | GetBestMidiChan(pChn); nNib++; } + if(firstNibble) + { + out[outPos] = data; + } else + { + out[outPos] = (out[outPos] << 4) | data; + outPos++; + } + firstNibble = !firstNibble; + } else // parsed a byte (variable) + { + if(!firstNibble) // From MIDI.TXT: '9n' is exactly the same as '09 n' or '9 n' -- so finish current byte first + { + outPos++; + } + out[outPos++] = data; + firstNibble = true; + } + } + if(!firstNibble) + { + // Finish current byte + outPos++; + } - if (nNib >= 2) + if(outPos == 0) + { + // Nothing there to send! + return; + } + + // Macro string has been parsed and translated, now send the message(s)... + size_t sendPos = 0; + while(sendPos < outPos) + { + size_t sendLen = 0; + if(out[sendPos] == 0xF0) + { + // SysEx start + if((sendPos <= outPos - 4) && (out[sendPos + 1] == 0xF0 || out[sendPos + 1] == 0xF1)) { - nNib = 0; - dwMidiCode |= dwByteCode << (nBytes * 8); - dwByteCode = 0; - nBytes++; - - if (nBytes >= 3) + // Internal macro (normal (F0F0) or extended (F0F1)), 4 bytes long + sendLen = 4; + } else + { + // SysEx message, find end of message + for(size_t i = sendPos + 1; i < outPos; i++) { - UINT nMasterCh = (nChn < m_nChannels) ? nChn + 1 : pChn->nMasterChn; - if ((nMasterCh) && (nMasterCh <= m_nChannels)) + if(out[i] == 0xF7) { -// -> CODE#0015 -// -> DESC="channels management dlg" - UINT nPlug = GetBestPlugin(nChn, PRIORITISE_CHANNEL, EVEN_IF_MUTED); - if(pChn->dwFlags & CHN_NOFX) nPlug = 0; -// -! NEW_FEATURE#0015 - if ((nPlug) && (nPlug <= MAX_MIXPLUGINS)) - { - IMixPlugin *pPlugin = m_MixPlugins[nPlug - 1].pMixPlugin; - if ((pPlugin) && (m_MixPlugins[nPlug - 1].pMixState)) - { - pPlugin->MidiSend(dwMidiCode); - } - } + // Found end of SysEx message + sendLen = i - sendPos + 1; + break; } - nBytes = 0; - dwMidiCode = 0; } + if(sendLen == 0) + { + // Didn't find end, so "invent" end of SysEx message + out[outPos++] = 0xF7; + sendLen = outPos - sendPos; + } } + } else + { + // Other MIDI messages, find beginning of next message + while(sendPos + (++sendLen) < outPos) + { + if((out[sendPos + sendLen] & 0x80) != 0) + { + // Next message begins here. + break; + } + } + } + if(sendLen == 0) + { + break; } - - return; + + size_t bytesSent = SendMIDIData(nChn, isSmooth, out + sendPos, sendLen, plugin); + // Ideally (if there's no error in the macro data), we should have sendLen == bytesSent. + if(bytesSent > 0) + { + sendPos += bytesSent; + } else + { + sendPos += sendLen; + } + } - // Internal device - const bool extendedParam = (dwMacro == MACRO_INTERNALEX); +} - pszMidiMacro += 4; // skip the F0.F0 part of the macro - // Determine which internal device is called; every internal code looks like F0.F0.yy.xx, - // where yy is the "device" (cutoff, resonance, plugin parameter, etc.) and xx is the value. - nInternalCode = -256; - if ((pszMidiMacro[0] >= '0') && (pszMidiMacro[0] <= '9')) nInternalCode = (pszMidiMacro[0] - '0') << 4; else - if ((pszMidiMacro[0] >= 'A') && (pszMidiMacro[0] <= 'F')) nInternalCode = (pszMidiMacro[0] - 'A' + 0x0A) << 4; - if ((pszMidiMacro[1] >= '0') && (pszMidiMacro[1] <= '9')) nInternalCode += (pszMidiMacro[1] - '0'); else - if ((pszMidiMacro[1] >= 'A') && (pszMidiMacro[1] <= 'F')) nInternalCode += (pszMidiMacro[1] - 'A' + 0x0A); - // Filter ? - if (nInternalCode >= 0) + +// Process MIDI macro data parsed by ProcessMIDIMacro... return bytes sent on success, 0 on (parse) failure. +size_t CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned char *macro, size_t macroLen, PLUGINDEX plugin) +//------------------------------------------------------------------------------------------------------------------------------ +{ + if(macroLen < 1) { - CHAR cData1 = pszMidiMacro[2]; - DWORD dwParam = 0; + return 0; + } - if ((cData1 == 'z') || (cData1 == 'Z')) + MODCHANNEL *pChn = &Chn[nChn]; + + if(macro[0] == 0xF0 && (macro[1] == 0xF0 || macro[1] == 0xF1)) + { + // Internal device. + if(macroLen < 4) { - // parametric macro - dwParam = param; - } else - { - // fixed macro - CHAR cData2 = pszMidiMacro[3]; - if ((cData1 >= '0') && (cData1 <= '9')) dwParam += (cData1 - '0') << 4; else - if ((cData1 >= 'A') && (cData1 <= 'F')) dwParam += (cData1 - 'A' + 0x0A) << 4; - if ((cData2 >= '0') && (cData2 <= '9')) dwParam += (cData2 - '0'); else - if ((cData2 >= 'A') && (cData2 <= 'F')) dwParam += (cData2 - 'A' + 0x0A); + return 0; } + const bool isExtended = (macro[1] == 0xF1); + const uint8 macroCode = macro[2]; + const uint8 param = macro[3]; - switch(nInternalCode) + switch(macroCode) { // F0.F0.00.xx: Set CutOff case 0x00: + if(!isExtended) { int oldcutoff = pChn->nCutOff; - if (dwParam < 0x80) + if(param < 0x80) { if(!isSmooth) { - pChn->nCutOff = dwParam; + pChn->nCutOff = param; } else { // on the first tick only, calculate step @@ -3171,7 +3279,7 @@ { pChn->m_nPlugInitialParamValue = pChn->nCutOff; // (dwParam & 0x7F) extracts the actual value that we're going to pass - pChn->m_nPlugParamValueStep = (float)((int)dwParam - pChn->m_nPlugInitialParamValue) / (float)m_nMusicSpeed; + pChn->m_nPlugParamValueStep = (float)((int)param - pChn->m_nPlugInitialParamValue) / (float)GetNumTicksOnCurrentRow(); } //update param on all ticks pChn->nCutOff = (BYTE) (pChn->m_nPlugInitialParamValue + (m_nTickCount + 1) * pChn->m_nPlugParamValueStep + 0.5); @@ -3180,61 +3288,71 @@ } #ifndef NO_FILTER oldcutoff -= pChn->nCutOff; - if (oldcutoff < 0) oldcutoff = -oldcutoff; - if ((pChn->nVolume > 0) || (oldcutoff < 0x10) + if(oldcutoff < 0) oldcutoff = -oldcutoff; + if((pChn->nVolume > 0) || (oldcutoff < 0x10) || (!(pChn->dwFlags & CHN_FILTER)) || (!(pChn->nLeftVol|pChn->nRightVol))) SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? false : true); #endif // NO_FILTER + return 4; } break; - + // F0.F0.01.xx: Set Resonance case 0x01: - if (dwParam < 0x80) + if(!isExtended) { - pChn->nRestoreResonanceOnNewNote = 0; - if(!isSmooth) + if(param < 0x80) { - pChn->nResonance = dwParam; - } else - { - // on the first tick only, calculate step - if(m_dwSongFlags & SONG_FIRSTTICK) + pChn->nRestoreResonanceOnNewNote = 0; + if(!isSmooth) { - pChn->m_nPlugInitialParamValue = pChn->nResonance; - // (dwParam & 0x7F) extracts the actual value that we're going to pass - pChn->m_nPlugParamValueStep = (float)((int)dwParam - pChn->m_nPlugInitialParamValue) / (float)m_nMusicSpeed; + pChn->nResonance = param; + } else + { + // on the first tick only, calculate step + if(m_dwSongFlags & SONG_FIRSTTICK) + { + pChn->m_nPlugInitialParamValue = pChn->nResonance; + // (dwParam & 0x7F) extracts the actual value that we're going to pass + pChn->m_nPlugParamValueStep = (float)((int)param - pChn->m_nPlugInitialParamValue) / (float)GetNumTicksOnCurrentRow(); + } + //update param on all ticks + pChn->nResonance = (BYTE) (pChn->m_nPlugInitialParamValue + (m_nTickCount + 1) * pChn->m_nPlugParamValueStep + 0.5); } - //update param on all ticks - pChn->nResonance = (BYTE) (pChn->m_nPlugInitialParamValue + (m_nTickCount + 1) * pChn->m_nPlugParamValueStep + 0.5); } - } - + #ifndef NO_FILTER - SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? false : true); + SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? false : true); #endif // NO_FILTER + return 4; + } break; // F0.F0.02.xx: Set filter mode (high nibble determines filter mode) case 0x02: - if (dwParam < 0x20) + if(!isExtended) { - pChn->nFilterMode = (dwParam >> 4); + if(param < 0x20) + { + pChn->nFilterMode = (param >> 4); #ifndef NO_FILTER - SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? false : true); + SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? false : true); #endif // NO_FILTER + } + return 4; } break; - // F0.F0.03.xx: Set plug dry/wet + // F0.F0.03.xx: Set plug dry/wet case 0x03: + if(!isExtended) { - const UINT nPlug = GetBestPlugin(nChn, PRIORITISE_CHANNEL, EVEN_IF_MUTED); - if ((nPlug) && (nPlug <= MAX_MIXPLUGINS) && dwParam < 0x80) + const PLUGINDEX nPlug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PRIORITISE_CHANNEL, EVEN_IF_MUTED); + if ((nPlug) && (nPlug <= MAX_MIXPLUGINS) && param < 0x80) { if(!isSmooth) { - m_MixPlugins[nPlug - 1].fDryRatio = 1.0 - (static_cast<float>(dwParam) / 127.0f); + m_MixPlugins[nPlug - 1].fDryRatio = 1.0 - (static_cast<float>(param) / 127.0f); } else { // on the first tick only, calculate step @@ -3242,48 +3360,73 @@ { pChn->m_nPlugInitialParamValue = m_MixPlugins[nPlug - 1].fDryRatio; // (dwParam & 0x7F) extracts the actual value that we're going to pass - pChn->m_nPlugParamValueStep = ((1 - ((float)(dwParam) / 127.0f)) - pChn->m_nPlugInitialParamValue) / (float)m_nMusicSpeed; + pChn->m_nPlugParamValueStep = ((1 - ((float)(param) / 127.0f)) - pChn->m_nPlugInitialParamValue) / (float)GetNumTicksOnCurrentRow(); } //update param on all ticks m_MixPlugins[nPlug - 1].fDryRatio = pChn->m_nPlugInitialParamValue + (float)(m_nTickCount + 1) * pChn->m_nPlugParamValueStep; } } + return 4; } break; - - // F0.F0.{80|n}.xx: Set VST effect parameter n to xx + // F0.F0.{80|n}.xx / F0.F1.n.xx: Set VST effect parameter n to xx default: - if (nInternalCode & 0x80 || extendedParam) + if((macroCode & 0x80) || isExtended) { - UINT nPlug = GetBestPlugin(nChn, PRIORITISE_CHANNEL, EVEN_IF_MUTED); - if ((nPlug) && (nPlug <= MAX_MIXPLUGINS)) + const PLUGINDEX nPlug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PRIORITISE_CHANNEL, EVEN_IF_MUTED); + const UINT plugParam = isExtended ? (0x80 + macroCode) : (macroCode & 0x7F); + if((nPlug) && (nPlug <= MAX_MIXPLUGINS)) { IMixPlugin *pPlugin = m_MixPlugins[nPlug-1].pMixPlugin; - if ((pPlugin) && (m_MixPlugins[nPlug-1].pMixState)) + if((pPlugin) && (m_MixPlugins[nPlug-1].pMixState)) { if(!isSmooth) { - pPlugin->SetZxxParameter(extendedParam ? (0x80 + nInternalCode) : (nInternalCode & 0x7F), dwParam & 0x7F); + pPlugin->SetZxxParameter(plugParam, param & 0x7F); } else { // on the first tick only, calculate step if(m_dwSongFlags & SONG_FIRSTTICK) { - pChn->m_nPlugInitialParamValue = pPlugin->GetZxxParameter(extendedParam ? (0x80 + nInternalCode) : (nInternalCode & 0x7F)); + pChn->m_nPlugInitialParamValue = pPlugin->GetZxxParameter(plugParam); // (dwParam & 0x7F) extracts the actual value that we're going to pass - pChn->m_nPlugParamValueStep = ((int)(dwParam & 0x7F) - pChn->m_nPlugInitialParamValue) / (float)m_nMusicSpeed; + pChn->m_nPlugParamValueStep = ((int)(param & 0x7F) - pChn->m_nPlugInitialParamValue) / (float)GetNumTicksOnCurrentRow(); } //update param on all ticks - pPlugin->SetZxxParameter(extendedParam ? (0x80 + nInternalCode) : (nInternalCode & 0x7F), (UINT) (pChn->m_nPlugInitialParamValue + (m_nTickCount + 1) * pChn->m_nPlugParamValueStep + 0.5)); + pPlugin->SetZxxParameter(plugParam, (UINT) (pChn->m_nPlugInitialParamValue + (m_nTickCount + 1) * pChn->m_nPlugParamValueStep + 0.5)); } } } + return 4; } - - } // end switch - } // end internal device - + break; + } + } else + { + // Not an internal device. Pass on to appropriate plugin. + const UINT nMasterCh = (nChn < GetNumChannels()) ? nChn + 1 : pChn->nMasterChn; + if((nMasterCh) && (nMasterCh <= GetNumChannels())) + { + const PLUGINDEX nPlug = (pChn->dwFlags & CHN_NOFX) ? 0 : ((plugin != 0) ? plugin : GetBestPlugin(nChn, PRIORITISE_CHANNEL, EVEN_IF_MUTED)); + if((nPlug) && (nPlug <= MAX_MIXPLUGINS)) + { + IMixPlugin *pPlugin = m_MixPlugins[nPlug - 1].pMixPlugin; + if ((pPlugin) && (m_MixPlugins[nPlug - 1].pMixState)) + { + // currently, we don't support sending long MIDI messages in one go... split it up + for(size_t pos = 0; pos < macroLen; pos += 3) + { + DWORD curData = 0; + memcpy(&curData, macro + pos, min(3, macroLen - pos)); + pPlugin->MidiSend(curData); + } + } + } + } + return macroLen; + } + return 0; } @@ -3322,14 +3465,6 @@ if ((pChn->nRowNote >= NOTE_MIN) && (pChn->nRowNote <= NOTE_MAX)) { - /* if (bPorta) - pChn->nPos = param; - else - pChn->nPos += param; */ - // The code above doesn't make sense at all. If there's a note and no porta, how on earth could nPos be something else than 0? - // Anyway, keeping a debug assert here, just in case... - ASSERT(bPorta || pChn->nPos == 0); - // XM compatibility: Portamento + Offset = Ignore offset if(bPorta && IsCompatibleMode(TRK_FASTTRACKER2)) { Modified: trunk/OpenMPT/soundlib/Sndfile.cpp =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.cpp 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/soundlib/Sndfile.cpp 2011-07-30 22:14:04 UTC (rev 947) @@ -764,6 +764,12 @@ if ((m_nRestartPos >= Order.size()) || (Order[m_nRestartPos] >= Patterns.Size())) m_nRestartPos = 0; + // Fix old nasty broken (non-standard) MIDI configs in files. + if(m_dwLastSavedWithVersion && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + { + FixMIDIConfigStrings(m_MidiCfg); + } + // plugin loader string sNotFound; std::list<PLUGINDEX> notFoundIDs; @@ -918,13 +924,13 @@ //----------------------------- { MemsetZero(m_MidiCfg); - lstrcpy(m_MidiCfg.szMidiGlb[MIDIOUT_START], "FF"); - lstrcpy(m_MidiCfg.szMidiGlb[MIDIOUT_STOP], "FC"); - lstrcpy(m_MidiCfg.szMidiGlb[MIDIOUT_NOTEON], "9c n v"); - lstrcpy(m_MidiCfg.szMidiGlb[MIDIOUT_NOTEOFF], "9c n 0"); - lstrcpy(m_MidiCfg.szMidiGlb[MIDIOUT_PROGRAM], "Cc p"); - lstrcpy(m_MidiCfg.szMidiSFXExt[0], "F0F000z"); - for (int iz=0; iz<16; iz++) wsprintf(m_MidiCfg.szMidiZXXExt[iz], "F0F001%02X", iz*8); + strcpy(m_MidiCfg.szMidiGlb[MIDIOUT_START], "FF"); + strcpy(m_MidiCfg.szMidiGlb[MIDIOUT_STOP], "FC"); + strcpy(m_MidiCfg.szMidiGlb[MIDIOUT_NOTEON], "9c n v"); + strcpy(m_MidiCfg.szMidiGlb[MIDIOUT_NOTEOFF], "9c n 0"); + strcpy(m_MidiCfg.szMidiGlb[MIDIOUT_PROGRAM], "Cc p"); + strcpy(m_MidiCfg.szMidiSFXExt[0], "F0F000z"); + CModDoc::CreateZxxFromType(m_MidiCfg.szMidiZXXExt, zxx_reso4Bit); } @@ -2902,3 +2908,38 @@ { m_bITBidiMode = IsCompatibleMode(TRK_IMPULSETRACKER); } + + +void FixMIDIConfigString(char *line) +//---------------------------------- +{ + for(size_t i = 0; i < MACRO_LENGTH; i++) + { + if(line[i] >= 'a' && line[i] <= 'f') // both A-F and a-f were treated as hex constants + { + line[i] = line[i] - 'a' + 'A'; + } else if(line[i] == 'K' || line[i] == 'k') // channel was K or k + { + line[i] = 'c'; + } else if(line[i] == 'X' || line[i] == 'x' || line[i] == 'Y' || line[i] == 'y') // those were pointless + { + line[i] = 'z'; + } + } +} + + +// Fix old-format (not conforming to IT's MIDI macro definitions) MIDI config strings. +void CSoundFile::FixMIDIConfigStrings(MODMIDICFG &midiCfg) +//-------------------------------------------------------- +{ + for(size_t i = 0; i < CountOf(midiCfg.szMidiSFXExt); i++) + { + FixMIDIConfigString(midiCfg.szMidiSFXExt[i]); + } + for(size_t i = 0; i < CountOf(midiCfg.szMidiZXXExt); i++) + { + FixMIDIConfigString(midiCfg.szMidiZXXExt[i]); + } +} + Modified: trunk/OpenMPT/soundlib/Sndfile.h =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.h 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/soundlib/Sndfile.h 2011-07-30 22:14:04 UTC (rev 947) @@ -211,6 +211,7 @@ LONG nRealVolume, nRealPan; LONG nVolume, nPan, nFadeOutVol; LONG nPeriod, nC5Speed, nPortamentoDest; + int nCalcVolume; // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros MODINSTRUMENT *pModInstrument; // Currently assigned instrument slot MODCHANNEL_ENVINFO VolEnv, PanEnv, PitchEnv; // Envelope playback info MODSAMPLE *pModSample; // Currently assigned sample slot @@ -230,6 +231,7 @@ BYTE nRestoreResonanceOnNewNote; //Like above BYTE nRestoreCutoffOnNewNote; //Like above BYTE nNote, nNNA; + BYTE nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros BYTE nNewNote, nNewIns, nCommand, nArpeggio; BYTE nOldVolumeSlide, nOldFineVolUpDown; BYTE nOldPortaUpDown, nOldFinePortaUpDown; @@ -453,9 +455,9 @@ #define MACRO_LENGTH 32 // max number of chars per macro struct MODMIDICFG { - CHAR szMidiGlb[9][MACRO_LENGTH]; - CHAR szMidiSFXExt[16][MACRO_LENGTH]; - CHAR szMidiZXXExt[128][MACRO_LENGTH]; + char szMidiGlb[9][MACRO_LENGTH]; // Global MIDI macros + char szMidiSFXExt[16][MACRO_LENGTH]; // Parametric MIDI macros + char szMidiZXXExt[128][MACRO_LENGTH]; // Fixed MIDI macros }; STATIC_ASSERT(sizeof(MODMIDICFG) == 4896); // this is directly written to files, so the size must be correct! @@ -778,6 +780,8 @@ bool ReadJ2B(const LPCBYTE lpStream, const DWORD dwMemLength); bool ReadMID(const LPCBYTE lpStream, DWORD dwMemLength); + static void FixMIDIConfigStrings(MODMIDICFG &midiCfg); + // Save Functions #ifndef MODPLUG_NO_FILESAVE UINT WriteSample(FILE *f, MODSAMPLE *pSmp, UINT nFlags, UINT nMaxLen=0); @@ -942,7 +946,9 @@ void ExtendedS3MCommands(UINT nChn, UINT param); void ExtendedChannelEffect(MODCHANNEL *, UINT param); inline void InvertLoop(MODCHANNEL* pChn); - void ProcessMidiMacro(UINT nChn, bool isSmooth, LPCSTR pszMidiMacro, UINT param = 0); + void ProcessMacroOnChannel(CHANNELINDEX nChn); + void ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, char *macro, uint8 param = 0, PLUGINDEX plugin = 0); + size_t SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned char *macro, size_t macroLen, PLUGINDEX plugin); void SetupChannelFilter(MODCHANNEL *pChn, bool bReset, int flt_modifier = 256) const; // Low-Level effect processing void DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide); Modified: trunk/OpenMPT/soundlib/Sndmix.cpp =================================================================== --- trunk/OpenMPT/soundlib/Sndmix.cpp 2011-07-30 21:36:06 UTC (rev 946) +++ trunk/OpenMPT/soundlib/Sndmix.cpp 2011-07-30 22:14:04 UTC (rev 947) @@ -1220,6 +1220,7 @@ pChn->dwFlags |= CHN_NOTEFADE; pChn->nFadeOutVol = 0; pChn->nRealVolume = 0; + pChn->nCalcVolume = 0; } } } @@ -1922,6 +1923,12 @@ // Check for unused channel if ((pChn->dwFlags & CHN_MUTE) || ((nChn >= m_nChannels) && (!pChn->nLength))) { + if(nChn < m_nChannels) + { + // Process MIDI macros on channels that are currently muted. + ProcessMacroOnChannel(nChn); + } + pChn->nVUMeter = 0; #ifdef ENABLE_STEREOVU pChn->nLeftVU = pChn->nRightVU = 0; @@ -1944,6 +1951,7 @@ // Reset channel data pChn->nInc = 0; pChn->nRealVolume = 0; + pChn->nCalcVolume = 0; pChn->nRampLength = 0; @@ -1955,7 +1963,11 @@ // Calc Frequency int period; - if (pChn->nPeriod && pChn->nLength) + + // Also process envelopes etc. when there's a plugin on this channel, for possible fake automation using volume and pan data. + // We only care about master channels, though, since automation only "happens" on them. + const bool plugAssigned = (nChn < m_nChannels) && (ChnSettings[nChn].nMixPlugin || (pChn->pModInstrument != nullptr && pChn->pModInstrument->nMixPlug)); + if ((pChn->nPeriod && pChn->nLength) || plugAssigned) { int vol = pChn->nVolume; @@ -2004,6 +2016,9 @@ pChn->nRealVolume = _muldiv(vol * m_nGlobalVolume, pChn->nGlobalVol * pChn->nInsVol, 1 << 20); } } + + pChn->nCalcVolume = vol; // Update calculated volume for MIDI macros + if (pChn->nPeriod < m_nMinPeriod) pChn->nPeriod = m_nMinPeriod; period = pChn->nPeriod; if ((pChn->dwFlags & (CHN_GLISSANDO|CHN_PORTAMENTO)) == (CHN_GLISSANDO|CHN_PORTAMENTO)) @@ -2021,6 +2036,14 @@ ProcessPanbrello(pChn); + } + + // Now that all relevant envelopes etc. have been processed, we can parse the MIDI macro data. + ProcessMacroOnChannel(nChn); + + // After MIDI macros have been processed, we can also process the pitch / filter envelope and other pitch-related things. + if (pChn->nPeriod && pChn->nLength) + { int nPeriodFrac = 0; ProcessPitchFilterEnvelope(pChn, period); @@ -2075,6 +2098,7 @@ pChn->nFadeOutVol = 0; pChn->dwFlags |= CHN_NOTEFADE; pChn->nRealVolume = 0; + pChn->nCalcVolume = 0; } UINT ninc = _muldiv(freq, 0x10000, gdwMixingFreq); @@ -2303,6 +2327,22 @@ } +void CSoundFile::ProcessMacroOnChannel(CHANNELINDEX nChn) +//------------------------------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + if(nChn < m_nChannels && pChn->nRowCommand == CMD_MIDI || pChn->nRowCommand == CMD_SMOOTHMIDI) + { + // Only smooth MIDI macros are processed on every tick + if((pChn->nRowCommand == CMD_MIDI) && !(m_dwSongFlags & SONG_FIRSTTICK)) return; + if(pChn->nRowParam < 0x80) + ProcessMIDIMacro(nChn, (pChn->nRowCommand == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro], pChn->nRowParam); + else + ProcessMIDIMacro(nChn, (pChn->nRowCommand == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(pChn->nRowParam & 0x7F)], 0); + } +} + + #ifdef MODPLUG_TRACKER VOID CSoundFile::ProcessMidiOut(UINT nChn, MODCHANNEL *pChn) //rewbs.VSTdelay: added arg @@ -2329,6 +2369,7 @@ // Get instrument info and plugin reference MODINSTRUMENT *pIns = pChn->pModInstrument; + PLUGINDEX nPlugin = 0; IMixPlugin *pPlugin = nullptr; if ((instr) && (instr < MAX_INSTRUMENTS)) @@ -2339,7 +2380,7 @@ // Check instrument plugins if ((pIns->nMidiChannel >= 1) && (pIns->nMidiChannel <= 16)) { - UINT nPlugin = GetBestPlugin(nChn, PRIORITISE_INSTRUMENT, RESPECT_MUTES); + nPlugin = GetBestPlugin(nChn, PRIORITISE_INSTRUMENT, RESPECT_MUTES); if ((nPlugin) && (nPlugin <= MAX_MIXPLUGINS)) { pPlugin = m_MixPlugins[nPlugin-1].pMixPlugin; @@ -2387,6 +2428,8 @@ MODCOMMAND::NOTE realNote = note; if((note >= NOTE_MIN) && (note <= NOTE_MAX)) realNote = pIns->NoteMap[note - 1]; + // Experimental VST panning + //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_PAN], 0, nPlugin); pPlugin->MidiCommand(pIns->nMidiChannel, pIns->nMidiProgram, pIns->wMidiBank, realNote, velocity, nChn); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |