diff --git a/build/windows/palette_win_setup.iss b/build/windows/palette_win_setup.iss index 1006e48c..c8dc223c 100644 --- a/build/windows/palette_win_setup.iss +++ b/build/windows/palette_win_setup.iss @@ -54,7 +54,7 @@ Source: "ship\ffgl\*"; DestDir: "{app}\ffgl"; Flags: ignoreversion recursesubdir Source: "ship\data\*"; DestDir: "{commoncf64}\{#MyAppName}\data"; Flags: comparetimestamp ignoreversion recursesubdirs createallsubdirs ; Source: "ship\data_surge\*"; DestDir: "{commoncf64}\{#MyAppName}\data_surge"; Flags: comparetimestamp ignoreversion recursesubdirs createallsubdirs ; Source: "ship\data_moldover\*"; DestDir: "{commoncf64}\{#MyAppName}\data_moldover"; Flags: comparetimestamp ignoreversion recursesubdirs createallsubdirs -Source: "logs_readme.txt"; DestDir: "{commoncf64}\{#MyAppName}\logs"; DestName: "readme.txt"; Flags: ignoreversion +Source: "logs_readme.txt"; DestDir: "{commoncf64}\{#MyAppName}\data\logs"; DestName: "readme.txt"; Flags: ignoreversion ; NOTE: Don't use "Flags: ignoreversion" on any shared system files ; This specifies the Visual C++ Windows Runtime Redistributable to install, it's put in {app}\bin to help debug things. diff --git a/cmd/palette/palette.go b/cmd/palette/palette.go index 00887c27..5a3f14fa 100644 --- a/cmd/palette/palette.go +++ b/cmd/palette/palette.go @@ -44,8 +44,8 @@ func main() { func usage() string { return `Invalid usage, expecting: - palette start [ monitor, engine, gui, bidule, resolume, mmtt ] - palette kill [ all, monitor, engine, gui, bidule, resolume, mmtt] + palette start [ {processname} ] + palette stop [ {processname}] palette status palette version palette logs [ archive, clear ] diff --git a/data/config/paramdefs.json b/data/config/paramdefs.json index 127b24d6..d3fa767a 100644 --- a/data/config/paramdefs.json +++ b/data/config/paramdefs.json @@ -193,7 +193,6 @@ "global.midithru": {"valuetype":"bool", "min":"false", "max":"true", "init":"false", "comment":"#" }, "global.midithrusynth": {"valuetype":"string", "min":"synth", "max":"synth", "init":"default", "comment":"#" }, "global.midisetexternalscale": {"valuetype":"bool", "min":"false", "max":"true", "init":"false", "comment":"#" }, -"global.morphdefault": {"valuetype":"string", "min":"morphtype", "max":"morphtype", "init":"quadrants", "comment":"#" }, "global.mmtt_xexpand": {"valuetype": "float", "min": "0.5", "max": "2.0", "init": "1.25", "comment": "#" }, "global.mmtt_yexpand": {"valuetype": "float", "min": "0.5", "max": "2.0", "init": "1.25", "comment": "#" }, "global.mmtt_zexpand": {"valuetype": "float", "min": "0.5", "max": "10.0", "init": "4.0", "comment": "#" }, diff --git a/doc/installation.md b/doc/installation.md index 42c1ce31..c8307c95 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -20,6 +20,9 @@ The instructions below assume that you have:
`C:\Program Files\Git\usr\bin`
`C:\Program Files\Git\mingw64\bin` +- Install the redistributable support for X64 (the filename is vc_redist_x64.exe) from this page: +https://learn.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170 + - All VST plugins should be put in `%CommonProgramFiles%\VST2` (for VST 2 plugins) and `%CommonProgramFiles%\VST3` (for VST 3 plugins). - After installing and starting Bidule, use Edit->Preferences->Plugins to set the VST plugin directory to @@ -58,5 +61,5 @@ The instructions below assume that you have: - You're now ready to start using the Palette, as described here -- If there are any issues, log files can be found in `%CommonProgramFiles%\Palette\logs`, +- If there are any issues, log files can be found in `%CommonProgramFiles%\Palette\data\logs`, with `engine.log` being the most important one. Feel free to email me@timthompson with questions or suggestions for improving this documentation. diff --git a/ffgl/source/lib/nosuch/NosuchDebug.cpp b/ffgl/source/lib/nosuch/NosuchDebug.cpp index f03f9ca4..e72c6c82 100644 --- a/ffgl/source/lib/nosuch/NosuchDebug.cpp +++ b/ffgl/source/lib/nosuch/NosuchDebug.cpp @@ -122,25 +122,34 @@ RealNosuchDebugInit() { dMutex = CreateMutex(NULL, FALSE, NULL); DebugInitialized = TRUE; - char* pValue; - size_t len; - NosuchDebugLogPath = "c:\\windows\\temp\\ffgl.log";// last resort - errno_t err = _dupenv_s( &pValue, &len, "CommonProgramFiles" ); - if( err == 0 && pValue != NULL ) - { - NosuchDebugLogPath = std::string( pValue ) + "\\Palette\\logs\\ffgl.log"; - free( pValue ); + char* value; + errno_t err; + size_t len; + + err = _dupenv_s( &value, &len, "PALETTE_DATA_PATH" ); + if ( err == 0 && value != NULL ) { + NosuchDebugLogPath = std::string( value ) + "\\logs\\ffgl.log"; + free( value ); + } else { + // Otherwise it's in %CommonProgramFiles% + err = _dupenv_s( &value, &len, "CommonProgramFiles" ); + if( err == 0 && value != NULL ) { + // %CommonProgramFiles% is defined + NosuchDebugLogPath = std::string( value ) + "\\Palette\\data\\logs\\ffgl.log"; + } } - NosuchDebug( "NosuchDebugInit: debuglevel=%d log=%s\n", NosuchDebugLevel, NosuchDebugLogPath.c_str() ); - NosuchDebug( "NosuchDebugInit: DebugCursor=%d Param=%d API=%d\n", NosuchDebugCursor , NosuchDebugParam, NosuchDebugAPI); + NosuchDebug( "NosuchDebugInit: Level=%d Cursor=%d Param=%d API=%d\n", NosuchDebugLevel, NosuchDebugCursor , NosuchDebugParam, NosuchDebugAPI); - err = _dupenv_s( &pValue, &len, "PALETTE" ); - if( err || pValue == NULL ) + err = _dupenv_s( &value, &len, "PALETTE" ); + if( err || value == NULL ) { - NosuchDebug( "No value for PALETTE!?\n" ); + NosuchDebug( "No value for PALETTE environment variable!?\n" ); + } + if (value != NULL) { + free( value ); } diff --git a/ffgl/source/lib/palette/LayerParams_types.h b/ffgl/source/lib/palette/LayerParams_types.h index 78ce3c93..d496e1c4 100644 --- a/ffgl/source/lib/palette/LayerParams_types.h +++ b/ffgl/source/lib/palette/LayerParams_types.h @@ -1,3 +1,4 @@ +DEFINE_TYPES(resolumepath); DEFINE_TYPES(plugins); DEFINE_TYPES(log); DEFINE_TYPES(pitchset); @@ -31,6 +32,9 @@ DEFINE_TYPES(midiport); void LayerParams_InitializeTypes() { + LayerParams_resolumepathTypes.push_back("C:/Program Files/Resolume Avenue/Avenue.exe"); + LayerParams_resolumepathTypes.push_back("C:/Program Files/Resolume Arena/Arena.exe"); + LayerParams_pluginsTypes.push_back(""); LayerParams_pluginsTypes.push_back("quadpro"); diff --git a/ffgl/source/lib/palette/LayerParams_typesdeclare.h b/ffgl/source/lib/palette/LayerParams_typesdeclare.h index ec6144f3..fc216a3b 100644 --- a/ffgl/source/lib/palette/LayerParams_typesdeclare.h +++ b/ffgl/source/lib/palette/LayerParams_typesdeclare.h @@ -1,3 +1,4 @@ +DECLARE_TYPES(resolumepath); DECLARE_TYPES(plugins); DECLARE_TYPES(log); DECLARE_TYPES(pitchset); diff --git a/kit/engine.go b/kit/engine.go index ddb1669e..57117222 100644 --- a/kit/engine.go +++ b/kit/engine.go @@ -98,7 +98,7 @@ func InitEngine() { LogIfError(err) TheAttractManager.SetAttractEnabled(enabled) - CheckAutorestartProcesses() + // CheckAutorestartProcesses() nats, err := GetParamBool("global.nats") LogIfError(err) diff --git a/kit/midiio.go b/kit/midiio.go index 26b80951..5baf5dec 100644 --- a/kit/midiio.go +++ b/kit/midiio.go @@ -5,9 +5,9 @@ package kit import ( "fmt" + "runtime/debug" "strings" "sync" - "runtime/debug" midi "gitlab.com/gomidi/midi/v2" "gitlab.com/gomidi/midi/v2/drivers" diff --git a/kit/synth.go b/kit/synth.go index bcaaef1e..294362bc 100644 --- a/kit/synth.go +++ b/kit/synth.go @@ -70,82 +70,31 @@ func InitSynths() { LogInfo("Synths loaded", "len", len(Synths)) } -func (synth *Synth) Channel() uint8 { - return uint8(synth.portchannel.channel) -} - -func (synth *Synth) ClearNoteDowns() { - - synth.noteMutex.Lock() - defer synth.noteMutex.Unlock() - - for i := range synth.noteDown { - synth.noteDown[i] = false - synth.noteDownCount[i] = 0 - } -} - -func GetSynth(synthName string) *Synth { - synth, ok := Synths[synthName] - if !ok { - return nil - } - return synth -} - -func NewSynth(name string, port string, channel int, bank int, program int) *Synth { - - // If there's already a Synth for this PortChannel, should error - - portchannel := PortChannel{port: port, channel: channel} - - var state *MIDIPortChannelState - if name == "fake" { - state = TheMidiIO.openFakeChannelOutput(port, channel) - } else { - state = TheMidiIO.openChannelOutput(portchannel) - } - - if state == nil { - LogWarn("InitSynths: Unable to open", "port", port) - return nil - } - sp := &Synth{ - name: name, - portchannel: portchannel, - bank: bank, - program: program, - // midiChannelOut: midiChannelOut, - noteDown: make([]bool, 128), - noteDownCount: make([]int, 128), - state: state, - } - sp.ClearNoteDowns() // debugging, shouldn't be needed - return sp -} - -func (synth *Synth) updatePortChannelState() (*MIDIPortChannelState, error) { - if TheMidiIO == nil { - return nil, fmt.Errorf("updatePortChannelState: TheMidiIO is nil?") +func (synth *Synth) midiOutputEnabled() bool { + if synth.state == nil { + LogWarn("Synth.MidiOutputEnabled: state is nil") + return false } - state, err := TheMidiIO.GetPortChannelState(synth.portchannel) - if err != nil { - return nil, err + if synth.state.output == nil { + LogWarn("Synth.MidiOutputEnabled: output is nil") + return false } - // This only sends the bank and/or program if they change - synth.UpdateBankProgram() - return state, nil + return true } // SendANO sends all-notes-off func (synth *Synth) SendANO() { + if !synth.midiOutputEnabled() { + return + } + state, err := synth.updatePortChannelState() if err != nil { LogIfError(err) return } - synth.ClearNoteDowns() + synth.clearNoteDowns() // send All Notes Off status := 0xb0 | byte(synth.portchannel.channel-1) @@ -162,6 +111,10 @@ func (synth *Synth) SendANO() { func (synth *Synth) SendController(cnum uint8, cval uint8) { + if !synth.midiOutputEnabled() { + return + } + state, err := synth.updatePortChannelState() if err != nil { LogIfError(err) @@ -189,123 +142,12 @@ func (synth *Synth) SendController(cnum uint8, cval uint8) { LogIfError(state.output.Send([]byte{status, data1, data2})) } -/* -func SendToSynth(value any) { - - var channel uint8 - var pitch uint8 - var velocity uint8 - - switch v := value.(type) { - case *NoteOn: - channel = v.Channel - pitch = v.Pitch - velocity = v.Velocity - if velocity == 0 { - LogInfo("MIDIIO.SendNote: noteon with velocity==0 NOT changed to a noteoff") - } - case *NoteOff: - channel = v.Channel - pitch = v.Pitch - velocity = v.Velocity - case *NoteFull: - channel = v.Channel - pitch = v.Pitch - velocity = v.Velocity - default: - LogWarn("SendToSynth: doesn't handle", "type", fmt.Sprintf("%T", v)) - return - } - - synthName := fmt.Sprintf("P_01_C_%02d", channel+1) - synth, ok := Synths[synthName] - if !ok { - LogWarn("SendPhraseElementToSynth: no such", "synth", synthName) - return - } - - if synth == nil { - // We don't complain, we assume the inability to open the - // synth named synthName has already been logged. - return - wsc := MIDI.GetMidiChannelOutput(synth.portchannel) - if mc == nil { - // Assumes errs are logged in GetMidiChannelOutput - return - } - - // This only sends the bank and/or program if they change - mc.SendBankProgram(synth.bank, synth.program) - - status := byte(synth.portchannel.channel - 1) - data1 := pitch - data2 := velocity - switch value.(type) { - case *NoteOn: - status |= NoteOnStatus - - // We now allow multiple notes with the same pitch, - // which assumes the synth handles it okay. - // There might need to be an option to - // automatically send a noteOff before sending the noteOn. - // if synth.noteDown[note.Pitch] { - // Warn("SendPhraseElementToSynth: Ignoring second noteon") - // } - - synth.noteDown[pitch] = true - synth.noteDownCount[pitch]++ - - LogOfType("midi", "SendNoteOnToSynth", - "synth", synth, - "channel", synth.portchannel.channel, - "pitch", pitch, - "notedowncount", synth.noteDownCount[pitch]) - - case *NoteOff: - status |= NoteOffStatus - data2 = 0 - synth.noteDown[pitch] = false - synth.noteDownCount[pitch]-- - - LogOfType("midi", "SendNoteOffToSynth", - "synth", synth, - "channel", synth.portchannel.channel, - "pitch", pitch, - "notedowncount", synth.noteDownCount[pitch]) - - // case "controller": - // status |= 0xB0 - // case "progchange": - // status |= 0xC0 - // case "chanpressure": - // status |= 0xD0 - // case "pitchbend": - // status |= 0xE0 +func (synth *Synth) SendNoteToMidiOutput(value any) { - default: - LogWarn("SendToSynth: can't handle", "type", fmt.Sprintf("%T", value)) + if !synth.midiOutputEnabled() { return } - LogOfType("midi", "Raw MIDI Output", - "synth", synth, - "status", hexString(status), - "data1", hexString(data1), - "data2", hexString(data2)) - - err := mc.output.Send([]byte{status, data1, data2}) - if err != nil { - LogWarn("output.Send", "err", err) - } -} -*/ - -func hexString(b byte) string { - return fmt.Sprintf("%02x", b) -} - -func (synth *Synth) SendNoteToMidiOutput(value any) { - // var channel uint8 var pitch uint8 var velocity uint8 @@ -391,6 +233,10 @@ func (synth *Synth) SendNoteToMidiOutput(value any) { // SendBytesToMidiOutput func (synth *Synth) SendBytesToMidiOutput(bytes []byte) { + if !synth.midiOutputEnabled() { + return + } + if len(bytes) == 0 { LogWarn("SendBytesToMidiOutput: 0-length bytes?") return @@ -435,11 +281,74 @@ func (synth *Synth) SendBytesToMidiOutput(bytes []byte) { } } -func (synth *Synth) SendBytes(bytes []byte) error { - return synth.state.output.Send(bytes) +func (synth *Synth) Channel() uint8 { + return uint8(synth.portchannel.channel) +} + +func (synth *Synth) clearNoteDowns() { + + synth.noteMutex.Lock() + defer synth.noteMutex.Unlock() + + for i := range synth.noteDown { + synth.noteDown[i] = false + synth.noteDownCount[i] = 0 + } +} + +func GetSynth(synthName string) *Synth { + synth, ok := Synths[synthName] + if !ok { + return nil + } + return synth +} + +func NewSynth(name string, port string, channel int, bank int, program int) *Synth { + + // If there's already a Synth for this PortChannel, should error + + portchannel := PortChannel{port: port, channel: channel} + + var state *MIDIPortChannelState + if name == "fake" { + state = TheMidiIO.openFakeChannelOutput(port, channel) + } else { + state = TheMidiIO.openChannelOutput(portchannel) + } + + if state == nil { + LogWarn("InitSynths: Unable to open", "port", port) + return nil + } + sp := &Synth{ + name: name, + portchannel: portchannel, + bank: bank, + program: program, + // midiChannelOut: midiChannelOut, + noteDown: make([]bool, 128), + noteDownCount: make([]int, 128), + state: state, + } + sp.clearNoteDowns() // debugging, shouldn't be needed + return sp +} + +func (synth *Synth) updatePortChannelState() (*MIDIPortChannelState, error) { + if TheMidiIO == nil { + return nil, fmt.Errorf("updatePortChannelState: TheMidiIO is nil?") + } + state, err := TheMidiIO.GetPortChannelState(synth.portchannel) + if err != nil { + return nil, err + } + // This only sends the bank and/or program if they change + synth.updateBankProgram() + return state, nil } -func (synth *Synth) UpdateBankProgram() { +func (synth *Synth) updateBankProgram() { state := synth.state state.mutex.Lock() @@ -465,3 +374,7 @@ func (synth *Synth) UpdateBankProgram() { LogIfError(state.output.Send([]byte{status, data1})) } } + +func hexString(b byte) string { + return fmt.Sprintf("%02x", b) +} diff --git a/kit/windowsmorph.go b/kit/windowsmorph.go index ebae3810..83e98344 100644 --- a/kit/windowsmorph.go +++ b/kit/windowsmorph.go @@ -243,8 +243,8 @@ import "C" import ( "fmt" - "time" "runtime/debug" + "time" ) type oneMorph struct { @@ -473,6 +473,9 @@ func WinMorphInitialize() error { numdevices := int(C.SenselNumDevices()) allMorphs = make([]*oneMorph, numdevices) + defaultMorphName := []string{"A", "B", "C", "D"} + nextMorphIndex := 0 + for idx := uint8(0); idx < uint8(numdevices); idx++ { m := &oneMorph{ @@ -516,11 +519,9 @@ func WinMorphInitialize() error { morphtype, ok := MorphDefs[m.serialNum] if !ok { // It's not explicitly present in morphs.json - t, err := GetParam("global.morphdefault") - if err != nil { - morphtype = "A" - } else { - morphtype = t + morphtype = defaultMorphName[nextMorphIndex] + if nextMorphIndex < len(defaultMorphName)-1 { + nextMorphIndex++ } LogInfo("Morph serial# isn't in morphs.json", "serialnum", m.serialNum, "morphtype", morphtype) } diff --git a/release/palette_7.85_win_setup.exe b/release/palette_7.85_win_setup.exe new file mode 100644 index 00000000..e8093a51 Binary files /dev/null and b/release/palette_7.85_win_setup.exe differ