acoular 24.3__py3-none-any.whl → 24.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acoular/__init__.py +119 -54
- acoular/calib.py +29 -38
- acoular/configuration.py +132 -82
- acoular/demo/__init__.py +10 -4
- acoular/demo/acoular_demo.py +73 -55
- acoular/environments.py +270 -264
- acoular/fastFuncs.py +366 -196
- acoular/fbeamform.py +1797 -1934
- acoular/grids.py +504 -548
- acoular/h5cache.py +74 -83
- acoular/h5files.py +159 -142
- acoular/internal.py +13 -14
- acoular/microphones.py +57 -53
- acoular/sdinput.py +57 -53
- acoular/signals.py +180 -178
- acoular/sources.py +920 -724
- acoular/spectra.py +353 -363
- acoular/tbeamform.py +416 -416
- acoular/tfastfuncs.py +180 -104
- acoular/tools/__init__.py +25 -0
- acoular/tools/aiaa.py +185 -0
- acoular/tools/helpers.py +189 -0
- acoular/tools/metrics.py +165 -0
- acoular/tprocess.py +1240 -1182
- acoular/traitsviews.py +513 -501
- acoular/trajectory.py +50 -52
- acoular/version.py +5 -6
- acoular/xml/minidsp_uma-16.xml +20 -0
- acoular/xml/{minidsp_uma16.xml → minidsp_uma-16_mirrored.xml} +3 -0
- {acoular-24.3.dist-info → acoular-24.7.dist-info}/METADATA +58 -39
- acoular-24.7.dist-info/RECORD +50 -0
- {acoular-24.3.dist-info → acoular-24.7.dist-info}/WHEEL +1 -1
- acoular-24.7.dist-info/licenses/LICENSE +28 -0
- acoular/fileimport.py +0 -380
- acoular/nidaqimport.py +0 -273
- acoular/tests/reference_data/BeamformerBase.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFLassoLarsBIC.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFNNLS.npy +0 -0
- acoular/tests/reference_data/BeamformerCapon.npy +0 -0
- acoular/tests/reference_data/BeamformerClean.npy +0 -0
- acoular/tests/reference_data/BeamformerCleansc.npy +0 -0
- acoular/tests/reference_data/BeamformerCleant.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSq.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerDamas.npy +0 -0
- acoular/tests/reference_data/BeamformerDamasPlus.npy +0 -0
- acoular/tests/reference_data/BeamformerEig.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerFunctional.npy +0 -0
- acoular/tests/reference_data/BeamformerGIB.npy +0 -0
- acoular/tests/reference_data/BeamformerGridlessOrth.npy +0 -0
- acoular/tests/reference_data/BeamformerMusic.npy +0 -0
- acoular/tests/reference_data/BeamformerOrth.npy +0 -0
- acoular/tests/reference_data/BeamformerSODIX.npy +0 -0
- acoular/tests/reference_data/BeamformerTime.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeSq.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeTraj.npy +0 -0
- acoular/tests/reference_data/Environment.npy +0 -0
- acoular/tests/reference_data/Example1_numerical_values_testsum.h5 +0 -0
- acoular/tests/reference_data/FiltFiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltFiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_A_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_C_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_Z_.npy +0 -0
- acoular/tests/reference_data/FiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/Filter__.npy +0 -0
- acoular/tests/reference_data/GeneralFlowEnvironment.npy +0 -0
- acoular/tests/reference_data/OctaveFilterBank__.npy +0 -0
- acoular/tests/reference_data/OpenJet.npy +0 -0
- acoular/tests/reference_data/PointSource.npy +0 -0
- acoular/tests/reference_data/PowerSpectra_csm.npy +0 -0
- acoular/tests/reference_data/PowerSpectra_ev.npy +0 -0
- acoular/tests/reference_data/RotatingFlow.npy +0 -0
- acoular/tests/reference_data/SlotJet.npy +0 -0
- acoular/tests/reference_data/TimeAverage__.npy +0 -0
- acoular/tests/reference_data/TimeCumAverage__.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_F_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_I_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_S_.npy +0 -0
- acoular/tests/reference_data/TimeInOut__.npy +0 -0
- acoular/tests/reference_data/TimePower__.npy +0 -0
- acoular/tests/reference_data/TimeReverse__.npy +0 -0
- acoular/tests/reference_data/UniformFlowEnvironment.npy +0 -0
- acoular/tests/reference_data/beamformer_traj_time_data.h5 +0 -0
- acoular/tests/run_tests.sh +0 -18
- acoular/tests/run_tests_osx.sh +0 -16
- acoular/tests/test.npy +0 -0
- acoular/tests/test_beamformer_results.py +0 -213
- acoular/tests/test_classes.py +0 -60
- acoular/tests/test_digest.py +0 -125
- acoular/tests/test_environments.py +0 -73
- acoular/tests/test_example1.py +0 -124
- acoular/tests/test_grid.py +0 -92
- acoular/tests/test_integrate.py +0 -102
- acoular/tests/test_signals.py +0 -60
- acoular/tests/test_sources.py +0 -65
- acoular/tests/test_spectra.py +0 -38
- acoular/tests/test_timecache.py +0 -35
- acoular/tests/test_tprocess.py +0 -90
- acoular/tests/test_traj_beamformer_results.py +0 -164
- acoular/tests/unsupported/SpeedComparison/OvernightTestcasesBeamformer_nMics32_nGridPoints100_nFreqs4_nTrials10.png +0 -0
- acoular/tests/unsupported/SpeedComparison/cythonBeamformer.pyx +0 -237
- acoular/tests/unsupported/SpeedComparison/mainForCython.py +0 -103
- acoular/tests/unsupported/SpeedComparison/mainForParallelJit.py +0 -143
- acoular/tests/unsupported/SpeedComparison/setupCythonOpenMP.py +0 -63
- acoular/tests/unsupported/SpeedComparison/sharedFunctions.py +0 -153
- acoular/tests/unsupported/SpeedComparison/timeOverNMics_AllImportantMethods.png +0 -0
- acoular/tests/unsupported/SpeedComparison/timeOverNMics_faverage.png +0 -0
- acoular/tests/unsupported/SpeedComparison/vglOptimierungFAverage.py +0 -204
- acoular/tests/unsupported/SpeedComparison/vglOptimierungGaussSeidel.py +0 -182
- acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAMFULL_INVERSE.py +0 -764
- acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAM_OS.py +0 -231
- acoular/tests/unsupported/SpeedComparison/whatsFastestWayFor_absASquared.py +0 -48
- acoular/tests/unsupported/functionalBeamformer.py +0 -123
- acoular/tests/unsupported/precisionTest.py +0 -153
- acoular/tests/unsupported/validationOfBeamformerFuncsPOSTAcoularIntegration.py +0 -254
- acoular/tests/unsupported/validationOfBeamformerFuncsPREeAcoularIntegration.py +0 -531
- acoular/tools.py +0 -422
- acoular-24.3.dist-info/RECORD +0 -148
- acoular-24.3.dist-info/licenses/LICENSE +0 -29
- {acoular-24.3.dist-info → acoular-24.7.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/microphones.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
#
|
|
2
|
-
#pylint: disable-msg=E0611, E1103, C0103, R0901, R0902, R0903, R0904, W0232
|
|
3
|
-
#------------------------------------------------------------------------------
|
|
1
|
+
# ------------------------------------------------------------------------------
|
|
4
2
|
# Copyright (c) Acoular Development Team.
|
|
5
|
-
|
|
6
|
-
"""Implements support for array microphone arrangements
|
|
3
|
+
# ------------------------------------------------------------------------------
|
|
4
|
+
"""Implements support for array microphone arrangements.
|
|
7
5
|
|
|
8
6
|
.. autosummary::
|
|
9
7
|
:toctree: generated/
|
|
@@ -13,108 +11,97 @@
|
|
|
13
11
|
"""
|
|
14
12
|
|
|
15
13
|
# imports from other packages
|
|
16
|
-
from numpy import array, average
|
|
17
|
-
from scipy.spatial.distance import cdist
|
|
18
|
-
from traits.api import HasPrivateTraits, Property, File, \
|
|
19
|
-
CArray, cached_property, on_trait_change, ListInt , Bool
|
|
20
|
-
from os import path, strerror
|
|
21
14
|
import errno
|
|
15
|
+
from os import path, strerror
|
|
16
|
+
|
|
17
|
+
from numpy import array, average
|
|
18
|
+
from scipy.spatial.distance import cdist
|
|
19
|
+
from traits.api import Bool, CArray, File, HasPrivateTraits, ListInt, Property, cached_property, on_trait_change
|
|
22
20
|
|
|
23
21
|
from .internal import digest
|
|
24
22
|
|
|
25
23
|
|
|
26
|
-
class MicGeom(
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
xml-source with element tag names `pos` and attributes Name, `x`, `y` and `z`.
|
|
24
|
+
class MicGeom(HasPrivateTraits):
|
|
25
|
+
"""Provides the geometric arrangement of microphones in the array.
|
|
26
|
+
|
|
27
|
+
The geometric arrangement of microphones is read in from an
|
|
28
|
+
xml-source with element tag names `pos` and attributes Name, `x`, `y` and `z`.
|
|
32
29
|
Can also be used with programmatically generated arrangements.
|
|
33
30
|
"""
|
|
34
31
|
|
|
35
32
|
#: Name of the .xml-file from wich to read the data.
|
|
36
|
-
from_file = File(filter=['*.xml'],
|
|
37
|
-
desc="name of the xml file to import")
|
|
33
|
+
from_file = File(filter=['*.xml'], desc='name of the xml file to import')
|
|
38
34
|
|
|
39
35
|
#: Validate mic geom from file
|
|
40
|
-
validate_file = Bool(True,
|
|
41
|
-
desc="Validate mic geom from file")
|
|
36
|
+
validate_file = Bool(True, desc='Validate mic geom from file')
|
|
42
37
|
|
|
43
38
|
#: Basename of the .xml-file, without the extension; is set automatically / readonly.
|
|
44
|
-
basename = Property(
|
|
45
|
-
desc="basename of xml file")
|
|
39
|
+
basename = Property(depends_on='from_file', desc='basename of xml file')
|
|
46
40
|
|
|
47
41
|
#: List that gives the indices of channels that should not be considered.
|
|
48
42
|
#: Defaults to a blank list.
|
|
49
|
-
invalid_channels = ListInt(
|
|
50
|
-
desc="list of invalid channels")
|
|
43
|
+
invalid_channels = ListInt(desc='list of invalid channels')
|
|
51
44
|
|
|
52
45
|
#: Number of microphones in the array; readonly.
|
|
53
|
-
num_mics = Property(
|
|
54
|
-
desc="number of microphones in the geometry")
|
|
46
|
+
num_mics = Property(depends_on=['mpos'], desc='number of microphones in the geometry')
|
|
55
47
|
|
|
56
48
|
#: Center of the array (arithmetic mean of all used array positions); readonly.
|
|
57
|
-
center = Property(
|
|
58
|
-
desc="array center")
|
|
49
|
+
center = Property(depends_on=['mpos'], desc='array center')
|
|
59
50
|
|
|
60
51
|
#: Aperture of the array (greatest extent between two microphones); readonly.
|
|
61
|
-
aperture = Property(
|
|
62
|
-
desc="array aperture")
|
|
52
|
+
aperture = Property(depends_on=['mpos'], desc='array aperture')
|
|
63
53
|
|
|
64
54
|
#: Positions as (3, :attr:`num_mics`) array of floats, may include also invalid
|
|
65
55
|
#: microphones (if any). Set either automatically on change of the
|
|
66
56
|
#: :attr:`from_file` argument or explicitely by assigning an array of floats.
|
|
67
|
-
mpos_tot = CArray(dtype=float,
|
|
68
|
-
desc="x, y, z position of all microphones")
|
|
57
|
+
mpos_tot = CArray(dtype=float, desc='x, y, z position of all microphones')
|
|
69
58
|
|
|
70
59
|
#: Positions as (3, :attr:`num_mics`) array of floats, without invalid
|
|
71
60
|
#: microphones; readonly.
|
|
72
|
-
mpos = Property(
|
|
73
|
-
desc="x, y, z position of microphones")
|
|
61
|
+
mpos = Property(depends_on=['mpos_tot', 'invalid_channels'], desc='x, y, z position of microphones')
|
|
74
62
|
|
|
75
63
|
# internal identifier
|
|
76
|
-
digest = Property(
|
|
64
|
+
digest = Property(depends_on=['mpos'])
|
|
77
65
|
|
|
78
66
|
@cached_property
|
|
79
|
-
def _get_digest(
|
|
67
|
+
def _get_digest(self):
|
|
80
68
|
return digest(self)
|
|
81
69
|
|
|
82
70
|
@cached_property
|
|
83
|
-
def _get_basename(
|
|
71
|
+
def _get_basename(self):
|
|
84
72
|
return path.splitext(path.basename(self.from_file))[0]
|
|
85
73
|
|
|
86
74
|
@cached_property
|
|
87
|
-
def _get_mpos(
|
|
75
|
+
def _get_mpos(self):
|
|
88
76
|
if self.validate_file:
|
|
89
|
-
if len(self.invalid_channels)==0:
|
|
77
|
+
if len(self.invalid_channels) == 0:
|
|
90
78
|
return self.mpos_tot
|
|
91
|
-
allr=[i for i in range(self.mpos_tot.shape[-1]) if i not in self.invalid_channels]
|
|
79
|
+
allr = [i for i in range(self.mpos_tot.shape[-1]) if i not in self.invalid_channels]
|
|
92
80
|
return self.mpos_tot[:, array(allr)]
|
|
93
|
-
|
|
94
|
-
raise FileNotFoundError(
|
|
95
|
-
errno.ENOENT, strerror(errno.ENOENT), self.from_file)
|
|
81
|
+
raise FileNotFoundError(errno.ENOENT, strerror(errno.ENOENT), self.from_file)
|
|
96
82
|
|
|
97
83
|
@cached_property
|
|
98
|
-
def _get_num_mics(
|
|
84
|
+
def _get_num_mics(self):
|
|
99
85
|
return self.mpos.shape[-1]
|
|
100
86
|
|
|
101
87
|
@cached_property
|
|
102
|
-
def _get_center(
|
|
88
|
+
def _get_center(self):
|
|
103
89
|
if self.mpos.any():
|
|
104
|
-
center = average(self.mpos,axis=1)
|
|
90
|
+
center = average(self.mpos, axis=1)
|
|
105
91
|
# set very small values to zero
|
|
106
|
-
center[abs(center) < 1e-16] = 0.
|
|
92
|
+
center[abs(center) < 1e-16] = 0.0
|
|
107
93
|
return center
|
|
94
|
+
return None
|
|
108
95
|
|
|
109
96
|
@cached_property
|
|
110
|
-
def _get_aperture(
|
|
97
|
+
def _get_aperture(self):
|
|
111
98
|
if self.mpos.any():
|
|
112
|
-
return cdist(self.mpos.T,self.mpos.T).max()
|
|
99
|
+
return cdist(self.mpos.T, self.mpos.T).max()
|
|
100
|
+
return None
|
|
113
101
|
|
|
114
102
|
@on_trait_change('basename')
|
|
115
|
-
def import_mpos(
|
|
116
|
-
"""
|
|
117
|
-
Import the microphone positions from .xml file.
|
|
103
|
+
def import_mpos(self):
|
|
104
|
+
"""Import the microphone positions from .xml file.
|
|
118
105
|
Called when :attr:`basename` changes.
|
|
119
106
|
"""
|
|
120
107
|
if not path.isfile(self.from_file):
|
|
@@ -124,12 +111,29 @@ class MicGeom( HasPrivateTraits ):
|
|
|
124
111
|
self.validate_file = False
|
|
125
112
|
|
|
126
113
|
import xml.dom.minidom
|
|
114
|
+
|
|
127
115
|
doc = xml.dom.minidom.parse(self.from_file)
|
|
128
116
|
names = []
|
|
129
117
|
xyz = []
|
|
130
118
|
for el in doc.getElementsByTagName('pos'):
|
|
131
119
|
names.append(el.getAttribute('Name'))
|
|
132
|
-
xyz.append(
|
|
120
|
+
xyz.append([float(el.getAttribute(a)) for a in 'xyz'])
|
|
133
121
|
self.mpos_tot = array(xyz, 'd').swapaxes(0, 1)
|
|
134
122
|
self.validate_file = True
|
|
135
123
|
|
|
124
|
+
def export_mpos(self, filename):
|
|
125
|
+
"""Export the microphone positions to .xml file.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
filename : str
|
|
130
|
+
Name of the file to which the microphone positions are written.
|
|
131
|
+
"""
|
|
132
|
+
basename = path.splitext(path.basename(filename))[0]
|
|
133
|
+
with open(filename, 'w') as f:
|
|
134
|
+
f.write(f'<?xml version="1.1" encoding="utf-8"?><MicArray name="{basename}">\n')
|
|
135
|
+
for i in range(self.mpos.shape[-1]):
|
|
136
|
+
f.write(
|
|
137
|
+
f' <pos Name="Point {i+1}" x="{self.mpos[0, i]}" y="{self.mpos[1, i]}" z="{self.mpos[2, i]}"/>\n',
|
|
138
|
+
)
|
|
139
|
+
f.write('</MicArray>')
|
acoular/sdinput.py
CHANGED
|
@@ -1,118 +1,122 @@
|
|
|
1
|
-
#
|
|
2
|
-
#pylint: disable-msg=E0611, E1101, C0103, R0901, R0902, R0903, R0904, W0232
|
|
3
|
-
#------------------------------------------------------------------------------
|
|
1
|
+
# ------------------------------------------------------------------------------
|
|
4
2
|
# Copyright (c) Acoular Development Team.
|
|
5
|
-
|
|
6
|
-
"""
|
|
3
|
+
# ------------------------------------------------------------------------------
|
|
4
|
+
"""Input from soundcard hardware using the SoundDevice library.
|
|
7
5
|
|
|
8
6
|
.. autosummary::
|
|
9
7
|
:toctree: generated/
|
|
10
8
|
|
|
11
9
|
SoundDeviceSamplesGenerator
|
|
12
10
|
"""
|
|
13
|
-
|
|
14
|
-
from traits.api import Int, Long,
|
|
15
|
-
|
|
11
|
+
|
|
12
|
+
from traits.api import Any, Bool, Int, Long, Property, cached_property, observe
|
|
13
|
+
|
|
14
|
+
from .configuration import config
|
|
16
15
|
from .internal import digest
|
|
16
|
+
from .tprocess import SamplesGenerator
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
if config.have_sounddevice:
|
|
19
|
+
import sounddevice as sd
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
class SoundDeviceSamplesGenerator(SamplesGenerator):
|
|
23
|
+
"""Controller for sound card hardware using sounddevice library.
|
|
22
24
|
|
|
23
25
|
Uses the device with index :attr:`device` to read samples
|
|
24
26
|
from input stream, generates output stream via the generator
|
|
25
27
|
:meth:`result`.
|
|
26
28
|
"""
|
|
27
29
|
|
|
30
|
+
def __init__(self, *args, **kwargs):
|
|
31
|
+
super().__init__(*args, **kwargs)
|
|
32
|
+
if config.have_sounddevice is False:
|
|
33
|
+
msg = 'SoundDevice library not found but is required for using the SoundDeviceSamplesGenerator class.'
|
|
34
|
+
raise ImportError(msg)
|
|
35
|
+
|
|
28
36
|
#: input device index, refers to sounddevice list
|
|
29
|
-
device = Int(0, desc=
|
|
37
|
+
device = Int(0, desc='input device index')
|
|
30
38
|
|
|
31
39
|
#: Number of input channels, maximum depends on device
|
|
32
|
-
numchannels = Long(1,
|
|
33
|
-
desc="number of analog input channels that collects data")
|
|
40
|
+
numchannels = Long(1, desc='number of analog input channels that collects data')
|
|
34
41
|
|
|
35
42
|
#: Number of samples to collect; defaults to -1.
|
|
36
43
|
# If is set to -1 device collects till user breaks streaming by setting Trait: collectsamples = False
|
|
37
|
-
numsamples = Long(-1,
|
|
38
|
-
desc="number of samples to collect")
|
|
44
|
+
numsamples = Long(-1, desc='number of samples to collect')
|
|
39
45
|
|
|
40
46
|
#: Indicates if samples are collected, helper trait to break result loop
|
|
41
|
-
collectsamples = Bool(True,
|
|
42
|
-
desc="Indicates if samples are collected")
|
|
47
|
+
collectsamples = Bool(True, desc='Indicates if samples are collected')
|
|
43
48
|
|
|
44
49
|
#: Sampling frequency of the signal, changes with sinusdevices
|
|
45
|
-
sample_freq = Property(
|
|
46
|
-
desc="sampling frequency")
|
|
50
|
+
sample_freq = Property(desc='sampling frequency')
|
|
47
51
|
|
|
48
52
|
#: Indicates that the sounddevice buffer has overflown
|
|
49
|
-
overflow = Bool(False,
|
|
50
|
-
desc="Indicates if sounddevice buffer overflow")
|
|
53
|
+
overflow = Bool(False, desc='Indicates if sounddevice buffer overflow')
|
|
51
54
|
|
|
52
55
|
#: Indicates that the stream is collecting samples
|
|
53
|
-
running = Bool(False,
|
|
54
|
-
desc="Indicates that the stream is collecting samples")
|
|
56
|
+
running = Bool(False, desc='Indicates that the stream is collecting samples')
|
|
55
57
|
|
|
56
58
|
#: The sounddevice InputStream object for inspection
|
|
57
59
|
stream = Any
|
|
58
60
|
|
|
59
61
|
# internal identifier
|
|
60
|
-
digest = Property(
|
|
61
|
-
|
|
62
|
+
digest = Property(depends_on=['device', 'numchannels', 'numsamples'])
|
|
63
|
+
|
|
62
64
|
@cached_property
|
|
63
|
-
def _get_digest(
|
|
65
|
+
def _get_digest(self):
|
|
64
66
|
return digest(self)
|
|
65
67
|
|
|
66
68
|
# checks that numchannels are not more than device can provide
|
|
67
69
|
@observe('device,numchannels')
|
|
68
|
-
def _get_numchannels(
|
|
69
|
-
self.numchannels = min(self.numchannels,sd.query_devices(self.device)['max_input_channels'])
|
|
70
|
+
def _get_numchannels(self, event): # noqa ARG002
|
|
71
|
+
self.numchannels = min(self.numchannels, sd.query_devices(self.device)['max_input_channels'])
|
|
70
72
|
|
|
71
|
-
def _get_sample_freq(
|
|
73
|
+
def _get_sample_freq(self):
|
|
72
74
|
return sd.query_devices(self.device)['default_samplerate']
|
|
73
75
|
|
|
74
|
-
def device_properties(
|
|
75
|
-
"""
|
|
76
|
-
Returns
|
|
76
|
+
def device_properties(self):
|
|
77
|
+
"""Returns
|
|
77
78
|
-------
|
|
78
79
|
Dictionary of device properties according to sounddevice
|
|
79
80
|
"""
|
|
80
81
|
return sd.query_devices(self.device)
|
|
81
82
|
|
|
82
83
|
def result(self, num):
|
|
83
|
-
"""
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
"""Python generator that yields the output block-wise. Use at least a
|
|
85
|
+
block-size of one ring cache block.
|
|
86
|
+
|
|
87
87
|
Parameters
|
|
88
88
|
----------
|
|
89
89
|
num : integer
|
|
90
90
|
This parameter defines the size of the blocks to be yielded
|
|
91
91
|
(i.e. the number of samples per block).
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
Returns
|
|
94
94
|
-------
|
|
95
|
-
Samples in blocks of shape (num, :attr:`numchannels`).
|
|
95
|
+
Samples in blocks of shape (num, :attr:`numchannels`).
|
|
96
96
|
The last block may be shorter than num.
|
|
97
|
+
|
|
97
98
|
"""
|
|
98
|
-
print(self.device_properties(),self.sample_freq)
|
|
99
|
+
print(self.device_properties(), self.sample_freq)
|
|
99
100
|
self.stream = stream_obj = sd.InputStream(
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
device=self.device,
|
|
102
|
+
channels=self.numchannels,
|
|
103
|
+
clip_off=True,
|
|
104
|
+
samplerate=self.sample_freq,
|
|
105
|
+
)
|
|
102
106
|
|
|
103
107
|
with stream_obj as stream:
|
|
104
108
|
self.running = True
|
|
105
|
-
if self.numsamples == -1:
|
|
106
|
-
while self.collectsamples:
|
|
107
|
-
data,self.overflow = stream.read(num)
|
|
108
|
-
yield data[:num]
|
|
109
|
-
|
|
110
|
-
elif self.numsamples > 0:
|
|
111
|
-
samples_count = 0
|
|
112
|
-
while samples_count < self.numsamples:
|
|
113
|
-
anz = min(num, self.numsamples-samples_count)
|
|
109
|
+
if self.numsamples == -1:
|
|
110
|
+
while self.collectsamples: # yield data as long as collectsamples is True
|
|
111
|
+
data, self.overflow = stream.read(num)
|
|
112
|
+
yield data[:num]
|
|
113
|
+
|
|
114
|
+
elif self.numsamples > 0: # amount of samples to collect is specified by user
|
|
115
|
+
samples_count = 0 # numsamples counter
|
|
116
|
+
while samples_count < self.numsamples:
|
|
117
|
+
anz = min(num, self.numsamples - samples_count)
|
|
114
118
|
data, self.overflow = stream.read(num)
|
|
115
|
-
yield data[:anz]
|
|
119
|
+
yield data[:anz]
|
|
116
120
|
samples_count += anz
|
|
117
121
|
self.running = False
|
|
118
|
-
return
|
|
122
|
+
return
|