Skip to content

Custom Seed Targets

Graham Wakefield edited this page Aug 11, 2021 · 11 revisions

For a custom seed-based target (i.e. not Patch/Field/Petal/Pod/Versio etc.):

You will need to create a JSON file specific to the hardware target. It could be a good idea to store this JSON file in the same folder as your patcher.

The path to this JSON file can be configured in the [oopsy] patcher using either the "browse" button, by sending the "target " message to it, or including it in the [oopsy] patcher arguments.

JSON configuration files

Here are some examples:

Minimal example

A minimal breadboard-ready configuration, with buttons attached to pins 18 & 28, knobs attached to pins 21 & 25:

{
	"components": {
		"sw1": {
			"component": "Switch",
			"pin": 18
		},
		"knob1": {
			"component": "AnalogControl",
			"pin": 21
		},
		"knob2": {
			"component": "AnalogControl",
			"pin": 25
		},
		"sw2": {
			"component": "Switch",
			"pin": 28
		}
	}
}

Daisy Pod

This is fully equivalent to the default Daisy Pod target:

{
	"name":"pod",
	"defines": {
		"OOPSY_TARGET_HAS_MIDI_INPUT": 1
	},
	"components": {
		"sw1": {
			"component": "Switch",
			"pin": 27
		},
		"sw2": {
			"component": "Switch",
			"pin": 28
		},
		"knob1": {
			"component": "AnalogControl",
			"pin": 21
		},
		"knob2": {
			"component": "AnalogControl",
			"pin": 15
		},
		"encoder": {
			"component": "Encoder",
			"pin": {"a":26, "b":25, "click":13 }
		},
		"led1": {
			"component": "RgbLed",
			"pin": {"r":20, "g":19, "b":18 }
		},
		"led2": {
			"component": "RgbLed",
			"pin": {"r":17, "g":24, "b":23 }
		}
	},
	"aliases": {
		"switch": "sw1",
		"button": "sw1",
		"switch1": "sw1",
		"button1": "sw1",
		"switch2": "sw2",
		"button2": "sw2",
		"enp": "encoder_press",
		"switch3": "encoder_press",
		"press": "encoder_press",
		"knob": "knob1",
		"ctrl": "knob1",
		"ctrl1": "knob1",
		"ctrl2": "knob2",
		"led": "led1"
	}
}

Noise Engineering Versio

This is fully equivalent to the default Versio target:

{
	"name":"versio",
	"defines": {},
	"components": {
		"sw1": {
			"component": "Switch",
			"pin": 27
		},
		"sw2": {
			"component": "Switch",
			"pin": 28
		},
		"knob1": {
			"component": "AnalogControl",
			"pin": 21
		},
		"knob2": {
			"component": "AnalogControl",
			"pin": 15
		},
		"encoder": {
			"component": "Encoder",
			"pin": {"a":26, "b":25, "click":13 }
		},
		"led1": {
			"component": "RgbLed",
			"pin": {"r":20, "g":19, "b":18 }
		},
		"led2": {
			"component": "RgbLed",
			"pin": {"r":17, "g":24, "b":23 }
		}
	},
	"aliases": {
		"switch": "sw1",
		"button": "sw1",
		"switch1": "sw1",
		"button1": "sw1",
		"switch2": "sw2",
		"button2": "sw2",
		"enp": "encoder_press",
		"switch3": "encoder_press",
		"press": "encoder_press",
		"knob": "knob1",
		"ctrl": "knob1",
		"ctrl1": "knob1",
		"ctrl2": "knob2",
		"led": "led1"
	}
}

Options

components (required): A set of named component mappings. Currently available components are:

  • "AnalogControl" (for CV inputs, knobs, sliders, etc.)
    • requires: "pin": <number>
    • provides inputs: <name>, <name>_trig
  • "GateIn"
    • requires: "pin": <number>
    • provides inputs: <name>, <name>_trig
  • "Switch"
    • requires: "pin": <number>,
    • provides inputs: <name>, <name>_rise, <name>_fall, <name>_seconds
  • "Switch3"
    • requires: "pin": { "a":<number>, "b":<number> }
    • provides inputs: <name>
  • "Encoder"
    • requires: "pin": { "a":<number>, "b":<number>, "click":<number> }
    • provides inputs: <name>, <name>_press, <name>_rise, <name>_fall, <name>_seconds
  • "Led"
    • requires: "pin": <number>,
    • provides outputs: <name>
  • "RgbLed"
    • requires: "pin": { "r":<number>, "g":<number>, "b":<number> },
    • provides outputs: <name>, <name>_red, <name>_green, <name>_blue, <name>_white
  • "GateOut"
    • requires: "pin": <number>,
    • provides outputs: <name>
  • "CVOuts" (a general purpose two-channel DAC)
    • no pins required,
    • provides outputs: <name>1, <name>2

More types will be added in the future.

name (optional): An optional name for the target. (Defaults to "custom")

defines (optional): Any C preprocessor #define values. For example, add "OOPSY_TARGET_HAS_MIDI_INPUT": 1 to ensure MIDI input handling code is included in the binary.

aliases (optional): To provide multiple alternate names that can map to the same component


## DEPRECATED INFORMATION BELOW

## This JSON structure is no longer supported since v0.4.0

## JSON configuration file

Minimal starting point:

{
	"defines": {
		"OOPSY_TARGET_SEED": 1
	},
	"labels": {
		"params": {},
		"outs": {},
		"datas": {}
	},
	"inputs": {},
	"outputs": {},
	"datahandlers": {},
	"inserts": [],
	"max_apps": 1
}

defines: Any C preprocessor #define values. For example, add "OOPSY_TARGET_HAS_MIDI_INPUT": 1 to ensure MIDI input handling code is included in the binary; "OOPSY_IO_COUNT": 4 if the device has 4x4 audio rather than the default 2x2 audio IO.

labels: These are the "magic" names that [param]s etc. will recognize, and should map to a string that exists as a key in the "inputs", "outputs" etc. sections of the JSON. More than one label can map to the same key.

~~inputs: These define how code is generated for control-rate inputs (knobs, cvs, gates, switches, etc.). Any feature marked "automap": true will be mapped to any otherwise unmapped [param] in the gen~ patch. The "code" section is a C/C++ statement to derive the control value from the Seed hardware.~~

~~outputs: These define how code is generated for non-audio [out] objects. The "code" section is a C/C++ statement to set the value on the Seed hardware; use $<name> to insert the gen~ variable value into the expression. The "where" key can define where this code should be inserted; use "audio" to place in the audio callback (e.g. gates, some LEDs) or "main" to place this in the main while loop (most other outputs).~~

datahandlers: These define how custom [data] objects can be used to map to hardware features. This is pretty experimental at the moment and is likely to change.

inserts: A way to insert arbitrary "code" into different sections of the template (according to the "where" tag). "where" can be

  • "header", (e.g. #include files)
  • "init", (for any code that needs to run at startup)
  • "main", (for any code that runs continuously in the main loop)
  • "display", (like "main", but only runs every 10ms)
  • "audio", (in audio interrupt, before audio processing)
  • "post_audio" (in audio interrupt, after audio processing)

max_apps: Assumes 1 if not defined. If greater than 1 this will enable multi-app binaries, but limits how many can be included.

Here is an example custom seed JSON config that has been used by a forum poster (note careful escaping of quotes):

{
  "defines": {
    "OOPSY_TARGET_SEED": 1
  },
  "labels": {
    "params": {
      "knob1": "kn1",
      "knob2": "kn2",
      "knob3": "kn3"
    },
    "outs": {},
    "datas": {}
  },
  "inputs": {
    "kn1": {
      "automap": true,
      "code": "hardware.seed.adc.GetFloat(0);"
    },
    "kn2": {
      "automap": true,
      "code": "hardware.seed.adc.GetFloat(1);"
    },
    "kn3": {
      "automap": true,
      "code": "hardware.seed.adc.GetFloat(2);"
    }
  },
  "outputs": {},
  "datahandlers": {},
  "inserts": [
    {
      "where": "init",
      "code": "AdcChannelConfig cfg[3];"
    },
    {
      "where": "init",
      "code": "cfg[0].InitSingle(oopsy::daisy.hardware.seed.GetPin(21));"
    },
    {
      "where": "init",
      "code": "cfg[1].InitSingle(oopsy::daisy.hardware.seed.GetPin(22));"
    },
    {
      "where": "init",
      "code": "cfg[2].InitSingle(oopsy::daisy.hardware.seed.GetPin(23));"
    },
    {
      "where": "init",
      "code": "oopsy::daisy.hardware.seed.adc.Init(cfg, 3);"
    },
    {
      "where": "header",
      "code": "#include \"daisy_seed.h\""
    },
    {
      "where": "header",
      "code": "using namespace daisy;"
    }
  ],
  "max_apps": 1
}
Clone this wiki locally