Files
soundgen/SoundGen/Synth.fs

74 lines
2.1 KiB
Forth

module SoundGen.Synth
open Settings
open Oscillator
open SoundGen.Oscillator
let private getHzBySemitones semi =
pitchStandard * (2. ** (1. / 12.)) ** semi
let getSemitoneShift (rootNote : string) (targetNote : string) : int =
// Define arrays to map pitch classes to numeric values and vice versa
let pitchClasses = [| "C"; "C#"; "D"; "D#"; "E"; "F"; "F#"; "G"; "G#"; "A"; "A#"; "B" |]
let pitchClassValues = [| 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11 |]
// Extract the note number and pitch class for the root note
let rootNoteNum = int (rootNote.Substring(rootNote.Length - 1))
let rootPitchClass = Array.findIndex ((=) (rootNote.Substring(0, rootNote.Length - 1))) pitchClasses
// Extract the note number and pitch class for the target note
let targetNoteNum = int (targetNote.Substring(targetNote.Length - 1))
let targetPitchClass = Array.findIndex ((=) (targetNote.Substring(0, targetNote.Length - 1))) pitchClasses
// Calculate the semitone shift using the formula
(targetNoteNum - rootNoteNum) * 12 + (targetPitchClass - rootPitchClass)
let private freq hz duration (osc: OscillatorParameter list) =
let samples =
seq { 0.0 .. (duration * sampleRate) }
let attack =
let samplesToRise =
(sampleRate * (0.001 * attackMs))
let risingDelta = 1. / samplesToRise
let mutable i = 0.
seq {
while true do
i <- i + risingDelta
yield min i 1.
}
let output =
Seq.map
(fun x ->
multiosc { Oscillators = osc; Sample = x }
|> (*) volume)
samples
let adsrLength = Seq.length output
let attackArray =
attack |> Seq.take adsrLength
let release = Seq.rev attackArray
Seq.zip3 release attackArray output
|> Seq.map (fun (x, y, z) -> (x * y * z))
let note semitone beats =
let hz = getHzBySemitones (semitone)
freq
hz
(beats * beatDuration)
[ { Osc = Saw; Freq = hz / 4. };
{ Osc = Saw; Freq = hz+0.5 };
{ Osc = Saw; Freq = hz-1. } ]