Skip to content

Instantly share code, notes, and snippets.

@Alwinfy
Last active February 27, 2021 23:18
Show Gist options
  • Select an option

  • Save Alwinfy/63bd69c2c2bf5113f5b9dee8489e1c5b to your computer and use it in GitHub Desktop.

Select an option

Save Alwinfy/63bd69c2c2bf5113f5b9dee8489e1c5b to your computer and use it in GitHub Desktop.
who needs wav libraries anyway: the repeat update
; Squished with the following vimscript command:
; %s/;.*//g | %s/\n/ /g | %s/\s\+/ /g | %s/\s*\([][()]\)\s*/\1/g | %s/notes-per-min/nm/g | %s/to-bytes/b/g | %s/sample-rate/sr/g | %s/bytes-per-sample/bps/g | %s/write-wav/ww/g | %s/note-freqs/nf/g | %s/note-lengt
h/nl/g | %s/note/n/g
(require racket/flonum)
(define sample-rate 8000)
(define bytes-per-sample 1) ; 8bit audio
(define (to-bytes num len)
(list->bytes
(let loop ([num num] [len len])
(if (zero? len)
null
(cons (bitwise-and num 255) (loop (arithmetic-shift num -8) (sub1 len)))))))
(define (write-wav body sample-rate bps out)
; Header
(write-bytes
(bytes-append
#"RIFF"
; Remaining size
(~> body stream-length (* bps) (+ 36) (to-bytes 4))
#"WAVEfmt "
(to-bytes 16 4) ; header size
(to-bytes 1 2) ; PCM=1
(to-bytes 1 2) ; 1 channel
(to-bytes sample-rate 4) ; SR
; byte-rate=SR*chans*bytes/sample
(to-bytes (* sample-rate bps) 4)
; block alignment=chans*bytes/sample
(to-bytes bps 2)
(to-bytes (* 8 bps) 2) ; bits/sample
#"data"
; size=samples*chans*bytes/sample
(to-bytes (* (stream-length body) bps) 4))
out)
; Body
(for ([sample body])
(write-bytes (to-bytes sample bps) out)))
(define middle-a 440.0)
(define (freq n) (if n (~> n (/ 12) ((curry expt 2)) (* middle-a)) 0.0))
(define vars (make-hash))
(define (note-freqs notes)
(append*
(for/list ([note notes])
(match note
[(list 'save id notes ...) #:when (symbol? id) (begin (hash-set! vars id (note-freqs notes)) null)]
[(list notes ...) `(,(list->vector (map freq notes)))]
[id #:when (hash-has-key? vars id) (hash-ref vars id)]
[(list* notes ..1 repeat) (apply append (build-list repeat (const (note-freqs notes))))]
[_ `(,(vector (freq note)))]))))
(define args
(let loop ([data (open-input-string string-args)])
(let ([val (read data)])
(if (eof-object? val) null (cons val (loop data))))))
(define-values (notes-per-min notes)
(match args
[(cons npm notes) (values npm (list->vector (note-freqs notes)))]
[null (values #f #())]))
(define valcap (~>> bytes-per-sample (* 8) (arithmetic-shift 1) sub1 ->fl))
(define note-length (* (/ 60 (or notes-per-min 1)) sample-rate))
(define period (/ (* 2 pi) sample-rate))
(cond
[(not notes-per-min) "Usage: !!doots [notes per minute] [notes ...]\nEach note's a number of halftones above middle A. #f means silence.\n`(1 2 . 3)` is shorthand for `1 2 1 2 1 2`.\n`(3 7 10)` is a C chord.\n`(save arpeggio 3 7 10) arpeggio #f arpeggio` expands to `3 7 10 #f 3 7 10`."]
[(< notes-per-min (vector-length notes)) "keep it under a minute you animals"]
[else
(make-attachment
(call-with-output-bytes
(curry write-wav
(for/stream ([idx (in-range (* note-length (vector-length notes)))])
(match-let ([note (vector-ref notes (exact-truncate (/ idx note-length)))])
(~>
(for/sum ([i note])
(~> i
(fl* period (->fl idx))
flsin (fl+ 1.0) (fl* 0.5 valcap)))
(/ (vector-length note))
exact-truncate)))
sample-rate bytes-per-sample))
"doot.wav" 'audio/wav)])
@clara-bg
Copy link

This is very fun ty

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment