feat: initial soundgen
This commit is contained in:
13
.idea/.idea.SoundGen/.idea/.gitignore
generated
vendored
Normal file
13
.idea/.idea.SoundGen/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider ignored files
|
||||
/modules.xml
|
||||
/contentModel.xml
|
||||
/projectSettingsUpdater.xml
|
||||
/.idea.SoundGen.iml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
224
SoundGen/Program.fs
Normal file
224
SoundGen/Program.fs
Normal file
@@ -0,0 +1,224 @@
|
||||
module SoundGen
|
||||
open System.IO
|
||||
|
||||
let sampleRate = 48000.
|
||||
let bpm = 128.
|
||||
let beatDuration = 60. / bpm
|
||||
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 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) |> toInt16Sample)
|
||||
|
||||
|
||||
let note semitone beats =
|
||||
let hz = getHzBySemitones semitone
|
||||
freq hz (beats * beatDuration)
|
||||
|
||||
let song =
|
||||
[
|
||||
//
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.5
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25
|
||||
note 0 0.25;
|
||||
note 0 0.5;
|
||||
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25
|
||||
note 5 0.25;
|
||||
note 5 0.5;
|
||||
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25
|
||||
note 3 0.25;
|
||||
note 3 0.5
|
||||
note (-2) 0.5
|
||||
//
|
||||
//
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.5
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25
|
||||
note 0 0.25;
|
||||
note 0 0.5;
|
||||
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25
|
||||
note 5 0.25;
|
||||
note 5 0.5;
|
||||
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25
|
||||
note 3 0.25;
|
||||
note 3 0.5
|
||||
note (-2) 0.5
|
||||
//
|
||||
//
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.5
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25
|
||||
note 0 0.25;
|
||||
note 0 0.5;
|
||||
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25
|
||||
note 5 0.25;
|
||||
note 5 0.5;
|
||||
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25
|
||||
note 3 0.25;
|
||||
note 3 0.5
|
||||
note (-2) 0.5
|
||||
//
|
||||
//
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.5
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25;
|
||||
note 0 0.25
|
||||
note 0 0.25;
|
||||
note 0 0.5;
|
||||
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25;
|
||||
note 5 0.25
|
||||
note 5 0.25;
|
||||
note 5 0.5;
|
||||
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25;
|
||||
note 3 0.25
|
||||
note 3 0.25;
|
||||
note 3 0.5
|
||||
note (-2) 0.5
|
||||
//
|
||||
] |> Seq.concat
|
||||
|
||||
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 write (ms:MemoryStream) =
|
||||
use fs = new FileStream(Path.Combine(__SOURCE_DIRECTORY__,"test.wav"), FileMode.Create)
|
||||
ms.WriteTo(fs)
|
||||
|
||||
song |> Seq.toArray |> pack |> write
|
||||
|
||||
|
||||
|
||||
|
||||
// let drawWave(freq, duration) =
|
||||
// Chart.Line(seq { 1. .. (duration * sampleRate) }, sound)
|
||||
// |> Chart.saveHtml("chart.html")
|
||||
// drawWave(440., 1.)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user