I have a program here that plays a continuous tone. If you press the ‘r’ key, the pitch rises. If you press ‘f’, the pitch falls. If you press ‘q’, the program quits. The first while loop continuously takes in keyboard input and the second one continuously plays a tone.
Questions:
-
Here is my understanding of how the program is working: The program, running on only 1 thread, is asynchronous but not simultaneous/parallel. The program is going through the entire buffer and then coming back to the
Channel
to check if it has anything. So it’s following a cycle of play → check keyboard input → play → check keyboard input. I can see this if I change the buffer size to 41_000 (the sample rate) so that the buffer takes 1 second to play. With this longer buffer, if I typerfrf
, the sound fluctuate in pitch every second or so. Is my understanding correct? -
For some reason, when I run this with 2 threads and with the
play()
function as a task (i.e.playtone(true)
as opposed toplaytone(false)
), there seems to be a lot of lag. Some of the key presses get ignored. I also don’t need to press the return key for the program to read the keystroke. Why is this happening? -
Do I need to put the second while loop in a
Task
if I want to use multi-threaded parallel execution? I don’t think I need it if I just want asynchronous but not parallel execution. -
Do I need to use
Task
s at all if I’m not interested in parallel execution?
using PortAudio: PortAudioStream, write
function playtone(play_as_task::Bool)
# set up a task to take keyboard input
input_chnl = Channel{Char}(1)
input_task = Task() do # read keyboard input and store in Channel, stop if 'q' is typed.
while true
put!(input_chnl, read(stdin, Char))
fetch(input_chnl) == 'q' ? break : nothing # quit if `q` is typed
end
end
input_task.sticky = false
schedule(input_task)
# set up a task to play sound
i::UInt64 = 1
buffer_size::UInt64 = 1_024
buffer = Vector{Float32}(undef, buffer_size)
freq = 440
stream = PortAudioStream(0, 1; warn_xruns=false)
function play()
while true
if isready(input_chnl)
_input = take!(input_chnl)
if _input == 'q' # stop playing sound
break
else # change frequency: 'f' -> fall, 'r' -> rise
freq = _input == 'f' ? freq - 20 : _input == 'r' ? freq + 20 : freq
end
end
buffer .= sin.(2 * pi * (i .+ (1:buffer_size)) * freq / stream.sample_rate) # crude, I know.
write(stream, buffer)
i += buffer_size
end
close(stream)
close(input_chnl)
end
if play_as_task
output_task = Task(play)
output_task.sticky = false
schedule(output_task)
else
play()
end
end
# playtone(true)
playtone(false)