You may find this useful / helpful as a starting point for Nyquist code.
This code can be run in the Nyquist Prompt (https://manual.audacityteam.org/man/nyquist_prompt.html)
In this simple example, there are no stop / start / parity bits, just a direct encoding of list of ones and zeros into an audio waveform.
For practical use it would need some modification, particularly as this will cause a stack overflow if the data is too long. Nevertheless, it should be able to handle a few hundred bits.
For long data, you would need to replace the SEQREP function with a DO loop, and handle I/O without retaining the data in RAM.
To more easily input data, you may want to read the data from a file using READ-BYTE (http://www.cs.cmu.edu/~rbd/doc/nyquist/part19.html#index1742)
More about reading from files here:
https://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-man/xlisp-man-029.htm
and here:
https://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-man/xlisp-man-032.htm
See here for the available Nyquist docs: https://wiki.audacityteam.org/wiki/Nyquist_Documentation
;type generate
(setf baud 2205) ;This gives an exactly 20 samples per bit packet.
;Define time to transit between low and high states as
;proportion of bit packet time.
(setf transition 0.3)
;Define signal level as bool t or nil.
(setf state t)
;Binary data as list
(setf data (list 1 1 0 1 0 1 0 0 1 0 1 1 0 1 0 0 0 1 0))
;Total length of a single bit
(setf bitdur (/ 1.0 baud))
(defun rise (dur)
(setf hz (/ 0.5 dur))
(osc (hz-to-step hz) dur *sine-table* -90))
(defun fall (dur)
(setf hz (/ 0.5 dur))
(osc (hz-to-step hz) dur *sine-table* 90))
;;;Convert transition time to seconds, rounded to exact number of samples
(defun get-transition-dur (dur ratio)
(let ((ttime (round (* ratio dur *sound-srate*))))
(/ ttime *sound-srate*)))
;;; Generate binary 1.
;;; 'state' is true/false for current signal level
(defun one ()
(setf sig (if state one-const one-rise))
(setf state t)
sig)
;;; Generate binary 0.
;;; 'state' is true/false for current signal level
(defun zero ()
(setf sig (if state zero-fall zero-const))
(setf state nil)
sig)
(defun add-bit (bit)
(if (= bit 1) (one) (zero)))
;; We require a different wave shape depending on current state
(setf one-const
(let ((tdur (get-transition-dur bitdur transition)))
(snd-const 1 0 *sound-srate* (- bitdur tdur))))
(setf one-rise
(let ((tdur (get-transition-dur bitdur transition)))
(seq (rise tdur)
(cue (snd-const 1 0 *sound-srate* (- bitdur tdur))))))
(setf zero-const
(let ((tdur (get-transition-dur bitdur transition)))
(snd-const -1 0 *sound-srate* (- bitdur tdur))))
(setf zero-fall
(let ((tdur (get-transition-dur bitdur transition)))
(seq (fall tdur)
(cue (snd-const -1 0 *sound-srate* (- bitdur tdur))))))
(seqrep (i (length data)) (cue (add-bit (nth i data))))