commit 5b8c7aa17b9e3e52d47285f487affb5770057aab Author: HiveBeats <38073817+HiveBeats@users.noreply.github.com> Date: Sun Oct 30 21:19:07 2022 +0400 feat: initial soundgen diff --git a/.idea/.idea.SoundGen/.idea/.gitignore b/.idea/.idea.SoundGen/.idea/.gitignore new file mode 100644 index 0000000..e7f936f --- /dev/null +++ b/.idea/.idea.SoundGen/.idea/.gitignore @@ -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 diff --git a/SoundGen/Program.fs b/SoundGen/Program.fs new file mode 100644 index 0000000..1f4d004 --- /dev/null +++ b/SoundGen/Program.fs @@ -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.) + + + \ No newline at end of file