Acoustic signal processing module that can be used with Python-Sounddevice ASIO [Application]

Introduction

This article is a continuation of Sound signal processing module that can be used with Python-Sounddevice ASIO [Basic]. Please see here for the initial setting method for using sounddevice from the preparation before using it, and the basic playback method, recording method, simultaneous recording / playback method, and streaming method for sounddevice.

Items covered in this article

In this article, I would like to write about the channel mapping method using sound device (input from your favorite microphone and output from your favorite speaker in a multi-channel environment).

See here for details Python sound device official

Postscript

With the update, it seems that the operation has become easier. The following articles describe older versions, so check the official documentation.

Basic method of channel mapping

For example, suppose you have a speaker array like this スクリーンショット 2019-12-04 17.45.36.png If you want to output the sound source of 5 channels in order from the speakers of 5 channels, there is no problem because the sound device side automatically allocates the channels.

If you want to output the sound source of 1 channel from any speaker (3rd speaker this time) 1 channel, do as follows.

MonoChannelPlayback.py


out = [-1,-1,0,-1,-1]
sd.play(..., mapping = out ,...)

Next, if you want to output the sound source of 3 channels from any speaker (2nd, 3rd, 5th speaker this time) 3 channel, do as follows.

MultiChannelsPlayback.py


out = [-1,0,1,-1,2]
sd.play(..., mapping = out ,...)

In other words, if you make a list with [-1] for the speakers you do not want to output and [0] [1] [2] .. for the speakers you want to output, and put them in mapping, they will be assigned. Also,

MultiChannelsPlayback.py


out = [-1,1,0,-1,2]
sd.play(..., mapping = out ,...)

You can change the order of the speakers that produce sound.

Settings for each API

This section describes the setting method when controlling input / output through unique APIs such as ASIO, Core Audio, and WASAPI. To check the API currently supported by your machine [Initial settings of equipment used](https://qiita.com/MikuMaekawa/items/aaac6df5d6cee2f8cf71#%E4%BD%BF%E7%94%A8% E6% A9% 9F% E6% 9D% 90% E3% 81% AE% E8% A8% AD% E5% AE% 9A) or try the following command.

checkAPI.py


sd.query_hostapis(index=None)

Return value

({'name': 'Core Audio',
  'devices': [0, 1, 2],
  'default_input_device': 0,
  'default_output_device': 1},)

ASIO Click here for channel mapping settings in ASIO. This is controlled assuming that there is an environment where the speaker output is 5 channels and the microphone input is 2 channels. スクリーンショット 2019-12-05 14.11.48.png

ASIOMultiChannnelsControl.py


out = [0,1,2,3,4]
in = [0,1]

asio_out = sd.AsioSettings(channel_map = out)
asio_in = sd.AsioSettings(channel_map = in)

#Regeneration
sd.play(..., extra_settings=asio_out)

#recording
recdata = sd.rec(..., channels=2, extra_settings=asio_in,...)

#Simultaneous recording / playback
recdata = sd.playrec(...,channels=2, extra_settings=(asio_in,asio_out),... )

You can also set extra_setting as the default if you know you will use the same settings all the time.

default_map.py


out = [0,1,2,3,4]
in = [0,1]

asio_out = sd.AsioSettings(channel_map = out)
asio_in = sd.AsioSettings(channel_map = in)

sd.default.extra_settings = (asio_in,asio_out)

Core Audio The method is almost the same as that of ASIO.

CoreAudioMultiChannnelsControl.py


out = [0,1,2,3,4]
in = [0,1]

ca_out = sd.CoreAudioSettings(channel_map = out)
ca_in = sd.CoreAudioSettings(channel_map = in)

#Regeneration
sd.play(..., extra_settings=ca_out)

#recording
recdata = sd.rec(..., channels=2, extra_settings=ca_in,...)

#Simultaneous recording / playback
recdata = sd.playrec(...,channels=2, extra_settings=(ca_in,ca_out),... )

WASAPI The same is true for WASAPI

WasapiMultiChannnelsControl.py


out = [0,1,2,3,4]
in = [0,1]

wasapi_out = sd.WasapiSettings(channel_map = out)
wasapi_in = sd.WasapiSettings(channel_map = in)

#Regeneration
sd.play(..., extra_settings=wasapi_out)

#recording
recdata = sd.rec(..., channels=2, extra_settings=wasapi_in,...)

#Simultaneous recording / playback
recdata = sd.playrec(...,channels=2, extra_settings=(wasapi_in,wasapi_out),... )

Finally

This concludes the explanation of sound device. Hopefully, the number of sounddevice users will increase in the world, and I hope that various super-convenient and strongest programs that use sounddevice will overflow in the world.

Finally, I will say goodbye to the program that automatically finds the impulse response from the multi-channel speaker array I used to the dummy head without going through DAW software. Thank you for your hard work.

Sample impulse response measurement program

I will write a commentary article when I feel like it, but maybe people who use it do not need commentary ...

InpulseResponse.py


import numpy as np
import scipy as sp
import sounddevice as sd
import wave
import struct

#Setting
Fs=44100
small=1000000 #Adjust the volume to 1 / small because the sound of the prepared wav file was too loud

#sounddevise Setting
sd.default.samplerate=Fs
print(sd.query_devices())
#A list of connected devices will appear, so enter the device ID.
deviceNo = input("Please enter the ID of the device to use  :" )
sd.default.device = int(deviceNo)

area = 1

#mapping
out1 = sd.CoreAudioSettings(channel_map=[0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1])
out2 = sd.CoreAudioSettings(channel_map=[-1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1])
out3 = sd.CoreAudioSettings(channel_map=[-1,-1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1])
out4 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,0,-1,-1,-1,-1,-1,-1,-1,-1])
out5 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,-1,0,-1,-1,-1,-1,-1,-1,-1])
out6 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,-1,-1,0,-1,-1,-1,-1,-1,-1])
out7 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,-1,-1,-1,0,-1,-1,-1,-1,-1])
out8 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,-1,-1,-1,-1,0,-1,-1,-1,-1])
out9 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,-1,-1,-1,-1,-1,0,-1,-1,-1])
out10 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,-1,-1,-1,-1,-1,-1,0,-1,-1])
out11 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,-1])
out12 = sd.CoreAudioSettings(channel_map=[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0])

