SourceMixer¶
- class acoular.sources.SourceMixer¶
Bases:
SamplesGenerator
Combine signals from multiple sources by mixing their outputs.
The
SourceMixer
class takes signals generated by multipleSamplesGenerator
instances and combines them into a single mixed output. The signals are weighted (if weights are provided) and added block-by-block, supporting efficient streaming.See also
acoular.base.SamplesGenerator
Base class for signal generators.
Notes
All sources must have the same sampling frequency, number of channels, and number of samples for proper mixing.
The weights for the sources can be specified to control their relative contributions to the mixed output. If no weights are provided, all sources are equally weighted.
Examples
Mix a stationary point source emitting a sine signal with two pink noise emitting point sources circling it and white noise for each channel:
>>> import numpy as np >>> import acoular as ac >>> >>> # Generate positional microphone data for a 3x3 grid in the x-y plane at z=0 >>> mic_positions = [] >>> for i in range(3): ... for j in range(3): ... mic_positions.append([i - 1, j - 1, 0]) # Center the grid at the origin >>> >>> # Convert positions to the format required by MicGeom >>> mg = ac.MicGeom(pos_total=np.array(mic_positions).T) >>> >>> # Generate positional data for trajectories of two moving sources >>> # Trajectory 1: Circle in x-y plane at z=1 >>> args = 2 * np.pi * np.arange(10) / 10 # Discrete points around the circle >>> x = np.cos(args) >>> y = np.sin(args) >>> z = np.ones_like(x) # Constant height at z=1 >>> >>> locs1 = np.array([x, y, z]) >>> # Map time indices to positions for Trajectory 1 >>> points1 = {time: tuple(pos) for time, pos in enumerate(locs1.T)} >>> tr1 = ac.Trajectory(points=points1) >>> >>> # Trajectory 2: Same circle but with a 180-degree phase shift >>> locs2 = np.roll(locs1, 5, axis=1) # Shift the positions by half the circle >>> # Map time indices to positions for Trajectory 2 >>> points2 = {time: tuple(pos) for time, pos in enumerate(locs2.T)} >>> tr2 = ac.Trajectory(points=points2) >>> >>> # Create signal sources >>> # Pink noise sources with different RMS values and random seeds >>> pinkNoise1 = ac.PNoiseGenerator(sample_freq=51200, num_samples=1024, rms=1.0, seed=42) >>> pinkNoise2 = ac.PNoiseGenerator(sample_freq=51200, num_samples=1024, rms=0.5, seed=24) >>> >>> # Moving sources emitting pink noise along their respective trajectories >>> pinkSource1 = ac.MovingPointSource(trajectory=tr1, signal=pinkNoise1, mics=mg) >>> pinkSource2 = ac.MovingPointSource(trajectory=tr2, signal=pinkNoise2, mics=mg) >>> >>> # White noise source generating uncorrelated noise for each microphone channel >>> whiteNoise = ac.WNoiseGenerator(sample_freq=51200, num_samples=1024, rms=1.0, seed=73) >>> whiteSources = ac.UncorrelatedNoiseSource(signal=whiteNoise, mics=mg) >>> >>> # Stationary point source emitting a sine wave >>> sineSignal = ac.SineGenerator(freq=1200, sample_freq=51200, num_samples=1024) >>> sineSource = ac.PointSource(signal=sineSignal, loc=(0, 0, 1), mics=mg) >>> >>> # Combine all sources in a SourceMixer with specified weights >>> sources = [pinkSource1, pinkSource2, whiteSources, sineSource] >>> mixer = ac.SourceMixer(sources=sources, weights=[1.0, 1.0, 0.3, 2.0]) >>> >>> # Generate and process the mixed output block by block >>> for block in mixer.result(num=256): # Generate blocks of 256 samples ... print(block.shape) Pink noise filter depth set to maximum possible value of 10. Pink noise filter depth set to maximum possible value of 10. (256, 9) (256, 9) (256, 9) (256, 9)
The output contains blocks of mixed signals. Each block is a combination of the four signals, weighted according to the provided weights.
- sources = List(Instance(SamplesGenerator, ()))¶
List of
SamplesGenerator
instances to be mixed. Each source provides a signal that will be combined in the output. All sources must have the same sampling frequency, number of channels, and number of samples. The list must contain at least one source.
- sample_freq = Property(depends_on=['sdigest'])¶
Sampling frequency of the mixed signal in Hz. Derived automatically from the first source in
sources
. If no sources are provided, default is0
.
- num_channels = Property(depends_on=['sdigest'])¶
Number of channels in the mixed signal. Derived automatically from the first source in
sources
. If no sources are provided, default is0
.
- num_samples = Property(depends_on=['sdigest'])¶
Total number of samples in the mixed signal. Derived automatically from the first source in
sources
. If no sources are provided, default is0
.
- weights = CArray(desc='channel weights')¶
Array of amplitude weights for the sources. If not set, all sources are equally weighted. The size of the weights array must match the number of sources in
sources
. For example, with two sources,weights = [1.0, 0.5]
would mix the first source at full amplitude and the second source at half amplitude.
- sdigest = Str()¶
Internal identifier for the combined state of all sources, used to track changes in the sources for reproducibility and caching.
- digest = Property(depends_on=['sdigest', 'weights'])¶
A unique identifier for the current state of the source, based on the states of the sources and the weights. (read-only)
- validate_sources()¶
Ensure that all sources are compatible for mixing.
This method checks that all sources in
sources
have the same sampling frequency, number of channels, and number of samples. AValueError
is raised if any mismatch is detected.- Raises:
ValueError
If any source has incompatible attributes.
- result(num)¶
Generate uncorrelated the mixed signal at microphones in blocks.
The
result()
method combines signals from all sources block-by-block, applying the specified weights to each source. The output blocks contain the mixed signal for all channels.- Parameters:
- num
int
Number of samples per block to be yielded.
- num
- Yields:
numpy.ndarray
A 2D array of shape (
num
,num_channels
) containing the mixed signal. The last block may have fewer samples if the total number of samples is not a multiple ofnum
.
- Raises:
ValueError
If the sources are not compatible for mixing.