ssfrr / AudioIO.jl

[UNMAINTAINED] - Simple Audio IO in Julia
MIT License
61 stars 24 forks source link

Real Time audio recording examples missing #47

Open Ownv94 opened 9 years ago

Ownv94 commented 9 years ago

Excuse me, I am working about Digital Signal Processing specifically Modulation and I want to record my voice in real time but I don't know how do that. I tried to look for something in "AudioIO.jl" about my problem but nothing. Could you make some examples about how record audio in real time please ?

PD: My OS: Windows 8 and a VM: Ubuntu 14.0.4

rob-luke commented 8 years ago

I just started playing with the package myself, no idea if this is the correct way to record from the input, but it works for me. I'd appreciate input from anyone familiar with the module (@ssfrr, @wherrera10, @JayKickliter, @goretkin).

using AudioIO

# Record for 11 seconds
secs = 11

type RecordingRenderer <: AudioIO.AudioRenderer
    active::Bool
    storage::Array{Float32}
    idx::Int

    function RecordingRenderer(length_s::Int)
        new(false, zeros(Float32, 44100 * length_s), 1)
    end
end
typealias RecordingNode AudioIO.AudioNode{RecordingRenderer}

idx = 1
import AudioIO.render
function render(node::RecordingRenderer, device_input::AudioIO.AudioBuf, info::AudioIO.DeviceInfo)
    len = 1024
    idx = node.idx
    node.storage[idx : idx + len - 1] = device_input
    node.idx += len
    return device_input .* false
end

myNode = RecordingNode(secs)
println("Start recording")
AudioIO.play(myNode); sleep(secs); stop(myNode)

println("Plot recording")
using UnicodePlots
display(lineplot(1:length(myNode.renderer.storage), myNode.renderer.storage, width = 50))

println("Start playback")
sleep(1)
player = ArrayPlayer(myNode.renderer.storage)
play(player)
sleep(secs)

output...

Start recording
INFO: Initializing PortAudio. Expect errors as we scan devices
INFO: Scheduling PortAudio Render Task...
INFO: PortAudio Render Task Running...
Plot recording
         ┌─────────────────────────────────────────────────────────┐
    0.26 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢀⠀⠀⠀⠀⠀⠀⠀⠀⡆⠀⠀⠀⠀│
         │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⠀⠀⠀⠀⠀⡀⠀⠀⡇⠀⠀⠀⠀│
         │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⡆⢠⠀⠀⢠⣷⠀⠀⡇⢸⠀⠀⠀│
         │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣸⣿⡇⣾⣇⣴⢸⣿⡆⡇⡇⢸⠀⠀⠀│
         │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣷⣿⣿⣿⢸⣿⡇⡇⡇⢸⠀⡀⠀│
         │⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣇⡇⡇⢸⠀⡇⠀│
         │⣸⡆⠀⠀⠀⠀⠀⠀⢀⣀⣴⣿⣤⣿⣷⣇⡀⣄⠀⢰⠀⠀⠀⠀⠀⢀⠀⠀⠀⢰⣆⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⢸⣾⣧⡆│
         │⣿⣿⣶⣦⠤⠤⠤⠤⢼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⡤⠤⠤⠤⠤⢼⣧⠤⡤⢼⣿⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣾⣿⣿⡇│
         │⢻⡏⠀⠀⠀⠀⠀⠀⢸⡟⠿⣿⣿⣿⣿⡟⠋⠟⠁⢸⠀⠀⠀⠀⠀⠘⠁⠀⠀⢸⠟⠀⠀⠸⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢹⣿⣿⠇│
         │⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠀⣿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⡇⢸⠁⡇⠀│
         │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⢹⣿⡇⡇⡇⢸⠀⠁⠀│
         │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⡟⣿⡟⢹⢸⣿⡇⡇⡇⢸⠀⠀⠀│
         │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⡇⢹⠁⠈⢸⣿⠀⡇⡇⢸⠀⠀⠀│
         │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠃⠈⠀⠀⠀⡟⠀⠃⡇⢸⠀⠀⠀│
   -0.25 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠁⠀⠀⠀⠀⠀⠀⠀⠀⠇⢸⠀⠀⠀│
         └─────────────────────────────────────────────────────────┘
         0                                             490000
Start playback
wherrera10 commented 8 years ago

That is a very nice plot!

A fork of AudioIO which I've created uses more simple functions, as in read and write. An example:

