From 059ea3db5b4bb882d61e61fc81969bdfc024aaa2 Mon Sep 17 00:00:00 2001 From: HiveBeats Date: Mon, 5 Jun 2023 16:56:55 +0400 Subject: [PATCH] feat: refactoring --- SoundGen/Fx.fs | 13 ++++------ SoundGen/Oscillator.fs | 1 - SoundGen/PCMWave.fs | 15 ++++++------ SoundGen/Program.fs | 27 ++++++++++----------- SoundGen/Settings.fs | 2 +- SoundGen/SoundGen.fsproj | 19 +++++++-------- SoundGen/Synth.fs | 51 +++++++++++++++++----------------------- 7 files changed, 54 insertions(+), 74 deletions(-) diff --git a/SoundGen/Fx.fs b/SoundGen/Fx.fs index d5a8a92..7ed2d7f 100644 --- a/SoundGen/Fx.fs +++ b/SoundGen/Fx.fs @@ -1,26 +1,22 @@ module SoundGen.Fx -open Settings - type Effect = | Saturator | Reverb type ReverbParam = { Wet: float; Room: float } type SaturatorParam = { Gain: float } -type WaveshaperParam = {Gain: float; Factor: float;} +type WaveshaperParam = { Gain: float; Factor: float } let saturator (param: SaturatorParam) (x: float) = tanh (param.Gain * x) let waveshaper (param: WaveshaperParam) x = - (x + param.Gain * sin (param.Factor * x)) - |> saturator { Gain = 1.0 } + (x + param.Gain * sin (param.Factor * x)) |> saturator { Gain = 1.0 } let reverb (buffer: float seq) = let delayMilliseconds = 500. //((1./8.) * beatDuration) // 500 is half a second - let delaySamples = - (delayMilliseconds * 48.0) |> int // assumes 44100 Hz sample rate + let delaySamples = (delayMilliseconds * 48.0) |> int // assumes 44100 Hz sample rate let decay = 0.5 let mutable newBuffer = Array.ofSeq buffer @@ -31,5 +27,4 @@ let reverb (buffer: float seq) = newBuffer.[i + delaySamples] <- smpl //else newBuffer <- Array.append newBuffer (smpl |> Array.singleton) - Seq.zip buffer newBuffer - |> Seq.map (fun (x, y) -> x + y * 0.8) + Seq.zip buffer newBuffer |> Seq.map (fun (x, y) -> x + y * 0.8) diff --git a/SoundGen/Oscillator.fs b/SoundGen/Oscillator.fs index fcd7ca9..8b0420b 100644 --- a/SoundGen/Oscillator.fs +++ b/SoundGen/Oscillator.fs @@ -17,7 +17,6 @@ type GenerationParameter = Sample: float } - let private pos hz x = (hz * x / sampleRate) % 1. let sineosc hz x = diff --git a/SoundGen/PCMWave.fs b/SoundGen/PCMWave.fs index 80975f2..87b1a53 100644 --- a/SoundGen/PCMWave.fs +++ b/SoundGen/PCMWave.fs @@ -1,15 +1,15 @@ module SoundGen.PCMWave + open Settings open System.IO let private toInt16Sample sample = sample |> (*) 32767. |> int16 -let private pack (d: int16 []) = +let private pack (d: int16[]) = let stream = new MemoryStream() - let writer = - new BinaryWriter(stream, System.Text.Encoding.ASCII) + let writer = new BinaryWriter(stream, System.Text.Encoding.ASCII) let dataLength = Array.length d * 2 @@ -33,12 +33,11 @@ let private pack (d: int16 []) = writer.Write(System.Text.Encoding.ASCII.GetBytes("data")) writer.Write(dataLength) - let data: byte [] = - Array.zeroCreate dataLength + let data: byte[] = Array.zeroCreate dataLength System.Buffer.BlockCopy(d, 0, data, 0, dataLength) writer.Write(data) stream - -let createWAV(wave: float seq) = - wave |> Seq.map (fun x -> x |> toInt16Sample) |> Seq.toArray |> pack \ No newline at end of file + +let createWAV (wave: float seq) = + wave |> Seq.map (fun x -> x |> toInt16Sample) |> Seq.toArray |> pack diff --git a/SoundGen/Program.fs b/SoundGen/Program.fs index b0fbb00..1c89506 100644 --- a/SoundGen/Program.fs +++ b/SoundGen/Program.fs @@ -2,7 +2,6 @@ open SoundGen open PCMWave open Fx -open SoundGen.Fx open Synth open System @@ -10,29 +9,27 @@ open System type Note = { Name: string; Length: int } let parseNotes (input: string) = - let gluedString = input.Replace ('\n', ' ') - gluedString.Split([| ' ' |], StringSplitOptions.RemoveEmptyEntries) + let gluedString = input.Replace('\n', ' ') + + gluedString.Split(' ', StringSplitOptions.RemoveEmptyEntries) |> Seq.map (fun noteStr -> - let noteSigns = noteStr.Split([| '-' |]) + let noteSigns = noteStr.Split('-') let name = noteSigns[0].Trim() let length = noteSigns[1].Trim() |> int - { Name = name.Trim(); Length = length }) + { Name = name; Length = length }) |> List.ofSeq let getNoteSound (input: Note) = let length = 1.0 / float input.Length - let semitoneShift = getSemitoneShift "A4" input.Name + let semitoneShift = getSemitoneShift input.Name note semitoneShift length -let input = "A4-4 A4-4 A4-4 A4-4 A4-2 A4-4 A4-4 A4-4 A4-4 A4-4 A4-2 D5-4 D5-4 D5-4 D5-4 D5-4 D5-4 D5-2 C5-4 C5-4 C5-4 C5-4 C5-4 C5-4 C5-2 G4-2 " -let notes = - String.replicate 2 input - |> parseNotes +let input = + "A4-4 A4-4 A4-4 A4-4 A4-2 A4-4 A4-4 A4-4 A4-4 A4-4 A4-2 D5-4 D5-4 D5-4 D5-4 D5-4 D5-4 D5-2 C5-4 C5-4 C5-4 C5-4 C5-4 C5-4 C5-2 G4-2 " -let song = - notes - |> Seq.map getNoteSound - |> Seq.concat +let notes = String.replicate 2 input |> parseNotes + +let song = notes |> Seq.map getNoteSound |> Seq.concat let writeToFile (ms: MemoryStream) = use fs = @@ -42,6 +39,6 @@ let writeToFile (ms: MemoryStream) = song //|> reverb -|> Seq.map (waveshaper {Gain=1.5; Factor=2.5}) +|> Seq.map (waveshaper { Gain = 1.5; Factor = 2.5 }) |> createWAV |> writeToFile diff --git a/SoundGen/Settings.fs b/SoundGen/Settings.fs index 3b9714b..36cb4d2 100644 --- a/SoundGen/Settings.fs +++ b/SoundGen/Settings.fs @@ -5,4 +5,4 @@ let bpm = 120. let beatDuration = 60. / bpm let pitchStandard = 440. let volume = 0.5 -let attackMs = 100. \ No newline at end of file +let attackMs = 100. diff --git a/SoundGen/SoundGen.fsproj b/SoundGen/SoundGen.fsproj index b1f3b32..67768dc 100644 --- a/SoundGen/SoundGen.fsproj +++ b/SoundGen/SoundGen.fsproj @@ -6,17 +6,14 @@ - - - - - - - - - - - + + + + + + + + diff --git a/SoundGen/Synth.fs b/SoundGen/Synth.fs index c4fec43..06f9e1b 100644 --- a/SoundGen/Synth.fs +++ b/SoundGen/Synth.fs @@ -2,38 +2,35 @@ 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 = +let private getSemitoneShiftInternal (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 |] - + let pitchClasses = + [| "C"; "C#"; "D"; "D#"; "E"; "F"; "F#"; "G"; "G#"; "A"; "A#"; "B" |] + // 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 - + + 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 - + + 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 samples = seq { 0.0 .. (duration * sampleRate) } let attack = - let samplesToRise = - (sampleRate * (0.001 * attackMs)) + let samplesToRise = (sampleRate * (0.001 * attackMs)) let risingDelta = 1. / samplesToRise let mutable i = 0. @@ -45,22 +42,18 @@ let private freq hz duration (osc: OscillatorParameter list) = } let output = - Seq.map - (fun x -> - multiosc { Oscillators = osc; Sample = x } - |> (*) volume) - samples + Seq.map (fun x -> multiosc { Oscillators = osc; Sample = x } |> (*) volume) samples let adsrLength = Seq.length output - let attackArray = - attack |> Seq.take adsrLength + 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)) + Seq.zip3 release attackArray output |> Seq.map (fun (x, y, z) -> (x * y * z)) +let getSemitoneShift (targetNote: string) : int = + getSemitoneShiftInternal "A4" targetNote let note semitone beats = let hz = getHzBySemitones (semitone) @@ -68,6 +61,6 @@ let note semitone beats = freq hz (beats * beatDuration) - [ { Osc = Saw; Freq = hz / 4. }; - { Osc = Saw; Freq = hz+0.5 }; - { Osc = Saw; Freq = hz-1. } ] + [ { Osc = Saw; Freq = hz / 4. } + { Osc = Saw; Freq = hz + 0.5 } + { Osc = Saw; Freq = hz - 1. } ]