Skip to content
This repository has been archived by the owner on Jan 3, 2023. It is now read-only.

Commit

Permalink
Add sine wave generators
Browse files Browse the repository at this point in the history
  • Loading branch information
mondeja committed Jun 7, 2021
1 parent 2e032bd commit e9f2370
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.2.1
current_version = 0.2.2

[bumpversion:file:waves/__init__.py]

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
:caption: Reference

ref/sound
ref/sound.generator
5 changes: 5 additions & 0 deletions docs/ref/sound.generator.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
sound.generator
===============

.. autofunction:: waves.sound.generator.mono_ttf_gen
.. autofunction:: waves.sound.generator.stereo_ttf_gen
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = waves
version = 0.2.1
version = 0.2.2
description = Utility to work with WAV files in a simple way.
long_description = file: README.rst
long_description_content_type = text/x-rst
Expand Down
12 changes: 11 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from waves import Sound
from waves import Sound, mono_ttf_gen as _mtg, stereo_ttf_gen as _stg


TESTS_DIR = os.path.abspath(os.path.dirname(__file__))
Expand All @@ -26,3 +26,13 @@ def mono_sound(mono_filepath):
@pytest.fixture
def stereo_sound(stereo_filepath):
return Sound.from_file(stereo_filepath)


@pytest.fixture
def mono_ttf_gen():
return _mtg


@pytest.fixture
def stereo_ttf_gen():
return _stg
26 changes: 26 additions & 0 deletions tests/test_generator/test_mono.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Mono sound wave generators tests."""

import pytest

from waves import Sound


@pytest.mark.parametrize("fps", (44100, 22050), ids=("fps=44100", "22050"))
@pytest.mark.parametrize(
"frequency",
(110, 220, 440),
ids=("frequency=110", "frequency=220", "frequency=440"),
)
@pytest.mark.parametrize(
"volume",
(0, 0.5, 1),
ids=("volume=0", "volume=0.5", "volume=1"),
)
def test_mono_ttf_gen(mono_ttf_gen, fps, frequency, volume):
time_to_frame = mono_ttf_gen(fps=fps, frequency=frequency, volume=volume)

sound = Sound.from_datatimes(time_to_frame, fps=fps).with_duration(1)

assert sound.fps == fps
assert sound.n_channels == 1
assert sound.duration == 1
26 changes: 26 additions & 0 deletions tests/test_generator/test_stereo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Stereo sound wave generators tests."""

import pytest

from waves import Sound


@pytest.mark.parametrize("fps", (44100, 22050), ids=("fps=44100", "22050"))
@pytest.mark.parametrize(
"frequencies",
((110, 220), (440, 880), (120, 360, 720)),
ids=("frequency=110", "frequency=220", "frequency=440"),
)
@pytest.mark.parametrize(
"volume",
(0, 0.5, 1),
ids=("volume=0", "volume=0.5", "volume=1"),
)
def test_stereo_ttf_gen(stereo_ttf_gen, fps, frequencies, volume):
time_to_frame = stereo_ttf_gen(fps=fps, frequencies=frequencies, volume=volume)

sound = Sound.from_datatimes(time_to_frame, fps=fps).with_duration(1)

assert sound.fps == fps
assert sound.n_channels == len(frequencies)
assert sound.duration == 1
7 changes: 2 additions & 5 deletions tests/test_io/test_from_datatimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@
from waves import Sound


def test_from_datatimes_mono():
def test_from_datatimes_mono(mono_ttf_gen):
fps, frequency, volume = (44100, 110, 0.5)
amplitude = np.iinfo(np.int16).max * volume

def time_to_frame(t):
return (np.sin(frequency * 2 * np.pi * t) * amplitude).astype(np.int16)

time_to_frame = mono_ttf_gen(fps=fps, frequency=frequency, volume=volume)
sound = Sound.from_datatimes(time_to_frame, fps=fps)

assert sound.n_bytes == 2
Expand Down
7 changes: 3 additions & 4 deletions tests/test_io/test_iter_dataframes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ def test_iter_dataframes_stereo_from_file(stereo_sound):
assert frame[1] == data[i][1]


def test_iter_dataframes_mono_from_function():
def test_iter_dataframes_mono_from_function(mono_ttf_gen):
fps, frequency, volume = (44100, 110, 0.5)
amplitude, t_fps = (np.iinfo(np.int16).max * volume, 1 / fps)

def time_to_frame(t):
return (np.sin(frequency * 2 * np.pi * t) * amplitude).astype(np.int16)
time_to_frame = mono_ttf_gen(fps=fps, frequency=frequency, volume=volume)
t_fps = 1 / fps

sound = Sound.from_datatimes(time_to_frame, fps=fps).with_duration(0.5)
for i, frame in enumerate(sound.iter_dataframes):
Expand Down
7 changes: 2 additions & 5 deletions tests/test_io/test_iter_datatimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,10 @@ def test_iter_datatimes_stereo_from_file(stereo_sound):
assert data[i][1] == frame[1]


def test_iter_datatimes_mono_from_function():
def test_iter_datatimes_mono_from_function(mono_ttf_gen):
fps, frequency, volume = (44100, 110, 0.5)
amplitude = np.iinfo(np.int16).max * volume

def time_to_frame(t):
return (np.sin(frequency * 2 * np.pi * t) * amplitude).astype(np.int16)

time_to_frame = mono_ttf_gen(fps=fps, frequency=frequency, volume=volume)
sound = Sound.from_datatimes(time_to_frame, fps=fps).with_duration(0.5)

zipped = zip(sound.iter_datatimes, sound.time_sequence)
Expand Down
5 changes: 3 additions & 2 deletions waves/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Breaks code base."""

from waves.sound.generator import mono_ttf_gen, stereo_ttf_gen
from waves.sound.main import Sound


__all__ = ("Sound",)
__all__ = ("Sound", "mono_ttf_gen", "stereo_ttf_gen")

__title__ = "waves"
__version__ = "0.2.1"
__version__ = "0.2.2"
86 changes: 86 additions & 0 deletions waves/sound/generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import functools

import numpy as np


@functools.lru_cache(maxsize=None)
def mono_ttf_gen(fps=44100, frequency=110, volume=0.5):
"""Generates a ``time_to_frame`` function for a mono sine wave which can be
passed to ``Sound.from_datatimes``.
Parameters
----------
fps : int, optional
Frames per second of the sound to generate.
frequency : int, optional
Frequency of the sine wave.
volume : float, optional
Value between 0 and 1 that will define the volume of the wave.
Returns
-------
function: Takes a parameter time ``t`` and returns the sound data for that
time.
Examples
--------
>>> from waves import Sound, mono_ttf_gen
>>>
>>> time_to_frame = mono_ttf_gen(frequency=660, volume=0.2)
>>> sound = Sound.from_datatimes(time_to_frame).with_duration(2)
"""
amplitude = np.iinfo(np.int16).max * volume

def time_to_frame(t):
return (np.sin(frequency * 2 * np.pi * t) * amplitude).astype(np.int16)

return time_to_frame


@functools.lru_cache(maxsize=None)
def stereo_ttf_gen(fps=44100, frequencies=(440, 110), volume=0.5):
"""Generates a ``time_to_frame`` function for a stereo sine wave which can be
passed to ``Sound.from_datatimes``.
Parameters
----------
fps : int, optional
Frames per second of the sound to generate.
frequencies : tuple or list, optional
Frequencies of each channel for the sine wave. The number of channels
will be determined by the number of values introduced in this parameter.
volume : float, optional
Value between 0 and 1 that will define the volume of the wave.
Returns
-------
function: Takes a parameter time ``t`` and returns the sound data for that
time for each channel of the sound.
Examples
--------
>>> from waves import Sound, stereo_ttf_gen
>>>
>>> time_to_frame = stereo_ttf_gen(frequencies=(660, 290), volume=0.2)
>>> sound = Sound.from_datatimes(time_to_frame).with_duration(2)
"""
amplitude = np.iinfo(np.int16).max * volume

def time_to_frame(t):
return [np.sin(freq * 2 * np.pi * t) * amplitude for freq in frequencies]

return time_to_frame

0 comments on commit e9f2370

Please sign in to comment.