Example #1
0
def test_reader_sweep(cfg):
    cfg = dataclasses.replace(cfg, sweep='0 0 1 1')
    read = WaveReader(CFG_DIR, cfg)
    instr = read.read()

    assert len(instr.waves) == 2
    assert (instr.sweep == [0, 0, 1, 1]).all()
Example #2
0
def get_phase(cfg):
    read = WaveReader(WAVE_DIR, cfg)
    instr = read.read()

    spectrum = rfft_norm(instr.waves[0])
    phase = np.angle(spectrum[1])
    return phase
Example #3
0
def test_reader_multi_waves():
    cfg_str = '''\
files:
  - path: sine440.69.wav
  - path: sine440.69.wav
    speed: 2
    volume: 1.1
  - path: sine256.59.624.wav
    speed: 3
    volume: 0
pitch_estimate: 69
nwave: 1

nsamp: 16
'''
    harmonics = [1, 2]  # plus 3 at volume=0

    cfg = n163_cfg(**yaml.load(cfg_str))
    read = WaveReader(WAVE_DIR, cfg)
    instr = read.read()

    # Calculate spectrum of resulting signal
    spectrum: np.ndarray = np.abs(rfft_norm(instr.waves[0]))
    spectrum[0] = 0
    threshold = np.amax(spectrum) / 2
    assert threshold > 0.1

    # Ensure pitches present
    assert (spectrum[harmonics] > threshold).all()

    # Ensure no other pitches present
    spectrum[harmonics] = 0
    spectrum[0] = 0
    assert (spectrum < threshold).all()
Example #4
0
def test_reader_stft_merge(cfg, stft_merge):
    cfg.stft_merge = stft_merge
    read = WaveReader(CFG_DIR, cfg)
    instr = read.read()

    # TODO check instr is legit
    assert instr
Example #5
0
def test_reader_subsample():
    wave_sub = 2
    env_sub = 3

    # should wave mandatory =N*env?
    for ntick in [3, 4]:
        cfg = n163_cfg(
            wav_path=WAV_PATH,
            nsamp=NSAMP,
            fps=60,
            pitch_estimate=74,
            nwave=ntick,
            wave_sub=wave_sub,
            env_sub=env_sub,
        )
        read = WaveReader(CFG_DIR, cfg)
        instr = read.read()

        # Subsampled waves
        nwave_sub = ceildiv(ntick, wave_sub)
        assert len(instr.waves) == nwave_sub

        # Subsampled sweep
        assert (instr.sweep == np.arange(nwave_sub)).all()

        # Subsampled volume/pitch
        nenv_sub = ceildiv(ntick, env_sub)

        def check(arr: np.ndarray):
            assert len(arr) == nenv_sub

        for i in range(ceildiv(ntick, env_sub)):
            check(instr.vols)
            check(instr.freqs)
Example #6
0
def test_reader_wave_locs(cfg):
    cfg = dataclasses.replace(cfg, wave_locs='0 1 3 6')
    read = WaveReader(CFG_DIR, cfg)
    instr = read.read()

    assert len(instr.waves) == 4
    assert (instr.sweep == map(int, '0 1 1 2 2 2 3'.split())).all()
Example #7
0
def test_reader_sweep_remove_unused(cfg):
    """ Ensure that waves not present in `sweep` are removed. """
    cfg = dataclasses.replace(cfg, sweep='0 1 3 6 3 1')
    read = WaveReader(CFG_DIR, cfg)
    instr = read.read()

    assert len(instr.waves) == 4
    assert (instr.sweep == [0, 1, 2, 3, 2, 1]).all()
Example #8
0
def test_reader_sweep_loop_release(cfg):
    cfg = dataclasses.replace(cfg, sweep='0 / 0 | 1 1')
    read = WaveReader(CFG_DIR, cfg)
    instr = read.read()

    assert len(instr.waves) == 2
    assert (instr.sweep == [0, 0, 1, 1]).all()
    assert instr.sweep.loop == 2
    assert instr.sweep.release == 1
def main():
    # Disable volume ratio compensation.
    File.VOLUME_RATIO = 1

    # ratios = []
    cfg_ratio = {}

    for width_ms in list(range(15, 40 + 1, 5)) + [None]:
        print('width_ms =', width_ms)
        for cfg_path in sorted(Path().glob('*.wtcfg')):
            # print(cfg_path)
            file_cfg: dict = yaml.load(cfg_path)
            cfg = to_brr.WavetableConfig(**file_cfg)
            if width_ms:
                cfg.width_ms = width_ms

            wr = WaveReader(cfg_path.parent, cfg)
            cfg_ratio[cfg_path] = calc_ratio(wr)
            # ratios.append(calc_ratio(wr))

        ratios = list(cfg_ratio.values())

        if width_ms is None:
            for k, v in cfg_ratio.items():
                print(k.stem, '', v)
            print()

        # print('Mean: ', np.mean(ratios))
        # print('Median: ', np.median(ratios))
        print('GMean: ', gmean(ratios))
        print()
