concertina

Elecronic concertina
git clone https://git.woozle.org/neale/concertina.git

commit
01b9509
parent
823b4c6
author
Neale Pickett
date
2026-02-24 20:22:31 -0700 MST
pre-generate layouts structure
7 files changed,  +150, -413
M Cargo.lock
+9, -390
  1@@ -2,17 +2,6 @@
  2 # It is not intended for manual editing.
  3 version = 4
  4 
  5-[[package]]
  6-name = "aes"
  7-version = "0.8.4"
  8-source = "registry+https://github.com/rust-lang/crates.io-index"
  9-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
 10-dependencies = [
 11- "cfg-if",
 12- "cipher",
 13- "cpufeatures",
 14-]
 15-
 16 [[package]]
 17 name = "atsamd-hal"
 18 version = "0.14.0"
 19@@ -21,66 +10,21 @@ checksum = "1cddc374559f476b8225083732b400544075c67616e1b1e1635b0efe84bef158"
 20 dependencies = [
 21  "atsamd21e",
 22  "bitfield",
 23- "bitflags 1.3.2",
 24+ "bitflags",
 25  "cortex-m",
 26- "embedded-hal 0.2.7",
 27+ "embedded-hal",
 28  "modular-bitfield",
 29  "nb 0.1.3",
 30  "num-traits",
 31  "paste",
 32- "rand_core 0.5.1",
 33+ "rand_core",
 34  "replace_with",
 35- "seq-macro 0.2.2",
 36+ "seq-macro",
 37  "typenum",
 38- "usb-device",
 39  "vcell",
 40  "void",
 41 ]
 42 
 43-[[package]]
 44-name = "atsamd-hal"
 45-version = "0.23.2"
 46-source = "registry+https://github.com/rust-lang/crates.io-index"
 47-checksum = "15ecba6d9da0e33fe34876782b771b0777d2515eab00da77fce41fa8824cfda9"
 48-dependencies = [
 49- "aes",
 50- "atsamd-hal-macros",
 51- "bitfield",
 52- "bitflags 2.11.0",
 53- "cipher",
 54- "cortex-m",
 55- "critical-section",
 56- "embedded-hal 0.2.7",
 57- "embedded-hal 1.0.0",
 58- "embedded-hal-nb",
 59- "embedded-io",
 60- "fugit",
 61- "heapless",
 62- "modular-bitfield",
 63- "nb 1.1.0",
 64- "num-traits",
 65- "opaque-debug",
 66- "paste",
 67- "rand_core 0.9.5",
 68- "seq-macro 0.3.6",
 69- "sorted-hlist",
 70- "typenum",
 71- "void",
 72-]
 73-
 74-[[package]]
 75-name = "atsamd-hal-macros"
 76-version = "0.3.0"
 77-source = "registry+https://github.com/rust-lang/crates.io-index"
 78-checksum = "ae9d1e473cf24a5deb7966c2e2559e0d739ca7734c0f1b9a058ef31e8188e9f4"
 79-dependencies = [
 80- "litrs",
 81- "phf",
 82- "phf_codegen",
 83- "serde",
 84- "serde_yaml",
 85-]
 86-
 87 [[package]]
 88 name = "atsamd21e"
 89 version = "0.11.0"
 90@@ -120,46 +64,17 @@ version = "1.3.2"
 91 source = "registry+https://github.com/rust-lang/crates.io-index"
 92 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 93 
 94-[[package]]
 95-name = "bitflags"
 96-version = "2.11.0"
 97-source = "registry+https://github.com/rust-lang/crates.io-index"
 98-checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
 99-
