/examples/asyncio_generators.py
https://github.com/spatialaudio/python-sounddevice · Python · 108 lines · 65 code · 18 blank · 25 comment · 17 complexity · 48df930af50d43e28cd3e82ec2bdd7ed MD5 · raw file
- #!/usr/bin/env python3
- """Creating an asyncio generator for blocks of audio data.
- This example shows how a generator can be used to analyze audio input blocks.
- In addition, it shows how a generator can be created that yields not only input
- blocks but also output blocks where audio data can be written to.
- You need Python 3.7 or newer to run this.
- """
- import asyncio
- import queue
- import sys
- import numpy as np
- import sounddevice as sd
- async def inputstream_generator(channels=1, **kwargs):
- """Generator that yields blocks of input data as NumPy arrays."""
- q_in = asyncio.Queue()
- loop = asyncio.get_event_loop()
- def callback(indata, frame_count, time_info, status):
- loop.call_soon_threadsafe(q_in.put_nowait, (indata.copy(), status))
- stream = sd.InputStream(callback=callback, channels=channels, **kwargs)
- with stream:
- while True:
- indata, status = await q_in.get()
- yield indata, status
- async def stream_generator(blocksize, *, channels=1, dtype='float32',
- pre_fill_blocks=10, **kwargs):
- """Generator that yields blocks of input/output data as NumPy arrays.
- The output blocks are uninitialized and have to be filled with
- appropriate audio signals.
- """
- assert blocksize != 0
- q_in = asyncio.Queue()
- q_out = queue.Queue()
- loop = asyncio.get_event_loop()
- def callback(indata, outdata, frame_count, time_info, status):
- loop.call_soon_threadsafe(q_in.put_nowait, (indata.copy(), status))
- outdata[:] = q_out.get_nowait()
- # pre-fill output queue
- for _ in range(pre_fill_blocks):
- q_out.put(np.zeros((blocksize, channels), dtype=dtype))
- stream = sd.Stream(blocksize=blocksize, callback=callback, dtype=dtype,
- channels=channels, **kwargs)
- with stream:
- while True:
- indata, status = await q_in.get()
- outdata = np.empty((blocksize, channels), dtype=dtype)
- yield indata, outdata, status
- q_out.put_nowait(outdata)
- async def print_input_infos(**kwargs):
- """Show minimum and maximum value of each incoming audio block."""
- async for indata, status in inputstream_generator(**kwargs):
- if status:
- print(status)
- print('min:', indata.min(), '\t', 'max:', indata.max())
- async def wire_coro(**kwargs):
- """Create a connection between audio inputs and outputs.
- Asynchronously iterates over a stream generator and for each block
- simply copies the input data into the output block.
- """
- async for indata, outdata, status in stream_generator(**kwargs):
- if status:
- print(status)
- outdata[:] = indata
- async def main(**kwargs):
- print('Some informations about the input signal:')
- try:
- await asyncio.wait_for(print_input_infos(), timeout=2)
- except asyncio.TimeoutError:
- pass
- print('\nEnough of that, activating wire ...\n')
- audio_task = asyncio.create_task(wire_coro(**kwargs))
- for i in range(10, 0, -1):
- print(i)
- await asyncio.sleep(1)
- audio_task.cancel()
- try:
- await audio_task
- except asyncio.CancelledError:
- print('\nwire was cancelled')
- if __name__ == "__main__":
- try:
- asyncio.run(main(blocksize=1024))
- except KeyboardInterrupt:
- sys.exit('\nInterrupted by user')