feat: refactoring
This commit is contained in:
@@ -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)
|
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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. } ]
|
||||||
|
|||||||
Reference in New Issue
Block a user