Skip to content

Instantly share code, notes, and snippets.

@nyanpasu64
Last active February 24, 2024 05:09
Show Gist options
  • Save nyanpasu64/424110eab84dad50cf1a6646a72b2627 to your computer and use it in GitHub Desktop.
Save nyanpasu64/424110eab84dad50cf1a6646a72b2627 to your computer and use it in GitHub Desktop.
mod of Furrykef's waveform generator (n163.ny) + 60hz interval generator (zz_beater.ny)

Just updated my N163 Waveform Generator with "full normalize" mode, to get a bit of extra amplitude out of your waves.

  • Scroll to n163-nyanpasu64.ny. Click "raw" and save it to %appdata%\audacity\Plug-Ins

    • Optionally, install my 60hz beat track generator. Save zz_beater.ny to the same folder.
    • To generate a beat track, click menu Generate/Beat Track... and specify how many beats. Mute the resulting track, collapse, and drag it above your sound sample.
    • Alternatively, menu Analyze/Regular Interval Labels...
  • Under Preferences (Ctrl+P), on the left click Keyboard. On the right, search N163, and Set to Shift+Enter.

  • Zoom into your audio.

  • To generate a waveform, first select one period of audio (pressing z will align your selection with zero crossings.)

    • Press Shift+Space to preview the note. Ensure it's approximately the right pitch.
  • For the first waveform, press Shift+Enter to generate a wave.

  • For subsequent waveforms, Shift+Down, Shift+Enter to append to the same label track.

  • Use the menu, Edit/Labels/Edit Labels, to export all N163 waveforms to a file, instead of copying from Audacity labels.

