# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.

# %% auto 0
__all__ = ['Sound']

# %% ../nbs/00_core.ipynb 2
import librosa
import plotly.express as px
from torch_snippets import logger, show, np, pd
from IPython.display import Audio
import soundfile as sf
from fastcore.dispatch import patch_to

# %% ../nbs/00_core.ipynb 3
class Sound:
    def __init__(self, audio, Fs):
        self.audio, self.Fs = audio, Fs

    @classmethod
    def from_path(cls, path):
        audio, Fs = librosa.load(path)
        return cls(audio, Fs)

    def plot(self, slice=None):
        factor = 1 + (int(self.size) // 2)
        audio = self.audio if not slice else self.__getitem__(slice).audio
        Fs = self.Fs
        o = audio[::factor]
        o = pd.DataFrame({'y': o, 'x': np.arange(0, len(audio)/Fs, step=(len(audio)/Fs)/len(o))})
        fig = px.line(o, x='x', y='y')
        show(fig)
        
    def __len__(self):
        return len(self.audio)
    
    def __getitem__(self, item):
        if isinstance(item, slice):
            to, end, step = item.start or 0, item.stop or len(self), item.step or 1
            if step > 0:
                return Sound(self.audio[int(to*self.Fs):int(end*self.Fs):item.step], self.Fs)
            else:
                return Sound(self.audio[int(end*self.Fs):int(to*self.Fs):item.step], self.Fs)
        else:
            return self.audio[item]

    def play(self, x=1):
        logger.info(self.formatted_size[-1])
        assert isinstance(x, int)
        show(Audio(self.audio[::x], rate=self.Fs))

    def write(self, fpath):
        logger.info(f'Writing audiofile of {self.formatted_size[-1]} to {fpath}')
        sf.write(fpath, self.audio, self.Fs, subtype='PCM_24')
        
    def assert_same_sampling_freq(self, other):
        if self.Fs != other.Fs:
            raise NotImplementedError("Yet to implement addition for different freq signals")
        
    def __mul__(self, other):
        self.assert_same_sampling_freq(other)
        return Sound(self.audio + other.audio, self.Fs)

    def __sub__(self, other):
        self.assert_same_sampling_freq(other)
        return Sound(self.audio - other.audio, self.Fs)
    
    def __add__(self, other):
        self.assert_same_sampling_freq(other)
        return Sound(np.r_[self.audio, other.audio], self.Fs)

    
    @property
    def size(self):
        # number of seconds
        return len(self) / self.Fs
        
    @property
    def formatted_size(self):
        _len = self.size; unit = 'seconds'; precision = 3
        if _len > 60: _len = _len / 60; unit = 'minutes'; precision = 2
        if _len > 60: _len = _len / 60; unit = 'hours'; precision = 2
        return _len, unit, precision, f'{_len:.{precision}f} {unit}'

# %% ../nbs/00_core.ipynb 4
@patch_to(Sound, cls_method=True)
def sine(cls, freq, time=1, sampling_freq=22050):
    𝜔 = 2*np.pi*freq
    return cls(np.sin(np.arange(0, time, step=1/sampling_freq)*𝜔), sampling_freq)

    
@patch_to(Sound, cls_method=True)
def blank(cls):
    return cls.sine(0, 0)
