Exemple #1
0
def test_generate_wave(form: str, frequency: float, amplitudes: np.ndarray,
                       frame_rate: int, location: float,
                       max_channel_delay: float, expected: np.ndarray) -> None:
    """Test `generate_wave` function."""
    result = generate_wave(form, frequency, amplitudes, frame_rate, location,
                           max_channel_delay)
    np.testing.assert_almost_equal(result, expected)
Exemple #2
0
def oscillate_between_sounds(sounds: np.ndarray,
                             frame_rate: int,
                             frequency: float,
                             waveform: str = 'sine') -> np.ndarray:
    """
    Combine multiple sounds into one sound by oscillating between them.

    :param sounds:
        array of shape (n_sounds, n_channels, n_frames)
    :param frame_rate:
        number of frames per second
    :param frequency:
        frequency of oscillations between sound sources
    :param waveform:
        form of oscillations wave
    :return:
        sound composed from input sounds
    """
    step = 2 / (sounds.shape[0] - 1)
    thresholds = np.arange(-1, 1 + 1e-7, step)
    weights = np.tile(thresholds.reshape((-1, 1)), (1, sounds.shape[2]))
    wave = generate_wave(waveform, frequency, np.ones(sounds.shape[2]),
                         frame_rate)
    wave = wave[0, :]
    weights = ((1 - np.abs(weights - wave) / step) *
               (np.abs(weights - wave) < step))
    weights = weights.reshape((weights.shape[0], 1, weights.shape[1]))
    sound = np.sum(sounds * weights, axis=0)
    return sound
Exemple #3
0
def tremolo(sound: np.ndarray,
            frame_rate: int,
            frequency: float = 6,
            amplitude: float = 0.5,
            waveform: str = 'sine') -> np.ndarray:
    """
    Make sound volume vibrating.

    :param sound:
        sound to be modified
    :param frame_rate:
        number of frames per second
    :param frequency:
        frequency of volume oscillations (in Hz)
    :param amplitude:
        relative amplitude of volume oscillations, must be between 0 and 1
    :param waveform:
        form of volume oscillations wave
    :return:
        sound with vibrating volume
    """
    if not (0 < amplitude <= 1):
        raise ValueError("Amplitude for tremolo must be between 0 and 1.")
    amplitudes = amplitude * np.ones(sound.shape[1])
    volume_wave = generate_wave(waveform, frequency, amplitudes, frame_rate)
    volume_wave += 1
    sound *= volume_wave
    return sound
Exemple #4
0
def test_frequency_filter_with_sine_waves(frequency: float, frame_rate: int,
                                          min_frequency: float,
                                          max_frequency: float, invert: bool,
                                          order: int) -> None:
    """Test that `frequency_filter` function removes requested frequencies."""
    sound = generate_wave('sine', frequency, np.ones(frame_rate), frame_rate)
    result = frequency_filter(sound, frame_rate, min_frequency, max_frequency,
                              invert, order)
    assert np.sum(np.abs(result)) < 0.01 * np.sum(np.abs(sound))
Exemple #5
0
def synthesize(timbre_spec: TimbreSpec, frequency: float, volume: float,
               duration: float, location: float, max_channel_delay: float,
               frame_rate: int) -> np.ndarray:
    """
    Synthesize sound fragment that corresponds to one note.

    :param timbre_spec:
        specification of a timbre
    :param frequency:
        frequency of fundamental in Hz
    :param volume:
        volume of the sound fragment
    :param duration:
        duration of fragment to be generated in seconds
    :param location:
        location of sound source;
        -1 stands for extremely left and 1 stands for extremely right
    :param max_channel_delay:
        maximum possible delay between channels in seconds;
        it is a measure of potential size of space occupied by sound sources
    :param frame_rate:
        number of frames per second
    :return:
        sound wave represented as timeline of pressure deviations
    """
    envelope = timbre_spec.fundamental_volume_envelope_fn(duration, frame_rate)
    overtones_share = calculate_overtones_share(timbre_spec)
    fundamental_share = 1 - overtones_share
    sound = generate_wave(timbre_spec.fundamental_waveform, frequency,
                          volume * fundamental_share * envelope, frame_rate,
                          location, max_channel_delay)
    for effect_fn in timbre_spec.fundamental_effects:
        sound = effect_fn(sound, frame_rate)
    for overtone_spec in timbre_spec.overtones_specs:
        envelope = overtone_spec.volume_envelope_fn(duration, frame_rate)
        overtone_sound = generate_wave(
            overtone_spec.waveform, overtone_spec.frequency_ratio * frequency,
            volume * overtone_spec.volume_share * envelope, frame_rate,
            location, max_channel_delay)
        for effect_fn in overtone_spec.effects:
            overtone_sound = effect_fn(overtone_sound, frame_rate)
        sound += overtone_sound
    return sound
