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