diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 0000000..fa1f864 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,31 @@ +name: run + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.10"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install base dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install soundfile pytest + - name: Run core tests + run: | + python -m pytest --ignore test/test_convert.py + - name: Install additional test dependencies + run: | + pip install -e '.[test]' + - name: Run all tests + run: | + python -m pytest diff --git a/penn/convert.py b/penn/convert.py index 45861aa..bacac0c 100644 --- a/penn/convert.py +++ b/penn/convert.py @@ -45,6 +45,21 @@ def frequency_to_samples(frequency, sample_rate=penn.SAMPLE_RATE): """Convert frequency in Hz to number of samples per period""" return sample_rate / frequency +def frequency_to_midi(frequency): + """ + Convert frequency to MIDI note number(s) + Based on librosa.hz_to_midi(frequencies) implementation + https://librosa.org/doc/main/_modules/librosa/core/convert.html#hz_to_midi + """ + return 12 * (torch.log2(frequency) - torch.log2(torch.tensor(440.0))) + 69 + +def midi_to_frequency(midi): + """ + Convert MIDI note number to frequency + Based on librosa.midi_to_hz(notes) implementation + https://librosa.org/doc/main/_modules/librosa/core/convert.html#midi_to_hz + """ + return 440.0 * (2.0 ** ((midi - 69.0) / 12.0)) ############################################################################### # Time conversions diff --git a/setup.py b/setup.py index 9be23e1..a54b23b 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,9 @@ 'pyworld', # 0.3.2 'scipy', # 1.9.3 'torchcrepe' # 0.0.17 + ], + 'test': [ + 'librosa', # 0.9.1 ] }, install_requires=[ diff --git a/test/test_convert.py b/test/test_convert.py new file mode 100644 index 0000000..e0dbff0 --- /dev/null +++ b/test/test_convert.py @@ -0,0 +1,32 @@ +import penn + +import librosa +import torch + + +############################################################################### +# Test convert.py +############################################################################### + + +def test_convert_frequency_to_midi(): + """Test that conversion from Hz to MIDI matches librosa implementation""" + + sample_data = torch.tensor([110.0, 220.0, 440.0, 500.0, 880.0]) + + penn_midi = penn.convert.frequency_to_midi(sample_data) + + librosa_midi = librosa.hz_to_midi(sample_data.numpy()) + + assert torch.allclose(penn_midi, torch.tensor(librosa_midi, dtype=torch.float32)) + +def test_convert_midi_to_frequency(): + """Test that conversion from MIDI to Hz matches librosa implementation""" + + sample_data = torch.tensor([45.0, 57.0, 69.0, 71.2131, 81.0]) + + penn_frequency = penn.convert.midi_to_frequency(sample_data) + + librosa_frequency = librosa.midi_to_hz(sample_data.numpy()) + + assert torch.allclose(penn_frequency, torch.tensor(librosa_frequency, dtype=torch.float32)) \ No newline at end of file