Exemple #6
0
def test_filter_sweep_on_one_band(frequency: float, frame_rate: int,
                                  bands: List[Tuple[Optional[float],
                                                    Optional[float]]],
                                  osc_frequency: float, waveform: str) -> None:
    """Test that `filter_sweep` function runs if there is one band only."""
    sound = generate_wave('sine', frequency, np.ones(frame_rate), frame_rate)
    _ = filter_sweep(sound,
                     frame_rate,
                     bands,
                     frequency=osc_frequency,
                     waveform=waveform)
Exemple #7
0
def test_frequency_filter_with_arbitrary_input(frequencies: List[float],
                                               frame_rate: int,
                                               min_frequency: float,
                                               max_frequency: float,
                                               invert: bool,
                                               order: int) -> None:
    """Test that `frequency_filter` function returns something finite."""
    waves = [
        generate_wave('sine', frequency, np.ones(frame_rate), frame_rate)
        for frequency in frequencies
    ]
    sound = sum(waves)
    result = frequency_filter(sound, frame_rate, min_frequency, max_frequency,
                              invert, order)
    assert np.all(np.isfinite(result))
    assert np.sum(np.abs(result)) < np.sum(np.abs(sound))
Exemple #8
0
def vibrato(sound: np.ndarray,
            frame_rate: int,
            frequency: float = 4,
            width: float = 0.2,
            waveform: str = 'sine') -> np.ndarray:
    """
    Make sound frequency vibrating.

    :param sound:
        sound to be modified
    :param frame_rate:
        number of frames per second
    :param frequency:
        frequency of sound's frequency oscillations (in Hz)
    :param width:
        difference between the highest frequency of oscillating sound
        and the lowest frequency of oscillating sound (in semitones)
    :param waveform:
        form of frequency oscillations wave
    :return:
        sound with vibrating frequency
    """
    semitone = 2**(1 / 12)
    highest_to_lowest_ratio = semitone**width
    # If x = 0, d(x + m * sin(2 * \pi * f * x))/dx = 1 + 2 * \pi * f * m.
    # If x = \pi, d(x + m * sin(2 * \pi * f * x))/dx = 1 - 2 * \pi * f * m.
    # Ratio of above right sides is `highest_to_lowest_ratio`.
    # Let us solve it for `m` (`max_delay`).
    max_delay = ((highest_to_lowest_ratio - 1) /
                 ((highest_to_lowest_ratio + 1) * 2 * np.pi * frequency))

    amplitudes = max_delay * frame_rate * np.ones(sound.shape[1])
    frequency_wave = generate_wave(waveform, frequency, amplitudes, frame_rate)
    time_indices = np.ones(sound.shape[1]).cumsum() - 1 + frequency_wave[0, :]

    upper_indices = np.ceil(time_indices).astype(int)
    upper_indices = np.clip(upper_indices, 0, sound.shape[1] - 1)
    upper_sound = sound[:, upper_indices]

    lower_indices = np.floor(time_indices).astype(int)
    lower_indices = np.clip(lower_indices, 0, sound.shape[1] - 1)
    lower_sound = sound[:, lower_indices]

    weights = time_indices - lower_indices
    sound = weights * upper_sound + (1 - weights) * lower_sound
    return sound
Exemple #9
0
def test_muting_of_filter_sweep(frequencies: List[float], frame_rate: int,
                                bands: List[Tuple[Optional[float],
                                                  Optional[float]]],
                                osc_frequency: float, waveform: str) -> None:
    """Test that `filter_sweep` function mutes what it must mute."""
    waves = [
        generate_wave('sine', frequency, np.ones(frame_rate), frame_rate)
        for frequency in frequencies
    ]
    sound = sum(waves)
    result = filter_sweep(sound,
                          frame_rate,
                          bands,
                          frequency=osc_frequency,
                          waveform=waveform)
    assert (np.sum(np.abs(result[:, :100])) <
            0.01 * np.sum(np.abs(sound[:, :100])))
    assert (np.sum(np.abs(result[:, -100:])) >
            0.5 * np.sum(np.abs(sound[:, -100:])))
Exemple #10
0
def test_phaser(frequency: float, frame_rate: int) -> None:
    """Test that `phaser` function runs without failures."""
    sound = generate_wave('sine', frequency, np.ones(frame_rate), frame_rate)
    result = phaser(sound, frame_rate)
    assert np.all(np.isfinite(result))