# -*- coding: utf-8 -*-

# Examples for Julia AudioIO module

using AudioIO
using WAV

CHUNKSIZE = 40960
FORMAT = AudioIO.paInt16
CHANNELS = 2
SRATE = 44100
RECORD_SECONDS = 20

"""
choose the devices for 2 channel IO
"""
function choose_input_output()
    devices = get_audio_devices()
    indev = -1
    outdev = -1
    for aud in devices
        println("$(aud.device_index) $(aud.name)")
        if (aud.max_input_channels == CHANNELS) & (indev == -1 )
            indev = aud.device_index
        end
        if(aud.max_output_channels == CHANNELS) & (outdev == -1)
            outdev = aud.device_index
        end
    end
    if indev == -1
        info("Appropriate input device not found.")
    elseif outdev == -1
        info("Appropriate output device not found.")
    else
        info("Using input device ", bytestring(devices[indev + 1].name), 
             ", number ", devices[indev + 1].device_index, 
             " and output device ", bytestring(devices[outdev + 1].name), 
             ", number ", devices[outdev + 1].device_index)
    end
    return indev, outdev
end

"""
read from input
"""
function record_audio(devnum, seconds)
    instream = AudioIO.open_read(devnum, CHANNELS, SRATE, CHUNKSIZE)
    bufsize = seconds * SRATE
    buf = AudioIO.read(instream, bufsize)
    AudioIO.Pa_CloseStream(instream.stream)
    buf
end

"""
write to WAV file
"""
function write_as_WAV(buffer, filename="temp.WAV")
    fio = open(filename, "w")
    WAV.wavwrite(deinterlace_stereo(buffer), fio, Fs=SRATE)
end

"""
interlaced to noninterlaced stereo data
"""
function deinterlace_stereo(buffer)
    reshape([buffer[1:2:end]; buffer[2:2:end]], 
            (floor(Int, length(buffer)/2), 2))
end

INS, OUTS = choose_input_output()

println("Starting recording...")
BUF = record_audio(INS, RECORD_SECONDS)
println("Finished reading from device number $INS")
println("Recording volume was $(mean(abs(BUF))*(100/16783))% of max")

write_as_WAV(BUF)
println("Finished writing to WAV file.")
rob-luke commented 8 years ago

Nice, I hadn't looked deep enough in to your fork then. But both our previous examples record and then play back. Not really suitable for the original question of real time processing.

Here is an example of processing the incoming audio in real time using Reactive.jl (@shashi) and plotting a naive level meter.

I'd love any suggestions on how to improve this acquisition code.

using AudioIO, Reactive

# Set up recording node
#
type RecordingRenderer <: AudioIO.AudioRenderer
    function RecordingRenderer()
        new()
    end
end
typealias RecordingNode AudioIO.AudioNode{RecordingRenderer}

x = Input(convert(Float32, 0.0))
import AudioIO.render
function render(node::RecordingRenderer, device_input::AudioIO.AudioBuf, info::AudioIO.DeviceInfo)
    push!(x, mean(abs(device_input)))
    return device_input .* false
end

# Basic processing
#
SMA(N::Int, buffer = Float32[]) =
 x -> begin
        push!(buffer, x)
        if length(buffer) == N+1 shift!(buffer) end
        round(Int, mean(buffer))
      end
moving_average = SMA(5)

# Run processing
#
myNode = RecordingNode()
println("Start recording")
AudioIO.play(myNode)

t_int = 1/20
solidglyph="█"
hist_vals = zeros(Float32, 4)

for t in 1:t_int:30
    print("\u1b[1G")
    print("\u1b[K")
    val = moving_average(1000*value(x))
    print(string(val, "  |", repeat(solidglyph, val)))
    sleep(t_int)
end
stop(myNode)

Output

INFO: Scheduling PortAudio Render Task...
INFO: PortAudio Render Task Running...
8  |████████
ssfrr commented 8 years ago

super cool, and I'm glad to see more audio-focused folks using Julia these days! There's currently an AudioInput node defined in nodes.jl that you should be able to pipe to speakers just with play(AudioInput()) (watch out for feedback!)

One cool way to implement this would be an EnvelopeFollower node that could take any other node as input, so you could do EnvelopeFollower(AudioInput()) or EnvelopeFollower(SomeOtherNode())

rob-luke commented 8 years ago

I think PR #57 addresses this issue.