feat: refactoring

This commit is contained in:
2023-06-05 16:56:55 +04:00
parent 07cb8f8003
commit 059ea3db5b
7 changed files with 54 additions and 74 deletions

View File

@@ -1,26 +1,22 @@
module SoundGen.Fx module SoundGen.Fx
open Settings
type Effect = type Effect =
| Saturator | Saturator
| Reverb | Reverb
type ReverbParam = { Wet: float; Room: float } type ReverbParam = { Wet: float; Room: float }
type SaturatorParam = { Gain: 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 saturator (param: SaturatorParam) (x: float) = tanh (param.Gain * x)
let waveshaper (param: WaveshaperParam) x = let waveshaper (param: WaveshaperParam) x =
(x + param.Gain * sin (param.Factor * x)) (x + param.Gain * sin (param.Factor * x)) |> saturator { Gain = 1.0 }
|> saturator { Gain = 1.0 }
let reverb (buffer: float seq) = let reverb (buffer: float seq) =
let delayMilliseconds = 500. //((1./8.) * beatDuration) // 500 is half a second let delayMilliseconds = 500. //((1./8.) * beatDuration) // 500 is half a second
let delaySamples = let delaySamples = (delayMilliseconds * 48.0) |> int // assumes 44100 Hz sample rate
(delayMilliseconds * 48.0) |> int // assumes 44100 Hz sample rate
let decay = 0.5 let decay = 0.5
let mutable newBuffer = Array.ofSeq buffer let mutable newBuffer = Array.ofSeq buffer
@@ -31,5 +27,4 @@ let reverb (buffer: float seq) =
newBuffer.[i + delaySamples] <- smpl newBuffer.[i + delaySamples] <- smpl
//else newBuffer <- Array.append newBuffer (smpl |> Array.singleton) //else newBuffer <- Array.append newBuffer (smpl |> Array.singleton)
Seq.zip buffer newBuffer Seq.zip buffer newBuffer |> Seq.map (fun (x, y) -> x + y * 0.8)
|> Seq.map (fun (x, y) -> x + y * 0.8)

View File

@@ -17,7 +17,6 @@ type GenerationParameter =
Sample: float } Sample: float }
let private pos hz x = (hz * x / sampleRate) % 1. let private pos hz x = (hz * x / sampleRate) % 1.
let sineosc hz x = let sineosc hz x =

View File

@@ -1,15 +1,15 @@
module SoundGen.PCMWave module SoundGen.PCMWave
open Settings open Settings
open System.IO open System.IO
let private toInt16Sample sample = sample |> (*) 32767. |> int16 let private toInt16Sample sample = sample |> (*) 32767. |> int16
let private pack (d: int16 []) = let private pack (d: int16[]) =
let stream = new MemoryStream() let stream = new MemoryStream()
let writer = let writer = new BinaryWriter(stream, System.Text.Encoding.ASCII)
new BinaryWriter(stream, System.Text.Encoding.ASCII)
let dataLength = Array.length d * 2 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(System.Text.Encoding.ASCII.GetBytes("data"))
writer.Write(dataLength) writer.Write(dataLength)
let data: byte [] = let data: byte[] = Array.zeroCreate dataLength
Array.zeroCreate dataLength
System.Buffer.BlockCopy(d, 0, data, 0, dataLength) System.Buffer.BlockCopy(d, 0, data, 0, dataLength)
writer.Write(data) writer.Write(data)
stream stream
let createWAV(wave: float seq) = let createWAV (wave: float seq) =
wave |> Seq.map (fun x -> x |> toInt16Sample) |> Seq.toArray |> pack wave |> Seq.map (fun x -> x |> toInt16Sample) |> Seq.toArray |> pack

View File

@@ -2,7 +2,6 @@
open SoundGen open SoundGen
open PCMWave open PCMWave
open Fx open Fx
open SoundGen.Fx
open Synth open Synth
open System open System
@@ -10,29 +9,27 @@ open System
type Note = { Name: string; Length: int } type Note = { Name: string; Length: int }
let parseNotes (input: string) = let parseNotes (input: string) =
let gluedString = input.Replace ('\n', ' ') let gluedString = input.Replace('\n', ' ')
gluedString.Split([| ' ' |], StringSplitOptions.RemoveEmptyEntries)
gluedString.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|> Seq.map (fun noteStr -> |> Seq.map (fun noteStr ->
let noteSigns = noteStr.Split([| '-' |]) let noteSigns = noteStr.Split('-')
let name = noteSigns[0].Trim() let name = noteSigns[0].Trim()
let length = noteSigns[1].Trim() |> int let length = noteSigns[1].Trim() |> int
{ Name = name.Trim(); Length = length }) { Name = name; Length = length })
|> List.ofSeq |> List.ofSeq
let getNoteSound (input: Note) = let getNoteSound (input: Note) =
let length = 1.0 / float input.Length let length = 1.0 / float input.Length
let semitoneShift = getSemitoneShift "A4" input.Name let semitoneShift = getSemitoneShift input.Name
note semitoneShift length 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 input =
let notes = "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 "
String.replicate 2 input
|> parseNotes
let song = let notes = String.replicate 2 input |> parseNotes
notes
|> Seq.map getNoteSound let song = notes |> Seq.map getNoteSound |> Seq.concat
|> Seq.concat
let writeToFile (ms: MemoryStream) = let writeToFile (ms: MemoryStream) =
use fs = use fs =
@@ -42,6 +39,6 @@ let writeToFile (ms: MemoryStream) =
song song
//|> reverb //|> reverb
|> Seq.map (waveshaper {Gain=1.5; Factor=2.5}) |> Seq.map (waveshaper { Gain = 1.5; Factor = 2.5 })
|> createWAV |> createWAV
|> writeToFile |> writeToFile

View File

@@ -5,4 +5,4 @@ let bpm = 120.
let beatDuration = 60. / bpm let beatDuration = 60. / bpm
let pitchStandard = 440. let pitchStandard = 440.
let volume = 0.5 let volume = 0.5
let attackMs = 100. let attackMs = 100.

View File

@@ -6,17 +6,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Settings.fs" /> <Compile Include="Settings.fs"/>
<Compile Include="Oscillator.fs" /> <Compile Include="Oscillator.fs"/>
<Compile Include="Synth.fs" /> <Compile Include="Synth.fs"/>
<Compile Include="Fx.fs" /> <Compile Include="Fx.fs"/>
<Compile Include="PCMWave.fs" /> <Compile Include="PCMWave.fs"/>
<Compile Include="Program.fs" /> <Compile Include="Program.fs"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

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