Example #10
0
def test_reader_pitch_metadata():
    """ Ensure WaveReader obtains root pitch from wav filename. """
    cfg_dict = yaml.load(SINE)
    # root_pitch should be obtained from filename
    read = WaveReader(WAVE_DIR, n163_cfg(**cfg_dict))
    read.read()

    cfg_dict['wav_path'] = 'Sample 19.wav'
    with pytest.raises(TypeError):
        read = WaveReader(WAVE_DIR, n163_cfg(**cfg_dict))
        read.read()
def calc_ratio(wr: WaveReader) -> float:
    assert len(wr.files) == 1
    file = wr.files[0]

    # Hook the file to save input data.
    # [idx][time] = value
    list_data: List[np.ndarray] = []

    def save_output(_channel_data_at):
        @wraps(_channel_data_at)
        def wrapper(*args, **kwargs):
            channel_data: np.ndarray = _channel_data_at(*args, **kwargs)

            assert len(channel_data) == 1
            data = channel_data[0].copy()
            list_data.append(data)

            return channel_data

        return wrapper

    file._channel_data_at = save_output(file._channel_data_at)

    # Read output data.
    instr = wr.read()
    waves = instr.waves

    assert len(waves) == len(list_data)

    # Compare magnitude of input and output data.
    wave_ratios = []
    for input, output in zip(list_data, waves):
        wave_ratios.append(magnitude(output) / magnitude(input))

    # Using
    return float(np.mean(wave_ratios))
Example #12
0
def stereo_read(stereo_cfg) -> WaveReader:
    return WaveReader(CFG_DIR, stereo_cfg)
Example #13
0
def read(cfg) -> WaveReader:
    return WaveReader(CFG_DIR, cfg)
Example #14
0
def process_cfg(global_cli: ExtractorCLI, cfg_path: Path) -> WavetableMetadata:
    """
    Reads cfg_path.
    Writes intermediate wav files to `cfg_dir/WAV_SUBDIR/cfg-012.wav`.
    Writes brr files to `global_cfg.dest_dir/cfg-012.brr`.

    The WAV file contains data + data[:unlooped_prefix].
    The BRR file loops WAV[unlooped_prefix:].
    """
    if not cfg_path.is_file():
        raise ValueError(f'invalid cfg_path {cfg_path}, is not file')

    # Initialize directories
    cfg_name = cfg_path.stem

    cfg_dir = cfg_path.parent   # type: Path
    wav_dir = cfg_dir / WAV_SUBDIR
    brr_dir = global_cli.dest_dir

    wav_dir.mkdir(exist_ok=True)

    for file in wav_dir.glob(f'{cfg_name}-*'):
        file.unlink()

    for file in brr_dir.glob(f'{cfg_name}-*'):
        file.unlink()

    # Load config file
    file_cfg: dict = yaml.load(cfg_path)
    cfg = WavetableConfig(**file_cfg)

    no_brr = cfg.no_brr
    unlooped_prefix = cfg.unlooped_prefix
    truncate_prefix = cfg.truncate_prefix
    gaussian = cfg.gaussian

    brr_arg = BrrEncoderArgs(
        gaussian=gaussian,
        loop=unlooped_prefix,
    )

    # Generate wavetables
    wr = WaveReader(cfg_path.parent, cfg)
    instr = wr.read()
    ntick = wr.ntick
    print(f'    {cfg_name}: ntick={ntick}')

    for i, wave in enumerate(instr.waves):
        wave_name = f'{cfg_name}-{i:03}'

        wav_path = (wav_dir / wave_name).with_suffix('.wav')
        wav_path.parent.mkdir(exist_ok=True)

        # Write WAV file
        wave = np.around(wave)
        wave_int = wave.astype(DTYPE)
        if any(wave_int != wave):
            # Overflows are not automatically caught.
            # https://github.com/numpy/numpy/issues/8987
            raise ValueError('Integer overflow when writing .wav!')
        wave = wave_int

        # Duplicate prefix of wave data
        wave = np.concatenate((wave, wave[:unlooped_prefix]))
        wavfile.write(str(wav_path), SAMPLE_RATE, wave)

        if no_brr:
            continue

        # Encode BRR file
        brr_path = (brr_dir / wave_name).with_suffix('.brr')
        brr = BrrEncoder(brr_arg, wav_path, brr_path)

        # The first sample's "prefix" is used. When we switch loop points to subsequent
        # samples, only their looped portions are used.
        if truncate_prefix and i != 0:
            brr.write(behead=True)
        else:
            brr.write()

    # Generate metadata

    pitches: List[float] = freq2midi(instr.freqs).tolist()

    return WavetableMetadata(
        nsamp=cfg.nsamp,
        ntick=ntick,
        fps=cfg.fps,
        wave_sub=cfg.wave_sub,
        env_sub=cfg.env_sub,
        root_pitch=cfg.root_pitch,
        pitches=pitches,
    )