ca_in = sd.CoreAudioSettings(channel_map=[0,1])

def wavread(filename):#Reading 16bit WAVE files
    wf = wave.open(filename,'rb')
    buf = wf.readframes(wf.getnframes())
    data = np.frombuffer(buf, dtype = "int16")
    return data

TSP=wavread("tsp.wav")/small 
#Put in white noise or Swept Sine signal (TSP) signal

#Compute the Convolution impulse response
def IR(rTSPdata,outputfilename):
    rTSPdata=rTSPdata.T
    ipls=np.real(sp.ifft(sp.fft(rTSPdata)*sp.fft(np.flipud(TSP),rTSPdata.size)))
    c=np.fft.fftshift(ipls/max(ipls))
    int16amp=32768 / int(c.max())
    y2 = np.array([c * int16amp],dtype = "int16")[0]
    y4 = struct.pack("h" * len(y2), *y2)
    w = wave.Wave_write(outputfilename)
    w.setparams((
            1,                        # channel
            2,                        # byte width
            44100,                    # sampling rate
            len(y4),                 # number of frames
            "NONE", "NONE"  # no compression
        ))
    w.writeframesraw(y4)
    w.close()

#Playrec
def PlayRec(outmap,IDnumber):
    #sd.play(TSP,Fs,extra_settings=outmap,blocking=True)
    TSP1=sd.playrec(TSP,Fs,channels=2,extra_settings=(ca_in,outmap),blocking=True)
    TSP2=sd.playrec(TSP,Fs,channels=2,extra_settings=(ca_in,outmap),blocking=True)
    TSP3=sd.playrec(TSP,Fs,channels=2,extra_settings=(ca_in,outmap),blocking=True)

    #We are doing additive polymerization, but recently we have prepared a long white noise or Swept Sine signal (TSP signal).
    #It seems that the mainstream is to find it in one shot without additive polymerization.
    
    rTSP = TSP1 + TSP2 + TSP3

    #Invert the recorded response to find the IR
    rTSP1 = rTSP[:,0]
    rTSP2 = rTSP[:,1]
    rTSP3 = rTSP[:,2]
    rTSP4 = rTSP[:,3]
    rTSP5 = rTSP[:,4]
    rTSP6 = rTSP[:,5]
    rTSP7 = rTSP[:,6]
    rTSP8 = rTSP[:,7]
    rTSP9 = rTSP[:,8]
    rTSP10 = rTSP[:,9]
    
    #Name the resulting impulse response and write it as a WAV file to a folder
    IR(rTSP1,"IR"+str(IDnumber)+"1.wav")
    IR(rTSP2,"IR"+str(IDnumber)+"2.wav")
    IR(rTSP3,"IR"+str(IDnumber)+"3.wav")
    IR(rTSP4,"IR"+str(IDnumber)+"4.wav")
    IR(rTSP5,"IR"+str(IDnumber)+"5.wav")
    IR(rTSP6,"IR"+str(IDnumber)+"6.wav")
    IR(rTSP7,"IR"+str(IDnumber)+"7.wav")
    IR(rTSP8,"IR"+str(IDnumber)+"8.wav")
    IR(rTSP9,"IR"+str(IDnumber)+"9.wav")
    IR(rTSP10,"IR"+str(IDnumber)+"10.wav")