100 [[package]]
101 name = "bytemuck"
102 version = "1.25.0"
103 source = "registry+https://github.com/rust-lang/crates.io-index"
104 checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
105 
106-[[package]]
107-name = "byteorder"
108-version = "1.5.0"
109-source = "registry+https://github.com/rust-lang/crates.io-index"
110-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
111-
112-[[package]]
113-name = "cfg-if"
114-version = "1.0.4"
115-source = "registry+https://github.com/rust-lang/crates.io-index"
116-checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
117-
118-[[package]]
119-name = "cipher"
120-version = "0.4.4"
121-source = "registry+https://github.com/rust-lang/crates.io-index"
122-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
123-dependencies = [
124- "crypto-common",
125- "inout",
126-]
127-
128 [[package]]
129 name = "concertina"
130 version = "0.1.0"
131 dependencies = [
132- "atsamd-hal 0.14.0",
133- "atsamd-hal 0.23.2",
134+ "atsamd-hal",
135  "cortex-m",
136  "cortex-m-rt",
137  "panic-halt",
138@@ -178,7 +93,7 @@ checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
139 dependencies = [
140  "bare-metal",
141  "bitfield",
142- "embedded-hal 0.2.7",
143+ "embedded-hal",
144  "volatile-register",
145 ]
146 
147@@ -202,31 +117,6 @@ dependencies = [
148  "syn 2.0.117",
149 ]
150 
151-[[package]]
152-name = "cpufeatures"
153-version = "0.2.17"
154-source = "registry+https://github.com/rust-lang/crates.io-index"
155-checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
156-dependencies = [
157- "libc",
158-]
159-
160-[[package]]
161-name = "critical-section"
162-version = "1.2.0"
163-source = "registry+https://github.com/rust-lang/crates.io-index"
164-checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
165-
166-[[package]]
167-name = "crypto-common"
168-version = "0.1.7"
169-source = "registry+https://github.com/rust-lang/crates.io-index"
170-checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
171-dependencies = [
172- "generic-array",
173- "typenum",
174-]
175-
176 [[package]]
177 name = "embedded-hal"
178 version = "0.2.7"
179@@ -237,124 +127,6 @@ dependencies = [
180  "void",
181 ]
182 
183-[[package]]
184-name = "embedded-hal"
185-version = "1.0.0"
186-source = "registry+https://github.com/rust-lang/crates.io-index"
187-checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
188-
189-[[package]]
190-name = "embedded-hal-nb"
191-version = "1.0.0"
192-source = "registry+https://github.com/rust-lang/crates.io-index"
193-checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
194-dependencies = [
195- "embedded-hal 1.0.0",
196- "nb 1.1.0",
197-]
198-
199-[[package]]
200-name = "embedded-io"
201-version = "0.6.1"
202-source = "registry+https://github.com/rust-lang/crates.io-index"
203-checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
204-
205-[[package]]
206-name = "equivalent"
207-version = "1.0.2"
208-source = "registry+https://github.com/rust-lang/crates.io-index"
209-checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
210-
211-[[package]]
212-name = "fugit"
213-version = "0.3.9"
214-source = "registry+https://github.com/rust-lang/crates.io-index"
215-checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6"
216-dependencies = [
217- "gcd",
218-]
219-
220-[[package]]
221-name = "gcd"
222-version = "2.3.0"
223-source = "registry+https://github.com/rust-lang/crates.io-index"
224-checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
225-
226-[[package]]
227-name = "generic-array"
228-version = "0.14.7"
229-source = "registry+https://github.com/rust-lang/crates.io-index"
230-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
231-dependencies = [
232- "typenum",
233- "version_check",
234-]
235-
236-[[package]]
237-name = "hash32"
238-version = "0.3.1"
239-source = "registry+https://github.com/rust-lang/crates.io-index"
240-checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
241-dependencies = [
242- "byteorder",
243-]
244-
245-[[package]]
246-name = "hashbrown"
247-version = "0.16.1"
248-source = "registry+https://github.com/rust-lang/crates.io-index"
249-checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
250-
251-[[package]]
252-name = "heapless"
253-version = "0.8.0"
254-source = "registry+https://github.com/rust-lang/crates.io-index"
255-checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
256-dependencies = [
257- "hash32",
258- "stable_deref_trait",
259-]
260-
261-[[package]]
262-name = "indexmap"
263-version = "2.13.0"
264-source = "registry+https://github.com/rust-lang/crates.io-index"
265-checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
266-dependencies = [
267- "equivalent",
268- "hashbrown",
269-]
270-
271-[[package]]
272-name = "inout"
273-version = "0.1.4"
274-source = "registry+https://github.com/rust-lang/crates.io-index"
275-checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
276-dependencies = [
277- "generic-array",
278-]
279-
280-[[package]]
281-name = "itoa"
282-version = "1.0.17"
283-source = "registry+https://github.com/rust-lang/crates.io-index"
284-checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
285-
286-[[package]]
287-name = "libc"
288-version = "0.2.182"
289-source = "registry+https://github.com/rust-lang/crates.io-index"
290-checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
291-
292-[[package]]
293-name = "litrs"
294-version = "0.4.2"
295-source = "registry+https://github.com/rust-lang/crates.io-index"
296-checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
297-dependencies = [
298- "proc-macro2",
299-]
300-
301 [[package]]
302 name = "modular-bitfield"
303 version = "0.11.2"
304@@ -400,12 +172,6 @@ dependencies = [
305  "autocfg",
306 ]
307 
308-[[package]]
309-name = "opaque-debug"
310-version = "0.3.1"
311-source = "registry+https://github.com/rust-lang/crates.io-index"
312-checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
313-
314 [[package]]
315 name = "panic-halt"
316 version = "0.2.0"
317@@ -418,44 +184,6 @@ version = "1.0.15"
318 source = "registry+https://github.com/rust-lang/crates.io-index"
319 checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
320 
321-[[package]]
322-name = "phf"
323-version = "0.11.3"
324-source = "registry+https://github.com/rust-lang/crates.io-index"
325-checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
326-dependencies = [
327- "phf_shared",
328-]
329-
330-[[package]]
331-name = "phf_codegen"
332-version = "0.11.3"
333-source = "registry+https://github.com/rust-lang/crates.io-index"
334-checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
335-dependencies = [
336- "phf_generator",
337- "phf_shared",
338-]
339-
340-[[package]]
341-name = "phf_generator"
342-version = "0.11.3"
343-source = "registry+https://github.com/rust-lang/crates.io-index"
344-checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
345-dependencies = [
346- "phf_shared",
347- "rand",
348-]
349-
350-[[package]]
351-name = "phf_shared"
352-version = "0.11.3"
353-source = "registry+https://github.com/rust-lang/crates.io-index"
354-checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
355-dependencies = [
356- "siphasher",
357-]
358-
359 [[package]]
360 name = "proc-macro2"
361 version = "1.0.106"
362@@ -471,7 +199,7 @@ version = "0.10.1"
363 source = "registry+https://github.com/rust-lang/crates.io-index"
364 checksum = "0a56e549059a2819576ebd50ab61ab9a289a2f22ce8c536d8e613daff887b0d3"
365 dependencies = [
366- "atsamd-hal 0.14.0",
367+ "atsamd-hal",
368  "cortex-m-rt",
369 ]
370 
371@@ -484,33 +212,12 @@ dependencies = [
372  "proc-macro2",
373 ]
374 
375-[[package]]
376-name = "rand"
377-version = "0.8.5"
378-source = "registry+https://github.com/rust-lang/crates.io-index"
379-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
380-dependencies = [
381- "rand_core 0.6.4",
382-]
383-
384 [[package]]
385 name = "rand_core"
386 version = "0.5.1"
387 source = "registry+https://github.com/rust-lang/crates.io-index"
388 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
389 
390-[[package]]
391-name = "rand_core"
392-version = "0.6.4"
393-source = "registry+https://github.com/rust-lang/crates.io-index"
394-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
395-
396-[[package]]
397-name = "rand_core"
398-version = "0.9.5"
399-source = "registry+https://github.com/rust-lang/crates.io-index"
400-checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
401-
402 [[package]]
403 name = "replace_with"
404 version = "0.1.8"
405@@ -535,12 +242,6 @@ dependencies = [
406  "semver",
407 ]
408 
409-[[package]]
410-name = "ryu"
411-version = "1.0.23"
412-source = "registry+https://github.com/rust-lang/crates.io-index"
413-checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
414-
415 [[package]]
416 name = "semver"
417 version = "0.9.0"
418@@ -562,61 +263,6 @@ version = "0.2.2"
419 source = "registry+https://github.com/rust-lang/crates.io-index"
420 checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99"
421 
422-[[package]]
423-name = "seq-macro"
424-version = "0.3.6"
425-source = "registry+https://github.com/rust-lang/crates.io-index"
426-checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
427-
428-[[package]]
429-name = "serde"
430-version = "1.0.228"
431-source = "registry+https://github.com/rust-lang/crates.io-index"
432-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
433-dependencies = [
434- "serde_core",
435- "serde_derive",
436-]
437-
438-[[package]]
439-name = "serde_core"
440-version = "1.0.228"
441-source = "registry+https://github.com/rust-lang/crates.io-index"
442-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
443-dependencies = [
444- "serde_derive",
445-]
446-
447-[[package]]
448-name = "serde_derive"
449-version = "1.0.228"
450-source = "registry+https://github.com/rust-lang/crates.io-index"
451-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
452-dependencies = [
453- "proc-macro2",
454- "quote",
455- "syn 2.0.117",
456-]
457-
458-[[package]]
459-name = "serde_yaml"
460-version = "0.9.34+deprecated"
461-source = "registry+https://github.com/rust-lang/crates.io-index"
462-checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
463-dependencies = [
464- "indexmap",
465- "itoa",
466- "ryu",
467- "serde",
468- "unsafe-libyaml",
469-]
470-
471-[[package]]
472-name = "siphasher"
473-version = "1.0.2"
474-source = "registry+https://github.com/rust-lang/crates.io-index"
475-checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
476-
477 [[package]]
478 name = "smart-leds"
479 version = "0.3.0"
480@@ -635,21 +281,6 @@ dependencies = [
481  "rgb",
482 ]
483 
484-[[package]]
485-name = "sorted-hlist"
486-version = "0.2.0"
487-source = "registry+https://github.com/rust-lang/crates.io-index"
488-checksum = "e3b3c9d254b6644002aecb837882062fbca9af420d2cd4c2f0e4da248d80571b"
489-dependencies = [
490- "typenum",
491-]
492-
493-[[package]]
494-name = "stable_deref_trait"
495-version = "1.2.1"
496-source = "registry+https://github.com/rust-lang/crates.io-index"
497-checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
498-
499 [[package]]
500 name = "static_assertions"
501 version = "1.1.0"
502@@ -690,12 +321,6 @@ version = "1.0.24"
503 source = "registry+https://github.com/rust-lang/crates.io-index"
504 checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
505 
506-[[package]]
507-name = "unsafe-libyaml"
508-version = "0.2.11"
509-source = "registry+https://github.com/rust-lang/crates.io-index"
510-checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
511-
512 [[package]]
513 name = "usb-device"
514 version = "0.2.9"
515@@ -708,7 +333,7 @@ version = "0.1.1"
516 source = "registry+https://github.com/rust-lang/crates.io-index"
517 checksum = "db75519b86287f12dcf0d171c7cf4ecc839149fe9f3b720ac4cfce52959e1dfe"
518 dependencies = [
519- "embedded-hal 0.2.7",
520+ "embedded-hal",
521  "nb 0.1.3",
522  "usb-device",
523 ]
524@@ -719,12 +344,6 @@ version = "0.1.3"
525 source = "registry+https://github.com/rust-lang/crates.io-index"
526 checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
527 
528-[[package]]
529-name = "version_check"
530-version = "0.9.5"
531-source = "registry+https://github.com/rust-lang/crates.io-index"
532-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
533-
534 [[package]]
535 name = "void"
536 version = "1.0.2"
537@@ -746,7 +365,7 @@ version = "0.3.0"
538 source = "registry+https://github.com/rust-lang/crates.io-index"
539 checksum = "b9b339c25c6d44131844c4c5ac0c7344d32080daaa38a6e9beaba927cbe28800"
540 dependencies = [
541- "embedded-hal 0.2.7",
542+ "embedded-hal",
543  "nb 0.1.3",
544  "smart-leds-trait",
545 ]
M Cargo.toml
+2, -3
 1@@ -6,7 +6,6 @@ edition = "2024"
 2 [dependencies]
 3 cortex-m-rt = { version = "0.7", optional = true }
 4 usb-device = { version = "0.2", optional = true }
 5-atsamd-hal-new = { version = "0.23.2", package = "atsamd-hal" }
 6 atsamd-hal = { version = "0.14" }
 7 smart-leds = "0.3"
 8 usbd-serial = "0.1"
 9@@ -18,5 +17,5 @@ qt_py_m0 = "0.10.1"
10 [features]
11 default = ["rt", "atsamd-hal/samd21e"]
12 rt = ["cortex-m-rt", "atsamd-hal/samd21e-rt"]
13-use_semihosting = []
14-usb = ["atsamd-hal/usb", "usb-device"]
15+#use_semihosting = []
16+#usb = ["atsamd-hal/usb", "usb-device"]
M Makefile
+3, -20
 1@@ -1,21 +1,4 @@
 2-flash: flash-qtpy
 3-flash-qtpy: build/qtpy/concertina.bin
 4-	stty -F /dev/ttyACM0 1200 && sleep 1 # Reset into bootloader
 5-	bossac --offset=0x2000 -w -v $< -R
 6+all: flash
 7 
 8-build: build/qtpy/concertina.bin
 9-
10-clean:
11-	rm -rf build/*
12-
13-build/%/concertina.bin: cmd/concertina
14-	tinygo build -o $@ -target=$* ./$</...
15-
16-
17-flash-test: flash-qtpy-test
18-flash-qtpy-test: build/qtpy/bellows-test.bin
19-	stty -F /dev/ttyACM0 1200 && sleep 1 # Reset into bootloader
20-	bossac --offset=0x2000 -w -v $< -R
21-
22-build/%/bellows-test.bin: cmd/bellows-test
23-	tinygo build -o $@ -target=$* ./$</...
24+flash:
25+	cargo hf2 --release
A build.rs
+104, -0
  1@@ -0,0 +1,104 @@
  2+use std::env;
  3+use std::fs;
  4+use std::path::Path;
  5+
  6+fn abc_to_midi(note: &str) -> i8 {
  7+    if note == "-" || note.is_empty() { return -1; }
  8+
  9+    let mut chars = note.chars();
 10+    let first = chars.next().unwrap();
 11+    
 12+    let mut val: i8 = match first {
 13+        'C' => 60, 'D' => 62, 'E' => 64, 'F' => 65, 'G' => 67, 'A' => 69, 'B' => 71,
 14+        'c' => 72, 'd' => 74, 'e' => 76, 'f' => 77, 'g' => 79, 'a' => 81, 'b' => 83,
 15+        _ => return -1,
 16+    };
 17+
 18+    for m in chars {
 19+        match m {
 20+            '♯' | '^' => val += 1,
 21+            '♭' | '_' => val -= 1,
 22+            '\'' => val += 12,
 23+            ',' => val -= 12,
 24+            _ => return -1,
 25+        }
 26+    }
 27+    val
 28+}
 29+
 30+fn main() {
 31+    let input = fs::read_to_string("src/layouts.txt").expect("Could not read layouts.txt");
 32+    let mut layouts_code = String::from("pub static LAYOUTS: &[Layout] = &[\n");
 33+    
 34+    let mut current_notes = [[[[ -1i8; 2]; 5]; 4]; 2];
 35+    let mut current_name = String::new();
 36+    let mut row_idx = 0;
 37+
 38+    for line in input.lines() {
 39+        let line = line.trim();
 40+        if line.is_empty() || line.starts_with('#') { continue; }
 41+
 42+        let fields: Vec<&str> = line.split_whitespace().collect();
 43+
 44+        // Check if this is a header or a data row
 45+        if fields.len() != 11 || fields[5] != "|" {
 46+            // If we were processing a previous layout, close it
 47+            if !current_name.is_empty() {
 48+                write_layout(&mut layouts_code, &current_name, &current_notes);
 49+            }
 50+            current_name = line.to_string();
 51+            current_notes = [[[[ -1i8; 2]; 5]; 4]; 2];
 52+            row_idx = 0;
 53+            continue;
 54+        }
 55+
 56+        // Parse Row
 57+        let mut data_fields = fields.clone();
 58+        data_fields.remove(5); // Remove "|"
 59+
 60+        for (col_idx, button) in data_fields.iter().enumerate() {
 61+            let side = col_idx / 5;
 62+            let col = col_idx % 5;
 63+            let notes: Vec<&str> = button.split('/').collect();
 64+            
 65+            let push = abc_to_midi(notes[0]);
 66+            let pull = if notes.len() > 1 { abc_to_midi(notes[1]) } else { push };
 67+
 68+            current_notes[side][row_idx][col][0] = push;
 69+            current_notes[side][row_idx][col][1] = pull;
 70+        }
 71+        row_idx += 1;
 72+    }
 73+
 74+    // Write the final layout
 75+    if !current_name.is_empty() {
 76+        write_layout(&mut layouts_code, &current_name, &current_notes);
 77+    }
 78+
 79+    layouts_code.push_str("];\n");
 80+
 81+    let out_dir = env::var("OUT_DIR").unwrap();
 82+    let dest_path = Path::new(&out_dir).join("layouts_data.rs");
 83+    fs::write(&dest_path, layouts_code).unwrap();
 84+    
 85+    println!("cargo:rerun-if-changed=layouts.txt");
 86+    println!("cargo:rerun-if-changed=build.rs");
 87+}
 88+
 89+fn write_layout(code: &mut String, name: &str, notes: &[[[[i8; 2]; 5]; 4]; 2]) {
 90+    code.push_str("    Layout {\n");
 91+    code.push_str(&format!("        name: {:?},\n", name));
 92+    code.push_str("        notes: [\n");
 93+    for side in notes {
 94+        code.push_str("            [\n");
 95+        for row in side {
 96+            code.push_str("                [");
 97+            for col in row {
 98+                code.push_str(&format!("[{}, {}], ", col[0], col[1]));
 99+            }
100+            code.push_str("],\n");
101+        }
102+        code.push_str("            ],\n");
103+    }
104+    code.push_str("        ],\n    },\n");
105+}
A src/layouts.txt
+23, -0
 1@@ -0,0 +1,23 @@
 2+Eirú C/G
 3+     	E,/F,	A,/B♭,	C♯/D♯	A/G	G♯/B♭	|	c♯/d♯	d♯/c♯	g♯/g	c♯'/b♭	a/d'
 4+	C,/G,	G,/B,	C/D	E/F	G/A	|	c/B	e/d	g/f	c'/a	e'/b
 5+	B,/A,	D/F♯	G/A	B/c	d/e	|	g/f♯	b/a	d'/c'	g'/e'	d♯'/f♯'
 6+	-	-	-	-	D,/D,	|	-	-	-	-	-
 7+
 8+Rochelle C/G
 9+     	E,/F,	A,/B♭,	C♯/D♯	A/G	G♯/B♭	|	c♯/d♯	a/c♯	g♯/g	c♯'/b♭	a'/d'
10+	C,/G,	G,/B,	C/D	E/F	G/A	|	c/B	e/d	g/f	c'/a	e'/b
11+	B,/A,	D/F♯	G/A	B/c	d/e	|	g/f♯	b/a	d'/c'	g'/e'	b'/f♯'
12+	-	-	-	-	D,/D,	|	-	-	-	-	-
13+
14+Jeffries C/G
15+	E,/F,	A,/B♭,	C♯/D♯	A/G	G♯/B♭	|	d♯/c♯	c♯/d♯	g♯/g	c♯'/b♭	a'/d'
16+	C,/G,	G,/B,	C/D	E/F	G/A	|	c/B	e/d	g/f	c'/a	e'/b
17+	B,/A,	D/F♯	G/A	B/c	d/e	|	g/f♯	b/a	d'/c' g'/e'	f'/f♯'
18+	-	-	-	-	D,/D,	|	-	-	-	-	-
19+
20+Lachenal C/G
21+	E,/F,	A,/B♭,	C♯/D♯	A/G	G♯/B♭	|	c♯/d♯	a/g	g♯/b♭	c♯'/d♯'	a'/f'
22+	C,/G,	G,/B,	C/D	E/F	G/A	|	c/B	e/d	g/f	c'/a	e'/b
23+	B,/A,	D/F♯	G/A	B/c	d/e	|	g/f♯	b/a	d'/c'	g'/e'	b'/f♯'
24+	-	-	-	-	D,/D,	|	-	-	-	-	-
M src/main.rs
+3, -0
 1@@ -24,6 +24,9 @@ use hal::prelude::*;
 2 use hal::timer::TimerCounter;
 3 use qt_py_m0 as bsp;
 4 
 5+include!("types.rs");
 6+include!(concat!(env!("OUT_DIR"), "/layouts_data.rs"));
 7+
 8 #[entry]
 9 fn main() -> ! {
10     let mut peripherals = Peripherals::take().unwrap();
A src/types.rs
+6, -0
1@@ -0,0 +1,6 @@
2+#[derive(Debug)]
3+pub struct Layout {
4+    pub name: &'static str,
5+    // [Sides: 2][Rows: 4][Columns: 5][Push/Pull: 2]
6+    pub notes: [[[[i8; 2]; 5]; 4]; 2],
7+}