multipitch-architectures / libfmp / c1 / c1s1_sheet_music.py
c1s1_sheet_music.py
Raw
"""
Module: libfmp.c1.c1s1_sheet_music
Author: Meinard Müller
License: The MIT license, https://opensource.org/licenses/MIT

This file is part of the FMP Notebooks (https://www.audiolabs-erlangen.de/FMP)
"""

import numpy as np


def generate_sinusoid_pitches(pitches=[69], dur=0.5, Fs=4000, amp=1):
    """Generation of sinusoids for a given list of MIDI pitches

    Notebook: C1/C1S1_MusicalNotesPitches.ipynb

    Args:
        pitches: List of MIDI pitches
        dur: Duration (in seconds) of each sinusoid
        Fs: Sampling rate
        amp: Amplitude of generated signal

    Returns:
        x: Signal
        t: Time axis (in seconds)
    """
    N = int(dur * Fs)
    t = np.arange(N) / Fs
    x = []
    for p in pitches:
        freq = 2 ** ((p - 69) / 12) * 440
        x = np.append(x, np.sin(2 * np.pi * freq * t))
    x = amp * x / np.max(x)
    return x, t


def generate_shepard_tone(freq=440, dur=0.5, Fs=44100, amp=1):
    """Generate Shepard tone

    Notebook: C1/C1S1_ChromaShepard.ipynb

    Args:
        freq: Frequency of Shepard tone
        dur: Duration (in seconds)
        Fs: Sampling rate
        amp: Amplitude of generated signal

    Returns:
        x: Shepard tone
        t: Time axis (in seconds)
    """
    N = int(dur * Fs)
    t = np.arange(N) / Fs
    num_sin = 1
    x = np.sin(2 * np.pi * freq * t)
    freq_lower = freq / 2
    while freq_lower > 20:
        num_sin += 1
        x = x + np.sin(2 * np.pi * freq_lower * t)
        freq_lower = freq_lower / 2
    freq_upper = freq * 2
    while freq_upper < 20000:
        num_sin += 1
        x = x + np.sin(2 * np.pi * freq_upper * t)
        freq_upper = freq_upper * 2
    x = x / num_sin
    x = amp * x/np.max(x)
    return x, t


def generate_chirp_exp_octave(freq_start=440, dur=8, Fs=44100, amp=1):
    """Generate one octave of a chirp with exponential frequency increase

    Notebook: C1/C1S1_ChromaShepard.ipynb

    Args:
        freq_start: Start frequency of chirp
        dur: Duration (in seconds)
        Fs: Sampling rate
        amp: Amplitude of generated signal

    Returns:
        x: chirp signal
        t: Time axis (in seconds)
    """
    N = int(dur * Fs)
    t = np.arange(N) / Fs
    x = np.sin(2 * np.pi * freq_start * np.power(2, t / dur) / np.log(2) * dur)
    x = amp * x/np.max(x)
    return x, t


def generate_shepard_glissando(num_octaves=3, dur_octave=8, Fs=44100):
    """Generate several ocatves of a Shepared glissando

    Notebook: C1/C1S1_ChromaShepard.ipynb

    Args:
        num_octaves: Number of octaves
        dur_octave: Duration (in seconds) per octave
        Fs: Sampling rate

    Returns:
        x: Shepared glissando
        t: Time axis (in seconds)
    """
    freqs_start = 10 * 2**np.arange(0, 11)
    # Generate Shepard glissando by superimposing chirps that differ by octaves
    for freq in freqs_start:
        if freq == 10:
            x, t = generate_chirp_exp_octave(freq_start=freq, dur=dur_octave, Fs=Fs, amp=1)
        else:
            chirp, t = generate_chirp_exp_octave(freq_start=freq, dur=dur_octave, Fs=Fs, amp=1)
            x = x + chirp
    x = x / len(freqs_start)
    # Concatenate several octaves
    x = np.tile(x, num_octaves)
    N = len(x)
    t = np.arange(N) / Fs
    return x, t