print("Start measurement")
PlayRec(out1,1)
PlayRec(out2,2)
PlayRec(out3,3)
PlayRec(out4,4)
PlayRec(out5,5)
PlayRec(out6,6)
PlayRec(out7,7)
PlayRec(out8,8)
PlayRec(out9,9)
PlayRec(out10,10)
PlayRec(out11,11)
PlayRec(out12,12)
print("End of measurement")

reference

Python sound device official Sound signal processing module that can be used with Python-Sounddevice ASIO [Basic]

Recommended Posts

Acoustic signal processing module that can be used with Python-Sounddevice ASIO [Application]
Acoustic signal processing module that can be used with Python-Sounddevice ASIO [Basic]
File types that can be used with Go
Python knowledge notes that can be used with AtCoder
Acoustic signal processing with Python (2)
Acoustic signal processing with Python
Python standard module that can be used on the command line
Easy padding of data that can be used in natural language processing
Mathematical optimization that can be used for free work with Python + PuLP
Until youtube-dl can be used with Synology (DS120j)
Functions that can be used in for statements
I made a familiar function that can be used in statistics with Python
No module named'distutils.util' cannot be used with get-pip.py
List packages that can be updated with pip
"Gazpacho", a scraping module that can be used more easily than Beautiful Soup
Convert images from FlyCapture SDK to a form that can be used with openCV
[Python] Introduction to web scraping | Summary of methods that can be used with webdriver
File sharing server made with Raspberry Pi that can be used for remote work
Basic algorithms that can be used in competition pros
Japanese can be used with Python in Docker environment
Color list that can be set with tkinter (memorial)
ANTs image registration that can be used in 5 minutes
[Django] About users that can be used on template
Limits that can be analyzed at once with MeCab
Format summary of formats that can be serialized with gensim
It seems that Skeleton Tracking can be done with RealSense
pd.tseries.offsets.DateOffset can be quite slow if not used with caution
Goroutine (parallel control) that can be used in the field
Goroutine that can be used in the field (errgroup.Group edition)
SSD 1306 OLED can be used with Raspberry Pi + python (Note)
Scripts that can be used when using bottle in Python
Acoustic signal processing starting with Python-Let's make a stereophonic system
I investigated the pretreatment that can be done with PyCaret
Let's make a diagram that can be clicked with IPython
I tried to expand the database so that it can be used with PES analysis software
Understand the probabilities and statistics that can be used for progress management with a python program
About the matter that torch summary can be really used when building a model with Pytorch
[Python] Make a graph that can be moved around with Plotly
Until torch-geometric can be used only with Windows (or Mac) CPU
Make a Spinbox that can be displayed in Binary with Tkinter
A timer (ticker) that can be used in the field (can be used anywhere)
I made a shuffle that can be reset (reverted) with Python
IOS application development with Python (kivy) that even monkeys can do
Make a currency chart that can be moved around with Plotly (2)
Python standard input summary that can be used in competition pro
Comparison of 4 styles that can be passed to seaborn with set_context
Make a Spinbox that can be displayed in HEX with Tkinter
Make a currency chart that can be moved around with Plotly (1)