Skip to content
Snippets Groups Projects
Commit a19d81aa authored by David Huss's avatar David Huss :speech_balloon:
Browse files

Basic midi functionality

parent a34b9654
No related branches found
No related tags found
No related merge requests found
%% Cell type:code id:52c8bec3-db2d-4522-b692-b035a71410de tags:
``` python
from matplotlib import pyplot as plt
%matplotlib inline
import math
!{sys.executable} -m pip install clipboard
%matplotlib inline
import clipboard
def draw(r, g, b):
x = [x for x in range(256)]
fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1])
ax.axhline(y=0.5, color='black', linestyle='--')
ax.set_xticks(range(0, 256, 64))
ax.set_yticks(range(-256*2, 256*2+1, 128))
ax.grid()
ax.plot(x, r, 'r')
ax.plot(x, g, 'g')
ax.plot(x, b, 'b')
def r():
start = 100
start2 = 220
last_a = 0
for i in range(256):
if i < start:
yield 0
elif i < start2:
span = 255-start
d = (i-start)/span
last_a = int(d*30.0)
yield min(255, last_a)
else:
span = 255-start2
d = (i-start2)/span
d = d*d*d
yield min(255, last_a + int(d*350.0))
def g():
start = 0
end = 180-80
scale = 0.25
for i in range(256):
if i < start:
yield 0
elif i > end:
d = 1.0 - ((i-end)/(295-end))
# d = (d*d)/4 + d/2
yield max(0, min(255, int(d*175*scale)))
else:
d = ((i-start)/(255-start))
d = (d*d*d)
yield min(255, int(d*2800*scale))
def b():
start = 4
end = 40
scale = 0.2
for i in range(256):
if i < start:
yield 0
elif i > end:
d = (i-end)/(60)
d = d*d
d = 1.0 - d
# d = math.sqrt(d)
yield max(0, min(255, int(d*32*scale)))
else:
d = (i-start)/(255-start)
d = math.sqrt(d)/2 + d/2
yield max(0, min(255, int(d*107*scale)))
r, g, b = list(r()), list(g()), list(b())
draw(r, g, b)
text = ""
text += "// Lookup Table for Red LED Channel\n"
text += f"int red_lookup[] = {{{', '.join(str(v) for v in r)}}};\n\n"
text += "// Lookup Table for Green LED Channel\n"
text += f"int green_lookup[] = {{{', '.join(str(v) for v in g)}}};\n\n"
text += "// Lookup Table for Blue LED Channel\n"
text += f"int blue_lookup[] = {{{', '.join(str(v) for v in b)}}};\n\n"
import ipywidgets as widgets
from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b):
print("copied")
clipboard.copy(text)
mybtn.on_click(mybtn_event_handler)
display(mybtn)
```
%% Output
zsh:1: parse error near `-m'
%% Cell type:code id:41562cc6-9911-4fb1-87ec-f2b3c8bfb3c2 tags:
``` python
from matplotlib import pyplot as plt
%matplotlib inline
import math
def draw(r):
l = len(r)
x = [x for x in range(l)]
fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1])
ax.axhline(y=0.5, color='black', linestyle='--')
ax.axvline(x=l/2, color='black', linestyle='--')
ax.set_xticks(range(0, l, 64))
ax.set_yticks(range(-l*2, l*2+1, 128))
ax.grid()
ax.plot(x, r)
def deadband(length, deadband=0.04):
readings = []
for i in range(length):
current_reading = i/(length-1)
scaler = (1.0) / (1.0 - deadband)
scaler += 0.1
if current_reading < 0.5:
current_reading += deadband
current_reading = min(0.5, current_reading)
current_reading = 0.5 - current_reading
current_reading *= scaler
current_reading = 0.5 - current_reading
# current_reading =
else:
current_reading -= deadband
current_reading = max(0.5, current_reading)
current_reading = 0.5 - current_reading
current_reading *= scaler
current_reading = 0.5 - current_reading
val = min(length, max(0, current_reading))
readings.append(val)
return readings
bip = deadband(16, deadband = 0.08)
draw(bip)
text = ""
text += "// Lookup Table for Bipolar Curve with deadband\n"
text += f"float bip_lookup[] = {{{', '.join(str(v) for v in bip)}}};\n\n"
import ipywidgets as widgets
from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b):
print("copied")
clipboard.copy(text)
mybtn.on_click(mybtn_event_handler)
display(mybtn)
len(bip)
```
%% Output
16
%% Cell type:code id:ecc666b0-8195-4276-a576-39d41753b540 tags:
``` python
from matplotlib import pyplot as plt
%matplotlib inline
import math
import sys
def draw(r):
l = len(r)
x = [x for x in range(l)]
fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1])
ax.axhline(y=0.5, color='black', linestyle='--')
ax.set_xticks(range(0, l, 64))
ax.set_yticks(range(-l*2, l*2+1, 128))
ax.grid()
ax.plot(x, r)
def lin_to_log(length, strength=1.0):
# Limit to range 0.0 and 1.0
strength = min(1.0, max(0.0, strength))
readings = []
linear_readings = []
for i in range(length):
current_reading = i/length
linear_readings.append(current_reading)
# Log of 0 is error, so handle it explicitly
if i == 0:
current_reading = 0.0
else:
current_reading = math.log10(i)
readings.append(current_reading)
# Normalize to scale 0.1 to one
maxima = max(readings)
scaler = 1.0 / maxima
readings = [r*scaler for r in readings]
output = []
for i, r in enumerate(readings):
val = r*strength + linear_readings[i] * (1.0 - strength)
output.append(val)
# Convert to integer value range
output = [o for o in output]
return output
# lilo = lin_to_log(4096, strength=1.0)
lilo = lin_to_log(32, strength=1.0)
# lilo = [l/256.0 for l in lilo]
draw(lilo)
text = ""
text += "// Lookup Table for Logarithmic Curve\n"
text += f"float log_lookup[] = {{{', '.join(str(v) for v in lilo)}}};\n\n"
# print(text)
import ipywidgets as widgets
from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b):
print("copied")
clipboard.copy(text)
mybtn.on_click(mybtn_event_handler)
display(mybtn)
min(lilo)
```
%% Output
0.0
%% Cell type:code id:2ecfda42-4a3c-489a-bc90-8576648c339c tags:
``` python
from matplotlib import pyplot as plt
%matplotlib inline
import math
import sys
def draw(r):
l = len(r)
x = [x for x in range(l)]
fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1])
ax.axhline(y=0.5, color='black', linestyle='--')
ax.set_xticks(range(0, l, 64))
ax.set_yticks(range(-l*2, l*2+1, 128))
ax.grid()
ax.plot(x, r)
def exp_lookup(length, strength=1.0):
# Limit to range 0.0 and 1.0
strength = min(1.0, max(0.0, strength))
readings = []
linear_readings = []
for i in range(length):
current_reading = i/length
linear_readings.append(current_reading)
# Log of 0 is error, so handle it explicitly
if i == 0:
current_reading = 0.0
else:
current_reading = i*i
readings.append(current_reading)
# Normalize to scale 0.1 to one
maxima = max(readings)
scaler = 1.0 / maxima
readings = [r*scaler for r in readings]
output = []
for i, r in enumerate(readings):
val = r*strength + linear_readings[i] * (1.0 - strength)
output.append(val)
# Convert to integer value range
output = [o for o in output]
return output
# lilo = lin_to_log(4096, strength=1.0)
lilo = exp_lookup(8, strength=1.0)
draw(lilo)
text = ""
text += "// Lookup Table for Exponential Curve\n"
text += f"float exp_lookup[] = {{{', '.join(str(v) for v in lilo)}}};\n\n"
# print(text)
import ipywidgets as widgets
from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b):
print("copied")
clipboard.copy(text)
mybtn.on_click(mybtn_event_handler)
display(mybtn)
max(lilo)
```
%% Output
0.9999999999999999
%% Cell type:code id:e51416f3-f34d-4513-9f0c-fa52c468274e tags:
``` python
from matplotlib import pyplot as plt
%matplotlib inline
from ipywidgets import interact, FloatSlider
import matplotlib.transforms as transforms
import math
def draw(f):
fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1])
b = scan2d(x, y, f)
ax.axhline(y=b, color='red', linestyle='--')
ax.axvline(x=f, color='red', linestyle='--')
trans = transforms.blended_transform_factory(
ax.get_yticklabels()[0].get_transform(), ax.transData)
ax.text(0.95, b, "{:.02f}".format(b), color="red", transform=trans, ha="right", va="bottom")
ax.grid()
ax.plot(x, y)
def lerp(a, b, f=0.5) -> float:
f = min(1.0, max(0.0, f))
if f == 0.0:
return a
elif f == 1.0:
return b
else:
return a * (1.0-f) + b * f
def lerp2d(x1, y1, x2, y2, f=0.5):
if f == 0.0:
return [x1, x2]
elif f == 1.0:
return [x1, x2]
else:
x = lerp(x1, x2, f)
y = lerp(y1, y2, f)
return [x, y]
# A function that scans through two lists representing x/y values using a
# third value called f and returns the linear interpolation between those points
def scan2d(x, y, f):
# f = min(1.0, max(0.0, f))
assert len(x) == len(y)
# Find ax and bx for given factor
xa = None
last_value = None
idx = None
for i, v in enumerate(x):
# this = abs(f-v)
this = f-v
if xa is None or this > 0:
xa = this
idx = i
idx2 = min(idx+1, len(x)-1)
if idx == idx2:
return y[idx]
xa = x[idx]
xb = x[idx2]
ya = y[idx]
yb = y[idx2]
xspan = xb-xa
xscaler = 1/xspan
new_f = (f-xa)*xscaler
return lerp(ya, yb, new_f)
lines_orig = [
[0.0, 0.0, -0.5],
[0.45, 0.5, 0.0],
[0.55, 0.5, 1.0],
[1.0, 1.0, 0.0],
]
half_deadband = 0.1
lines_orig = [
[-10.0, -10.0, 0.0],
[-10.0+half_deadband, -10.0, 0.0],
]
steps = list(range(-9, 11))
for i in steps:
f = float(i)
lines_orig.append([f-half_deadband, f, 0.0])
lines_orig.append([f+half_deadband, f, 0.0])
# Calculate curves for points of curvature
lines = []
for i, l in enumerate(lines_orig):
i2 = min(len(lines_orig)-1, i+1)
if l[2] == 0.0:
lines.append(l)
else:
xa = lines_orig[i][0]
xb = lines_orig[i2][0]
ya = lines_orig[i][1]
yb = lines_orig[i2][1]
x_span = xb-xa
y_span = yb-ya
x_step = 1/20
y_step = 1/20
for j in range(20):
x = x_step * j
y = y_step * j
y_curve = 0
if l[2] > 0.0:
y_curve = y*y*y
else:
y_curve = y*y
y = (1.0-l[2]) * y + l[2] * y_curve
lines.append([xa+x*x_span, ya+y*y_span, 0.0])
x = [a[0] for a in lines]
y = [a[1] for a in lines]
c = [a[2] for a in lines]
# draw(x, y, 0.45/2)
interact(draw, f=FloatSlider(min=min(x), max=max(x), step=0.001, value=0.2))
```
%% Output
<function __main__.draw(f)>
%% Cell type:code id:f35f1609-3a10-4dce-b7dd-201d79f2c39c tags:
``` python
# Saturation curve
# X / Y / Curvature
lines_orig = [
[-1.5, -1.0, 1.0],
[-0.7, -0.7, 0.0],
[0.0, 0.0, 0.0],
[0.7, 0.7, -1.4],
[1.5, 1.0, 0.0],
]
lines = []
for i, l in enumerate(lines_orig):
i2 = min(len(lines_orig)-1, i+1)
if l[2] == 0.0:
lines.append(l)
else:
xa = lines_orig[i][0]
xb = lines_orig[i2][0]
ya = lines_orig[i][1]
yb = lines_orig[i2][1]
x_span = xb-xa
y_span = yb-ya
x_step = 1/20
y_step = 1/20
for j in range(20):
x = x_step * j
y = y_step * j
y_curve = 0
if l[2] > 0.0:
y_curve = y*y*y
else:
y_curve = y*y
y = (1.0-l[2]) * y + l[2] * y_curve
lines.append([xa+x*x_span, ya+y*y_span, 0.0])
x = [a[0] for a in lines]
y = [a[1] for a in lines]
c = [a[2] for a in lines]
# draw(x, y, 0.45/2)
interact(draw, f=FloatSlider(min=min(x), max=max(x), step=0.001, value=0.2))
```
%% Output
<function __main__.draw(f)>
%% Cell type:code id:f0cd9d06-a15a-46b4-a3ef-4aee3d4f7cd0 tags:
``` python
# Midi to pitch
notes = [36, 48, 60]
for note in notes:
print((note-36)/12.0)
```
%% Output
0.0
1.0
2.0
%% Cell type:code id:59b56dbc-6852-4989-bc19-3525ee7caf8b tags:
``` python
```
......
......@@ -11,6 +11,7 @@
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <CpuLoadMeter.h>
#include <MIDI.h>
#include "leds.h"
#include "potentiometers.h"
......@@ -20,7 +21,7 @@
#include "helpers.h"
#include "ui.h"
MIDI_CREATE_DEFAULT_INSTANCE();
#define BUFFER_LENGTH_SECONDS 5
static const size_t buffer_length = 48000 * BUFFER_LENGTH_SECONDS;
......@@ -43,7 +44,7 @@ Button button_2 = Button(D8);
Button button_3 = Button(D9);
Button button_4 = Button(D10);
Button button_5 = Button(D13);
Button button_6 = Button(D14);
Button button_6 = Button(D27);
// Initialize Potentiometers
Potentiometer pot_1 = Potentiometer(A0);
......@@ -80,6 +81,7 @@ float delaytime = 100.0f;
float reverbmix = 0.0f;
float lfo_amount = 0.0f;
float pitch_val = 0.5f;
float midi_pitch_offset = 0.0f;
// Actual audio-processing is orchestrated here
void AudioCallback(float **in, float **out, size_t size) {
......@@ -105,13 +107,13 @@ void AudioCallback(float **in, float **out, size_t size) {
// If the dial is over 50% jump instead
if (lfo_amount > 0.5f) {
looper.addToPlayhead(rand * random_amount* 48000.0f);
looper.setPlaybackSpeed(pitch_val);
looper.setPlaybackSpeed(pitch_val + midi_pitch_offset);
}
}
if (lfo_amount > 0.5f) {
looper.setPlaybackSpeed(pitch_val);
looper.setPlaybackSpeed(pitch_val + midi_pitch_offset);
} else {
looper.setPlaybackSpeed(pitch_val + lfo_value * lfo_amount);
looper.setPlaybackSpeed(pitch_val + lfo_value * lfo_amount + midi_pitch_offset);
}
float looper_out;
......@@ -157,6 +159,28 @@ void AudioCallback(float **in, float **out, size_t size) {
}
}
// TODO: Add Voice Stealing/Multiple Playheads?
// TODO: Is USB Midi possible? https://github.com/electro-smith/DaisyExamples/blob/master/seed/USB_MIDI/USB_MIDI.cpp
void handleNoteOn(byte inChannel, byte inNote, byte inVelocity) {
Serial.print("Note On: ");
Serial.println(inNote);
// Note Off can come in as Note On w/ 0 Velocity
if (inVelocity == 0.f) {
midi_pitch_offset = 0.0f;
}
else {
midi_pitch_offset = (int(inNote)-36.0)/12.0f;
}
}
void handleNoteOff(byte inChannel, byte inNote, byte inVelocity) {
Serial.print("Note Off: ");
Serial.println(inNote);
midi_pitch_offset = 0.0f;
}
......@@ -217,6 +241,11 @@ void setup() {
// Set the analog read and write resolution to 12 bits
analogReadResolution(12);
//
MIDI.setHandleNoteOn(handleNoteOn);
MIDI.setHandleNoteOff(handleNoteOff);
MIDI.begin(MIDI_CHANNEL_OMNI); // Listen to all incoming messages
// Set Knob names and display functions
pot_1.name = "Start";
pot_2.name = "Length";
......@@ -283,6 +312,9 @@ void loop() {
// Set the Color and brightness of the RGB LED in 8 bits
rgb_led.setAudioLevelIndicator(int(input_envelope_follower.getValue() * 255));
// MIDI
MIDI.read();
}
......
......@@ -326,7 +326,7 @@
"net": "2185e7f6-a495-4624-9096-20ddaa5c0580"
},
"e098cbdf-50ef-4243-b379-8fb08f979275/067d51b3-ef03-4f9c-a32e-0738642ce1c2": {
"net": "04129d36-c709-4a56-a7ee-ee2bb18553c9"
"net": "565d0d0a-8ff2-4a59-834b-57f224ced232"
},
"e098cbdf-50ef-4243-b379-8fb08f979275/0e0ab734-776c-4fc0-aacc-4638e57ed246": {
"net": "e149f05c-07b2-4770-aff0-a9163db00bf6"
......@@ -337,9 +337,6 @@
"e098cbdf-50ef-4243-b379-8fb08f979275/22650871-f429-4723-b648-509abbdba4f3": {
"net": "8b8d6956-28eb-4fc1-b988-cece63ad99a7"
},
"e098cbdf-50ef-4243-b379-8fb08f979275/2b5c4342-82e9-41c9-a8c1-431823790a61": {
"net": "9b161be3-8ce9-4f24-9cae-5d5ace7db3f7"
},
"e098cbdf-50ef-4243-b379-8fb08f979275/2cfa56c9-6838-4f0e-9459-d0560ce71518": {
"net": "a3a8239f-f0b7-458a-8324-136070f8be77"
},
......@@ -389,7 +386,7 @@
"net": "04a96525-5195-41ce-a259-17c102c2e743"
},
"e098cbdf-50ef-4243-b379-8fb08f979275/c7861f7d-db74-42b2-80ee-5277075ebf57": {
"net": "565d0d0a-8ff2-4a59-834b-57f224ced232"
"net": "04129d36-c709-4a56-a7ee-ee2bb18553c9"
},
"e098cbdf-50ef-4243-b379-8fb08f979275/d618345a-d484-4861-8fa8-336a0e07fc0a": {
"net": "32687c38-bbf6-41ca-a65f-3b1211b52c7f"
......@@ -554,7 +551,7 @@
"alt_pins": {},
"connections": {
"eb7f68b1-6fba-4a35-a470-2cecb23fe087/588536f1-9e96-4500-bcbb-4ba48082b643": {
"net": "6ab0ab61-ac50-44e2-a7ff-24876bdce251"
"net": "3463744f-816e-4bb0-bd10-205313de2731"
},
"eb7f68b1-6fba-4a35-a470-2cecb23fe087/b3cef4f5-fe6c-45b5-b063-1618f997168f": {
"net": "2aafef96-ebb9-4f7a-818d-d2324990e1f7"
......@@ -938,10 +935,10 @@
"alt_pins": {},
"connections": {
"e5da453c-ba97-4fa0-8e05-5f6b4e026f87/16ee5a5c-8eea-4447-ad48-04d973776e3a": {
"net": "6ab0ab61-ac50-44e2-a7ff-24876bdce251"
"net": "f4eae880-b8bf-4d7c-98ba-b6681c4ef628"
},
"e5da453c-ba97-4fa0-8e05-5f6b4e026f87/b57ca2a1-1bbf-43f0-b0a0-bf953094a8de": {
"net": "6eafcae1-05a0-42b3-baf1-4b3be74b0033"
"net": "3463744f-816e-4bb0-bd10-205313de2731"
}
},
"entity": "c44a773c-cdf5-4193-b5be-ab54952f3f28",
......@@ -1125,7 +1122,7 @@
"net": "2aafef96-ebb9-4f7a-818d-d2324990e1f7"
},
"48af78e5-730e-44eb-977a-cf188e4fcb39/dda0ec99-5d70-4fbe-a8db-897542e7554e": {
"net": "6eafcae1-05a0-42b3-baf1-4b3be74b0033"
"net": "f4eae880-b8bf-4d7c-98ba-b6681c4ef628"
}
},
"entity": "3ce770af-7be0-4f72-b2af-2c8010911aaf",
......@@ -1224,10 +1221,10 @@
"net": "a02d443f-8de2-4573-825d-60a4607f56f8"
},
"4778d1df-df5d-4710-a422-83e88d006a3c/49aea420-016d-4ba2-9451-5996acefec56": {
"net": "6eafcae1-05a0-42b3-baf1-4b3be74b0033"
"net": "f4eae880-b8bf-4d7c-98ba-b6681c4ef628"
},
"4778d1df-df5d-4710-a422-83e88d006a3c/5172e1fe-e3f0-43e0-9863-465c59f46ab7": {
"net": "6ab0ab61-ac50-44e2-a7ff-24876bdce251"
"net": "3463744f-816e-4bb0-bd10-205313de2731"
},
"4778d1df-df5d-4710-a422-83e88d006a3c/a1754538-4d10-4c86-a7a0-7c17d15ba7e8": {
"net": "cd66fb2a-28ed-4b48-94e9-06544a645f40"
......@@ -1300,24 +1297,6 @@
"refdes": "R5",
"tag": "7416c727-3b25-46b1-8f80-7f3f4f8e3987",
"value": ""
},
"ff689786-fa22-4d0f-b29c-85939ed2ff75": {
"alt_pins": {},
"connections": {
"2e41400f-138b-4ab4-9ba7-c3255ff0adbd/5dd0f83d-34b2-41a1-ae35-435479e4002e": {
"net": "a02d443f-8de2-4573-825d-60a4607f56f8"
},
"2e41400f-138b-4ab4-9ba7-c3255ff0adbd/831b482b-82af-40ba-8cc0-5e272f25cc2c": {
"net": "9b161be3-8ce9-4f24-9cae-5d5ace7db3f7"
}
},
"entity": "8676e35a-3078-4165-8014-8efdbc6c4bc1",
"group": "00000000-0000-0000-0000-000000000000",
"part": "16758ba5-8e81-4ea7-8183-74aeb6ca95ab",
"pin_names": {},
"refdes": "S7",
"tag": "7310bc96-4e80-4645-a9b7-00b967c0436d",
"value": ""
}
},
"group_names": {},
......@@ -1470,6 +1449,15 @@
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
},
"3463744f-816e-4bb0-bd10-205313de2731": {
"is_port": false,
"is_power": false,
"name": "",
"net_class": "d3cde4be-ff63-4cf9-bc82-2965b938b344",
"port_direction": "bidirectional",
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
},
"35318027-7725-469c-bfe6-a1fcce7f63ef": {
"is_port": false,
"is_power": false,
......@@ -1533,24 +1521,6 @@
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
},
"6ab0ab61-ac50-44e2-a7ff-24876bdce251": {
"is_port": false,
"is_power": false,
"name": "",
"net_class": "d3cde4be-ff63-4cf9-bc82-2965b938b344",
"port_direction": "bidirectional",
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
},
"6eafcae1-05a0-42b3-baf1-4b3be74b0033": {
"is_port": false,
"is_power": false,
"name": "",
"net_class": "d3cde4be-ff63-4cf9-bc82-2965b938b344",
"port_direction": "bidirectional",
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
},
"6f86e55e-8dd3-485c-b17a-2195abe85f7a": {
"is_port": false,
"is_power": false,
......@@ -1606,15 +1576,6 @@
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
},
"9b161be3-8ce9-4f24-9cae-5d5ace7db3f7": {
"is_port": false,
"is_power": false,
"name": "sw1",
"net_class": "d3cde4be-ff63-4cf9-bc82-2965b938b344",
"port_direction": "bidirectional",
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
},
"9d3b8d51-9462-4431-b0fa-bf6eb832d214": {
"is_port": false,
"is_power": false,
......@@ -1839,6 +1800,15 @@
"port_direction": "bidirectional",
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
},
"f4eae880-b8bf-4d7c-98ba-b6681c4ef628": {
"is_port": false,
"is_power": false,
"name": "",
"net_class": "d3cde4be-ff63-4cf9-bc82-2965b938b344",
"port_direction": "bidirectional",
"power_symbol_name_visible": true,
"power_symbol_style": "gnd"
}
},
"project_meta": {
......@@ -1874,7 +1844,6 @@
"6867db91-5c46-4d55-b783-b4f6f5d6dca2": "40",
"6d347ac8-118a-4818-a689-f8e2ab5b02a2": "45",
"6e71e1fa-3a90-4a42-95fb-6d0b5e964d4a": "26",
"7310bc96-4e80-4645-a9b7-00b967c0436d": "11",
"7416c727-3b25-46b1-8f80-7f3f4f8e3987": "27",
"76d929d5-5eca-47ac-abaf-e320e60981a9": "4",
"77075bed-79c4-4012-9c6f-625e01438759": "17",
......
This diff is collapsed.
images/schematic-1.jpg

202 KiB | W: | H:

images/schematic-1.jpg

197 KiB | W: | H:

images/schematic-1.jpg
images/schematic-1.jpg
images/schematic-1.jpg
images/schematic-1.jpg
  • 2-up
  • Swipe
  • Onion skin
No preview for this file type
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment