refactor: separate files

This commit is contained in:
HiveBeats
2022-10-31 08:49:49 +04:00
parent 2e1a5b2bf8
commit b56520740e
7 changed files with 188 additions and 120 deletions

16
SoundGen.sln Normal file
View File

@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SoundGen", "SoundGen\SoundGen.fsproj", "{724E3D1E-26BC-45FD-B9D5-D1BE001E916A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{724E3D1E-26BC-45FD-B9D5-D1BE001E916A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{724E3D1E-26BC-45FD-B9D5-D1BE001E916A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{724E3D1E-26BC-45FD-B9D5-D1BE001E916A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{724E3D1E-26BC-45FD-B9D5-D1BE001E916A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

19
SoundGen/Fx.fs Normal file
View File

@@ -0,0 +1,19 @@
module SoundGen.Fx
type Effect =
| Saturator
| Reverb
type Reverb = { Wet: float; Room: float }
type Saturator = { Gain: float }
let saturate (param: Saturator, x: float) = tanh (param.Gain * x)
//
// let process(effects:Effect list, sound:float seq) =
// let mutable output = sound
// effects
// |> List.iter((fun eff ->
// match eff with
// |Saturator -> output <- (output |> Seq.map saturate eff)))
//

44
SoundGen/PCMWave.fs Normal file
View File

@@ -0,0 +1,44 @@
module SoundGen.PCMWave
open Settings
open System.IO
let toInt16Sample sample = sample |> (*) 32767. |> int16
let pack (d: int16 []) =
let stream = new MemoryStream()
let writer =
new BinaryWriter(stream, System.Text.Encoding.ASCII)
let dataLength = Array.length d * 2
// RIFF
writer.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"))
writer.Write(Array.length d)
writer.Write(System.Text.Encoding.ASCII.GetBytes("WAVE"))
// fmt
let sr = sampleRate |> int
writer.Write(System.Text.Encoding.ASCII.GetBytes("fmt "))
writer.Write(16)
writer.Write(1s) // PCM
writer.Write(1s) // mono
writer.Write(sr) // sample rate
writer.Write(sr * 16 / 8) // byte rate
writer.Write(2s) // bytes per sample
writer.Write(16s) // bits per sample
// data
writer.Write(System.Text.Encoding.ASCII.GetBytes("data"))
writer.Write(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

View File

@@ -1,88 +1,9 @@
module SoundGen 
open System.IO open System.IO
open SoundGen
let sampleRate = 48000. open PCMWave
let bpm = 120. open Fx
let beatDuration = 60. / bpm open Synth
let pitchStandard = 440.
let volume = 0.5
let attackMs = 100.
let getHzBySemitones semi =
pitchStandard * (2. ** (1. / 12.)) ** semi
let getSemitonesByNote (str: string) =
let defaultOctave = 4
let notes =
[ "A"
"A#"
"B"
"C"
"C#"
"D"
"D#"
"E"
"F"
"F#"
"G"
"G#" ]
|> List.toArray
let octave =
str.Substring(str.Length - 1) |> int
let noteShift =
Array.findIndex (fun e -> e = str.Substring(0, str.Length - 1)) notes
(octave - defaultOctave - 1) * 12 + noteShift
let saturate x =
tanh (6. * x)
let toInt16Sample sample = sample |> (*) 32767. |> int16
let freq hz duration =
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 ->
x
|> (*) (2. * System.Math.PI * hz / sampleRate)
|> sin
|> (*) 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) |> saturate)
let note semitone beats =
let hz = getHzBySemitones semitone
freq hz (beats * beatDuration)
let song = let song =
[ note 3 0.5 [ note 3 0.5
@@ -126,47 +47,13 @@ let song =
note 15 0.5 ] note 15 0.5 ]
|> Seq.concat |> Seq.concat
let pack (d: int16 []) =
let stream = new MemoryStream()
let writer = let writeToFile (ms: MemoryStream) =
new BinaryWriter(stream, System.Text.Encoding.ASCII)
let dataLength = Array.length d * 2
// RIFF
writer.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"))
writer.Write(Array.length d)
writer.Write(System.Text.Encoding.ASCII.GetBytes("WAVE"))
// fmt
let sr = sampleRate |> int
writer.Write(System.Text.Encoding.ASCII.GetBytes("fmt "))
writer.Write(16)
writer.Write(1s) // PCM
writer.Write(1s) // mono
writer.Write(sr) // sample rate
writer.Write(sr * 16 / 8) // byte rate
writer.Write(2s) // bytes per sample
writer.Write(16s) // bits per sample
// data
writer.Write(System.Text.Encoding.ASCII.GetBytes("data"))
writer.Write(dataLength)
let data: byte [] =
Array.zeroCreate dataLength
System.Buffer.BlockCopy(d, 0, data, 0, dataLength)
writer.Write(data)
stream
let write (ms: MemoryStream) =
use fs = use fs =
new FileStream(Path.Combine(__SOURCE_DIRECTORY__, "test.wav"), FileMode.Create) new FileStream(Path.Combine(__SOURCE_DIRECTORY__, "test.wav"), FileMode.Create)
ms.WriteTo(fs) ms.WriteTo(fs)
song |> Seq.map (fun x -> x |> toInt16Sample) |> Seq.toArray |> pack |> write song |> createWAV |> writeToFile

8
SoundGen/Settings.fs Normal file
View File

@@ -0,0 +1,8 @@
module SoundGen.Settings
let sampleRate = 48000.
let bpm = 120.
let beatDuration = 60. / bpm
let pitchStandard = 440.
let volume = 0.5
let attackMs = 100.

20
SoundGen/SoundGen.fsproj Normal file
View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Settings.fs" />
<Compile Include="Synth.fs" />
<Compile Include="Fx.fs" />
<Compile Include="PCMWave.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
</Project>

74
SoundGen/Synth.fs Normal file
View File

@@ -0,0 +1,74 @@
module SoundGen.Synth
open Settings
let getHzBySemitones semi =
pitchStandard * (2. ** (1. / 12.)) ** semi
let getSemitonesByNote (str: string) =
let defaultOctave = 4
let notes =
[ "A"
"A#"
"B"
"C"
"C#"
"D"
"D#"
"E"
"F"
"F#"
"G"
"G#" ]
|> List.toArray
let octave =
str.Substring(str.Length - 1) |> int
let noteShift =
Array.findIndex (fun e -> e = str.Substring(0, str.Length - 1)) notes
(octave - defaultOctave - 1) * 12 + noteShift
let freq hz duration =
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 ->
x
|> (*) (2. * System.Math.PI * hz / sampleRate)
|> sin
|> (*) 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)