My fork of 0CC (https://github.com/nyanpasu64/0CC-FamiTracker/releases) supports pasting many N163 waves at the same time.

  • To copy all waveforms, open the file and delete everything but the last column. Add semicolons at the end of each line.
  • In the text editor, copy all lines. Then in the N163 instrument editor, click the Paste button to paste all waveforms sequentially.
;nyquist plug-in
;version 3
;type analyze
;name "N163/FDS waveform..."
;action "Calculating N163/FDS waveform..."
;control mode "Mode" choice "Namco 163,Famicom Disk System" 0
;control wavesize-choice "Wave size (N163 only)" choice "4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,160,164,168,172,176,180,184,188,192,196,200,204,208,212,216,220,224,228,232,236,240" 7
;control normalize-choice "Normalize result" choice "no,balanced,full" 2
; **********************************************************************
; Namco 163/Famicom Disk System waveform tool
; By Kef Schecter
; Public domain
; **********************************************************************
(setf *gc-flag* nil)
(setf mode-n163 0)
(setf mode-fds 1)
(setf normalize-no 0)
(setf normalize-balanced 1)
(setf normalize-full 2)
(cond
((= mode mode-n163) ; Convert from wavesize-choice table to actual
; wave size (4, 8, 12...)
(setf wavesize (* 4 (1+ wavesize-choice)))
(setf max-volume 15))
(t (setf wavesize 64) ; FDS mode
(setf max-volume 63)))
(setf half-max-volume (/ (+ max-volume 1) 2.0))
(setf new-old-ratio (/ (float wavesize) len))
(setf resample-rate (truncate (* *sound-srate* new-old-ratio)))
(defmacro push (item lst)
`(setq ,lst (cons ,item ,lst)))
(defun process ()
(let* ((resampled (resample s resample-rate))
(normalized (cond
((= normalize-choice normalize-full) (normalize-full resampled))
((= normalize-choice normalize-balanced) (normalize resampled))
((= normalize-choice normalize-no) (resampled))))
(samples-array (snd-samples normalized wavesize))
(samples-list '()))
(dotimes (index wavesize)
(push (n163-adjust (aref samples-array index)) samples-list))
(reverse samples-list)
)
)
(defun normalize (snd)
(let ((maximum (peak snd NY:ALL)))
(scale (/ 1.0 maximum) snd)))
(defun normalize-full (snd)
(let* (
(positive-snd (sum snd (peak (sum snd -100) NY:ALL) -100))
(maximum (peak positive-snd NY:ALL))
(zero-two (scale (/ 2.0 maximum) positive-snd))
)
(sum zero-two -1)))
; Converts value in range from -1.0 to 1.0 into an integer from 0 to max-volume
; Does not bother range-checking input
(defun n163-adjust (value)
(min (float max-volume) (truncate (* (+ value 1.0) half-max-volume))))
(defun run ()
(list (list 0 (get-duration 1) (string-trim "()" (format nil "~a" (process))))))
; Execution begins here
(if (arrayp s)
"Stereo tracks are not supported at this time."
(run))
;nyquist plug-in
;version 4
;type generate
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
;name "Beat Track..."
;manpage "Beat_Track"
;preview linear
;action "Generating Beat..."
;author "Dominic Mazzoni, nyanpasu64"
;copyright "Released under terms of the GNU General Public License version 2"
;; by Dominic Mazzoni, David R. Sky and Steve Daulton.
;; Drip sound generator by Paul Beach
;; Released under terms of the GNU General Public License version 2:
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html .
;control action "Action choice" choice "Generate track, Help screen 1, Help screen 2" 0
;control freq "Freq (ticks per second)" real "hi guys" 60 0 1000
;control timesig "Beats per measure (bar)" int "1 - 20 beats/measure" 4 1 20
;control swing "Swing amount" float "+/- 1" 0 -1 1
;control measures "Number of measures (bars)" int "1 - 1000 bars" 16 1 1000
;control click-track-dur "Optional rhythm track duration (minutes seconds)" string "Whole numbers only" ""
;;control ticklen "Individual beat duration (milliseconds)" int "1 - 100 ms" 10 1 100
;control offset "Start time offset (seconds)" real "0 - 30 seconds" 0 0 30
;control click-type "Beat sound" choice "Metronome tick,Ping,Cowbell,Resonant noise,Noise click,Drip" 4
;;control q "Noise click resonance - discernable pitch (q)" int "1 - 20" 1 1 20
;control high "MIDI pitch of strong beat" int "18 - 116" 84 18 116
;control low "MIDI pitch of weak beat" int "18 - 116" 80 18 116
;; allow q control to be commented out.
(if (not (boundp 'q))
(setf q 10))
;; allow ticklen control to be commented out.
(if (not (boundp 'ticklen))
(setf ticklen 10))
;; TODO: Hard code tick length (long and short versions
;; TODO: add drum sounds
(defun help1 ()
(format nil
"Rhythm Track Generator help - screen 1 of 2
Generates a rhythm track at the selected freq, beats per
measure, and either number of measures or track duration,
using the selected sound.
'Freq': number of beats (clicks) per minute.
'Beats per measure (bar)': For example, 3/4 time means one
strong beat then two others to form one bar, repeated
depending on 'number of measures' or 'rhythm track duration'.
'Swing amount': When set to a non-zero amount, alternate
beats are delayed or advanced to give a swing feel. At
maximum / minimum settings the rhythm plays with triplet timing.
'Optional rhythm track duration': If you enter a value into this
field, either (minutes seconds - separated by a space), or
(seconds), the generated rhythm track will be at or slightly
longer than this duration: the end of the track is extended
into a whole measure if the entered duration does not
produce this. Use whole numbers only.
If you enter a value into this field, the 'number of measures'
value will be ignored.
To generate rhythm track or view help screen 2,
restart Rhythm Track and select from 'Action choice'.")) ;end of help1
(defun help2 ()
(format nil
"Rhythm Track Generator help - screen 2 of 2
'Start time offset': makes the rhythm track start at a later
time than the very beginning (zero seconds), maximum
of 30 seconds.
'Beat sound': choose which sound to use for the beats.
'MIDI pitch of strong/weak beat': MIDI values indicate
what pitch to use. C-notes are:
24, 36, 48, 60 (middle C), 72, 84, 96, 108.
C# (C-sharp) above middle C is 61.
To generate rhythm track or view help screen 1,
restart Rhythm Track and select from 'Action choice'.")) ;end of help 2
;Check function: returns 1 on error
;min and max are allowable min and max values for arg
(defun check (arg min max)
(if (and (>= arg min) (<= arg max)) 0 1))
;Function to convert a string into a list
(defun string-to-list (string)
(read (make-string-input-stream (format nil "(~a)" string))))
(defun round-up (x)
(if (> x (truncate x))
(truncate (1+ x))
(truncate x)))
(defun m-s-to-seconds (mslist)
(if (= (length mslist) 1)
;just seconds
(first mslist)
;minutes and seconds
(+ (* 60 (first mslist))(second mslist))))
;Function to generate drip sound clicks
;code by Paul Beach www.proviewlandscape.com/liss/
;stretch-abs function makes this sound more like 'tick' sounds.
;limit hz to nyquist frequency.
(defun drip (p) ;p is pitch in hz
(let* ((maxhz (/ *sound-srate* 2.1))
(hz1 (min maxhz (* 2.40483 p)))
(hz2 (min maxhz (* 5.52008 p)))
(hz3 (min maxhz (* 8.653 p)))
(hz4 (min maxhz (* 11.8 p))))
(lp
(stretch-abs 1
(mult (exp-dec 0 0.015 0.25)
(sim
(mult (hzosc hz1) 0.5)
(mult (hzosc hz2) 0.25)
(mult (hzosc hz3) 0.125)
(mult (hzosc hz4) 0.0625))))
440)))
;Function used to normalize noise and tick clicks
;this function is necessary because filtering
;changes amplitude of filtered noise clicks
(defun normalize (sound)
(scale (/ (peak sound ny:all)) sound))
;; Error Checking
(defun errorcheck ()
;beats per measure
(if (= (check timesig 1 20) 1)
(setq error-msg (strcat error-msg (format nil
"Beats per measure ~a outside valid range 1 to 20
" timesig))))
;number of measures
(if (= (check measures 1 1000) 1)
(setq error-msg (strcat error-msg (format nil
"Number of measures ~a outside valid range 1 to 1000
" measures))))
;time start offset
(if (= (check offset 0 30) 1)
(setq error-msg (strcat error-msg (format nil
"Time offset ~a outside valid range 0 to 30 seconds
" offset))))
; q
(if (= (check q 1 20) 1)
(setq error-msg (strcat error-msg (format nil
"Filter quality q ~a outside valid range 1 to 20
" q))))
;high MIDI pitch
(if (= (check high 18 116) 1)
(setq error-msg (strcat error-msg (format nil
"High MIDI pitch ~a outside valid range 18 to 116
" high))))
;low MIDI pitch
(if (= (check low 18 116) 1)
(setq error-msg (strcat error-msg (format nil
"Low MIDI pitch ~a outside valid range 18 to 116
" low))))
;validate string
(if (> (length m-s) 0) ;if at least one item
(cond
((or
(> (length m-s) 2) ;there are more than 2 items or
(not (integerp (first m-s))) ;first is not an integer or
(and (= (length m-s) 2)(not (integerp (second m-s))))) ;second is not an integer
(setq error-msg (strcat error-msg (format nil
"If used, 'Optional rhythm track duration' must be
entered as either one number (seconds0, or two
numbers (minutes seconds) separated by a space.
Use whole numbers only.~%"))))
;one or two integers
((and
(= (length m-s) 1) ;one integer and
(= (check (first m-s) 0 3660) 1)) ;outside valid range
(setq error-msg (strcat error-msg (format nil
"~a seconds is outside valid range 0 to 3660~%" (first m-s)))))
;one or two integers
((and
(= (length m-s) 2) ;2 integers and
(or
(= (check (first m-s) 0 60) 1) ;1st is outside valid range or
(= (check (second m-s) 0 59) 1))) ;2nd is outside valid range or
(setq error-msg (strcat error-msg (format nil
"~a is outside valid range 0 to (60 59)~%"
m-s))))))) ;end of error checking
(defun metronome-tick (hz peak)
(let* ((ln 300)
(sig-array (make-array ln))
(x 1))
;; generate some 'predictable' white noise
(dotimes (i ln)
(setf x (rem (* 479 x) 997))
(setf (aref sig-array i) (- (/ x 500.0) 1)))
(setf sig (sim (s-rest-abs 0.2)
(snd-from-array 0 44100 sig-array)))
(setf sig
(mult (abs-env (pwev 10 (/ ln 44100.0) 2 1 0))
(highpass8 (lowpass2 sig (* 2 hz) 6)
hz)))
(let ((gain (/ (peak sig 300))))
; The '1.11' factor makes up for gain reduction in 'resample'
(mult (abs-env (pwlv 1.11 0.02 1.11 0.05 0 ))
(jcrev (mult peak gain sig) 0.01 0.1)))))
(defun s-rest-abs (d)
(abs-env (s-rest d)))
(defun cowbell (hz)
(sim
(mult (pwev 0.3 0.8 0.0005)
(hzosc hz *tri-table*)
(hzosc (* hz 3.46) *tri-table*))
(mult (pwev 0.7 0.2 0.01)
(hzosc (* hz 7.3) *tri-table*)
(hzosc (* hz 1.52) *tri-table*))))
(defun get-metronome-tick (hz gain)
(resample
(sound-srate-abs 44100 (metronome-tick hz gain))
*sound-srate*))
(defun get-ping (pitch)
(stretch-abs ticklen
(mult
(control-srate-abs *sound-srate* (pwl 0.005 amp 0.995 amp 1))
(osc pitch))))
(defun get-resonant-noise (pitch)
(stretch-abs 0.05 ; 50 milliseconds
(mult
(control-srate-abs *sound-srate* (pwl 0.05 amp 0.95 amp 1))
(normalize (lowpass2 (noise 1) (step-to-hz pitch) 20)))))
(defun get-noise-click (pitch)
(stretch-abs 0.005
(mult
(control-srate-abs *sound-srate* (pwl 0.005 amp 0.995 amp 1))
(normalize (lowpass2 (noise 1) (step-to-hz pitch) 2)))))
(defun get-drip (pitch)
(stretch-abs ticklen
(mult
(control-srate-abs *sound-srate* (pwl 0.005 amp 0.995 amp 1))
(normalize (drip (step-to-hz pitch))))))
(defun get-cowbell (pitch)
(mult 0.8 (cowbell (step-to-hz pitch))))
;Function to make click
(defun click (type accent)
(setq pitch (if (= accent 1) high low))
(setq amp (if (= accent 1) 0.75 0.5))
(case type
(0 (get-metronome-tick (step-to-hz pitch) amp))
(1 (get-ping pitch))
(2 (get-cowbell pitch))
(3 (get-resonant-noise pitch))
(4 (get-noise-click pitch))
(t (get-drip pitch))))
(defun swing-adjust (i val)
(* val (/ 3.0) (rem (1+ i) 2)))
;Function to make one measure and save it in the global *measure*
(defun makemeasure ()
(setf *measure*
(sim
(s-rest (* timesig beatlen)) ;required for trailing silence
(click click-type 1) ;accented beat
(simrep (x (- timesig 1))
(at-abs (* beatlen (+ x 1 (swing-adjust x swing)))
(cue (click click-type 0))))))) ;unaccented beat
(defun make-click-track (measures mdur)
(setf *measure* (set-logical-stop (cue *measure*) (* timesig beatlen)))
(seqrep (i measures) (cue *measure*)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; START MAIN PROGRAM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;view 1 of 2 help screens, or generate rhythm track
(cond ;'master' cond
((= action 1)(help1)); display help screen 1
((= action 2)(help2)) ; display help screen 2
(t ;Run Program
(setq len (/ (* *sound-srate* timesig measures) freq))
(setq error-msg "") ;initialize error-msg
;convert minutes-seconds string to a list
;for example, "4 30" becomes (4 30)
(setf m-s (string-to-list click-track-dur))
;run error checks on input values
(errorcheck)
(if (= (length error-msg) 0) ;if no errors, generate Rhythm Track
(progn
; duration of 1 beat, originally statically 0.01 s
(setf ticklen (* (max 1 (min 100 ticklen)) 0.001))
(setf beatlen (/ freq))
;call function to make one measure
(makemeasure)
;calculate measures from text input (if used)
(if m-s (setq measures
(/ (m-s-to-seconds m-s)(* timesig beatlen))))
;if previewing, restrict number of measures
(let ((preview (/ (get '*project* 'preview-duration)
(* timesig beatlen))))
(if (not (get '*track* 'view)) ;NIL if preview
(setq measures (min preview measures))))
;round up number of measures
(setq measures (round-up measures))
;add time offset and beatlen of silence to rhythm track,
(seq
(s-rest offset) ;offset
(make-click-track measures (* timesig beatlen)))) ;click track
;Else error message
(setq error-msg (strcat (format nil
"Error.~%You have entered at least one invalid value:~%~%") error-msg)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment