[{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/292/original/synthio_DC_DAC_test.png?1699238810","metadata":{}},{"element_type":"alert","content":"\u003cp\u003eBreaking News: The fix allowing negative LFO CV voltages is now part of CircuitPython 9.0.0-beta.2 and newer versions. Thanks @jepler!\u003c/p\u003e","metadata":{"class":"element row-fluid alert-element build-alert alert-info","markdown":"Breaking News: The fix allowing negative LFO CV voltages is now part of CircuitPython 9.0.0-beta.2 and newer versions. Thanks @jepler!","alert_type":"info"}},{"element_type":"text","content":"\n  \n  \n  \n        \u003cp\u003eThis note describes a method to output Eurorack CV (control voltage) signals from \u003cem\u003e\u003cstrong\u003eCircuitPython\u003c/strong\u003e\u003c/em\u003e \u003cstrong\u003e\u003cem\u003esynthio\u003c/em\u003e\u003c/strong\u003e using the PCM510x \u003ca href=\"https://adafruit-playground.com/u/CGrover/pages/pcm510xa-i2s-stereo-dac-breakout\" target=\"_blank\"\u003eI2S DAC.\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eRather than employing CV-like object controls such as \u003cem\u003eEnvelope\u003c/em\u003e and \u003cem\u003eLFO\u003c/em\u003e to only adjust the parameters of other \u003cem\u003esynthio\u003c/em\u003e objects, it would be useful to also control physically external devices such as the CV inputs of Eurorack modules. To do that we'll need to configure the \u003cem\u003esynthio.Note\u003c/em\u003e object to ignore its typical behavior as an oscillator. Oh, and it would be handy to have an I2S DAC on-hand with a DC-coupled output that's capable of positive and negative output voltage that can be connected to a Eurorack module.\u003c/p\u003e\n\u003cp\u003eHere's the test setup:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eCreate a \u003cem\u003eNote\u003c/em\u003e.\u003cem\u003ewaveform\u003c/em\u003e (wave shape table) object containing the maximum wave value (16-bit signed). This is an array filled with a single value that acts like a fixed DC voltage.\u003c/li\u003e\n\u003cli\u003eSet the \u003cem\u003eNote\u003c/em\u003e wave shape oscillator frequency to an arbitrary value such as 440Hz. The frequency value is unimportant since the oscillator waveform output will simply be a fixed value.\u003c/li\u003e\n\u003cli\u003eDefine a \u003cem\u003esynthio.LFO\u003c/em\u003e object to output the LFO signal. If outputting an ADSR envelope is desired, define a \u003cem\u003esynthio.Envelope\u003c/em\u003e object.\u003c/li\u003e\n\u003cli\u003eCreate a \u003cem\u003esynthio.Note\u003c/em\u003e object where the \u003cem\u003eamplitude\u003c/em\u003e parameter is controlled by the ADSR envelope or LFO.\u003c/li\u003e\n\u003cli\u003e\"Press\" the note to output the ADSR envelope or LFO signal from the I2S DAC.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eInstead of the I2S DAC, CV output signals can be created in this manner using \u003cem\u003eaudiopwmio\u003c/em\u003e and \u003cem\u003eaudioio\u003c/em\u003e to use PWM or analog DAC output pins. Boards like the \u003cem\u003eQT PY RP2040\u003c/em\u003e\u0026nbsp;and \u003cem\u003eGrand Central M4 Express\u003c/em\u003e\u0026nbsp;could be used for PWM or analog DAC outputs. Keep in mind that, unlike the 0 volt baseline of the I2S DAC, the baseline of a PWM or analog DAC signal is biased to approximately +1.65 volts.\u003c/p\u003e\n      \n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eExtend the CV Range with CircuitPython 9.0.0-beta.2\u003c/h3\u003e\n\u003cp\u003eThe current stable release of CircuitPython (8.x.x) will not permit the output of CV signals lower than the baseline voltage (0 volts for the I2S DAC, +1.65 volts for PWM and analog DAC). However, CircuitPython 9.0.0-beta.2 incorporates a fix that allows a negative \u003cem\u003eamplitude\u003c/em\u003e parameter value so that the CV output can reach below the baseline voltage. An added benefit of the fix is that a negative \u003cem\u003eamplitude\u003c/em\u003e value can also be used to invert the phase of a signal, just like when describing the gain of an operational amplifier; negative values are used to represent the gain of an inverting amplifier.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eCircuitPython Test Code\u003c/h3\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# SPDX-FileCopyrightText: 2023, 2024 JG for Cedar Grove Maker Studios\n# SPDX-License-Identifier: MIT\n\nimport time\nimport ulab.numpy as np\nimport board\nimport audiobusio\n\n# import audioio\n# import audiopwmio\nimport audiomixer\nimport synthio\n\nSAMPLE_RATE = 44100\nSAMPLE_SIZE = 256\nloudness = 1\nVOLUME = int(loudness * 32760)  # 0-32767 (signed 16-bit)\n\n# waveforms, envelopes and synth setup\nsine = np.array(\n    np.sin(np.linspace(0, 4 * np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME,\n    dtype=np.int16,\n)\ndc_max = np.array([VOLUME for i in range(SAMPLE_SIZE)], dtype=np.int16)\ndc_min = np.array([-VOLUME for i in range(SAMPLE_SIZE)], dtype=np.int16)\n\n\nlfo = synthio.LFO(rate=0.5, waveform=sine)\n\namp_env0 = synthio.Envelope(\n    attack_time=0.25, decay_time=1, release_time=0.2, attack_level=1, sustain_level=0.5\n)\n\nsynth = synthio.Synthesizer(sample_rate=SAMPLE_RATE)\n\n# ADSR envelope test (uncomment to test envelope output)\n# note_0 = synthio.Note(frequency=440, envelope=amp_env0, waveform=dc_max)\n\n# LFO test (uncomment to test LFO output)\nnote_0 = synthio.Note(frequency=440, amplitude=lfo, waveform=dc_max)\n\n# Instantiate output path (UM FeatherS2 pin assignments)\n# PCM510x I2S DAC\naudio = audiobusio.I2SOut(bit_clock=board.D12, word_select=board.D9, data=board.D6)\n\n# Analog DAC (board-dependent pin assignment)\n# audio = audioio.AudioOut(board.A0)\n\n# PWM pin (board-dependent pin assignment)\n# audio = audiopwmio(board.D13)\n\nmixer = audiomixer.Mixer(\n    voice_count=4,\n    sample_rate=SAMPLE_RATE,\n    channel_count=1,\n    bits_per_sample=16,\n    samples_signed=True,\n    buffer_size=2048,\n)\naudio.play(mixer)\n\nmixer.voice[0].play(synth)\nmixer.voice[0].level = 0.75\n\nprint(\"Test CV Output\")\nwhile True:\n    print(\"press note_0\", note_0)\n    synth.press(note_0)\n    time.sleep(10)  # 10s for LFO, 1s for ADSR envelope\n    print(\"release note_0\")\n    synth.release(note_0)\n    time.sleep(1)","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eI2S DAC Output\u003c/h3\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/721/original/LF0_ADSR_CV_test_2024-02-25.png?1708911496","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThe tests were successful and produced viable and useful CV outputs for controlling external Eurorack modules, albeit at a slightly lower input voltage value. Increasing the voltage can be easily accommodated by most modules or by passing the CV signal through a simple DC amplifier module. We're good to go!\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cpre\u003eAttribution: Patch Symbols from PATCH \u0026amp; TWEAK by Kim Bjørn and Chris Meyer, published by Bjooks, are licensed under Creative Commons CC BY-ND 4.0\u003c/pre\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/6250","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/6251","metadata":{}}]