7 people like it.

AsciiWaveformRenderer

The ASCII wave renderer behind https://goq2q.net/blog/tech/using-ascii-waveforms-to-test-real-time-audio-code

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
namespace AsciiWaveRenderer
open System
open NAudio.Wave
open FSharp

module private AsciiWaveRenderer =
    let garbageSeq =
        let rand = new Random()
        let b = Array.zeroCreate 4
        Seq.initInfinite (fun _ ->
            rand.NextBytes b
            BitConverter.ToSingle (b, 0)
        )
        |> Seq.cache

type AsciiWaveRenderer =
    static member RenderSamples (buffer: float32[], waveFormat: WaveFormat, ?graphHeight) =
        let h = defaultArg graphHeight 10

        let channels = [|
            for ci in 0 .. waveFormat.Channels - 1 -> [|
                for si in 0 .. (buffer.Length / waveFormat.Channels) - 1 ->
                    let i = (si * waveFormat.Channels) + ci
                    buffer.[i]
            |]
        |]

        let cs =
            channels
            |> Array.map (fun samples ->
                let halfh = h/2
                let halfhf = float32 halfh
                [|
                    for hi in halfh .. -1 .. -halfh -> String([|
                        for si in 0 .. samples.Length - 1 ->
                            if hi = (int (round (samples.[si] * halfhf))) then '*' else ' '
                    |])
                |]
            )

        cs

    static member ReadAndRenderSamples ((sampleProvider: ISampleProvider), nSamples, ?buffSize, ?buffOffset, ?graphHeight, ?fillBufferWithGarbage) =
        let count = nSamples * sampleProvider.WaveFormat.Channels
        let buff = Array.zeroCreate<float32> (defaultArg buffSize count)
        let garbage = AsciiWaveRenderer.garbageSeq |> Seq.take buff.Length |> Seq.toArray
        garbage.CopyTo (buff, 0)
        let count' = sampleProvider.Read (buff, defaultArg buffOffset 0, count)
        
        AsciiWaveRenderer.RenderSamples (buff, sampleProvider.WaveFormat, ?graphHeight = graphHeight)

    static member MakeGarbageBufferForSampleReading ((sampleProvider: ISampleProvider), nSamples, ?buffSize) =
        let count = nSamples * sampleProvider.WaveFormat.Channels
        let buff = Array.zeroCreate<float32> (defaultArg buffSize count)
        let garbage = AsciiWaveRenderer.garbageSeq |> Seq.take buff.Length |> Seq.toArray
        garbage.CopyTo (buff, 0)
        
        buff
Multiple items
module AsciiWaveRenderer

from AsciiWaveRenderer

--------------------
type AsciiWaveRenderer =
  static member MakeGarbageBufferForSampleReading : sampleProvider:ISampleProvider * nSamples:int * ?buffSize:int -> float32 []
  static member ReadAndRenderSamples : sampleProvider:ISampleProvider * nSamples:int * ?buffSize:int * ?buffOffset:int * ?graphHeight:int * ?fillBufferWithGarbage:'a -> String [] []
  static member RenderSamples : buffer:float32 [] * waveFormat:WaveFormat * ?graphHeight:int -> String [] []
namespace System
namespace NAudio
namespace NAudio.Wave
namespace Microsoft.FSharp
val private garbageSeq : seq<float32>
val rand : Random
Multiple items
type Random =
  new : unit -> Random + 1 overload
  member Next : unit -> int + 2 overloads
  member NextBytes : buffer:byte[] -> unit + 1 overload
  member NextDouble : unit -> float

--------------------
Random() : Random
Random(Seed: int) : Random
val b : byte []
type Array =
  member Clone : unit -> obj
  member CopyTo : array:Array * index:int -> unit + 1 overload
  member GetEnumerator : unit -> IEnumerator
  member GetLength : dimension:int -> int
  member GetLongLength : dimension:int -> int64
  member GetLowerBound : dimension:int -> int
  member GetUpperBound : dimension:int -> int
  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
  member Initialize : unit -> unit
  member IsFixedSize : bool
  ...
val zeroCreate : count:int -> 'T []
module Seq

from Microsoft.FSharp.Collections
val initInfinite : initializer:(int -> 'T) -> seq<'T>
type BitConverter =
  static val IsLittleEndian : bool
  static member DoubleToInt64Bits : value:float -> int64
  static member GetBytes : value:bool -> byte[] + 9 overloads
  static member Int32BitsToSingle : value:int -> float32
  static member Int64BitsToDouble : value:int64 -> float
  static member SingleToInt32Bits : value:float32 -> int
  static member ToBoolean : value:ReadOnlySpan<byte> -> bool + 1 overload
  static member ToChar : value:ReadOnlySpan<byte> -> char + 1 overload
  static member ToDouble : value:ReadOnlySpan<byte> -> float + 1 overload
  static member ToInt16 : value:ReadOnlySpan<byte> -> int16 + 1 overload
  ...
BitConverter.ToSingle(value: ReadOnlySpan<byte>) : float32
BitConverter.ToSingle(value: byte [], startIndex: int) : float32
val cache : source:seq<'T> -> seq<'T>
module AsciiWaveRenderer

from AsciiWaveRenderer
val buffer : float32 []
Multiple items
val float32 : value:'T -> float32 (requires member op_Explicit)

--------------------
type float32 = Single

--------------------
type float32<'Measure> = float32
val waveFormat : WaveFormat
Multiple items
type WaveFormat =
  new : unit -> WaveFormat + 3 overloads
  member AverageBytesPerSecond : int
  member BitsPerSample : int
  member BlockAlign : int
  member Channels : int
  member ConvertLatencyToByteSize : milliseconds:int -> int
  member Encoding : WaveFormatEncoding
  member Equals : obj:obj -> bool
  member ExtraSize : int
  member GetHashCode : unit -> int
  ...

--------------------
WaveFormat() : WaveFormat
WaveFormat(br: IO.BinaryReader) : WaveFormat
WaveFormat(sampleRate: int, channels: int) : WaveFormat
WaveFormat(rate: int, bits: int, channels: int) : WaveFormat
val graphHeight : int option
val h : int
val defaultArg : arg:'T option -> defaultValue:'T -> 'T
val channels : float32 [] []
val ci : int
val si : int
val i : int
val cs : String [] []
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []
val samples : float32 []
val halfh : int
val halfhf : float32
val hi : int
Multiple items
type String =
  new : value:char[] -> string + 8 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool + 3 overloads
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 3 overloads
  member EnumerateRunes : unit -> StringRuneEnumerator
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  ...

--------------------
String(value: char []) : String
String(value: nativeptr<char>) : String
String(value: nativeptr<sbyte>) : String
String(value: ReadOnlySpan<char>) : String
String(c: char, count: int) : String
String(value: char [], startIndex: int, length: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
Multiple items
val int : value:'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val round : value:'T -> 'T (requires member Round)
val sampleProvider : ISampleProvider
type ISampleProvider =
  member Read : buffer:float32[] * offset:int * count:int -> int
  member WaveFormat : WaveFormat
val nSamples : int
val buffSize : int option
val buffOffset : int option
val fillBufferWithGarbage : 'a option
val count : int
val buff : float32 []
val garbage : float32 []
val take : count:int -> source:seq<'T> -> seq<'T>
val toArray : source:seq<'T> -> 'T []
val count' : int
static member AsciiWaveRenderer.RenderSamples : buffer:float32 [] * waveFormat:WaveFormat * ?graphHeight:int -> String [] []
Raw view Test code New version

More information

Link:http://fssnip.net/85g
Posted:2 years ago
Author:John Wostenberg
Tags: #audio