import numpy as np
from matplotlib import pyplot as plt
from scipy.fft import fft, ifft
from IPython.display import Audio, display
from scipy.io import wavfile
from ipywidgets import widgets
from functools import partial
import pandas as pd
from fractions import Fraction

def create_signal(frequency, amplitude, delay, phase=0, sampling=44100, seconds=1.0):
  timesteps = np.linspace(0,seconds, int(seconds * sampling))
  # return np.sum(amplitude * np.sin(frequency * 2*np.pi * (timesteps[:, np.newaxis]-delay)*(timesteps[:, np.newaxis]-delay > 0)), axis=1), timesteps
  return np.sum(amplitude * triangle(frequency, (timesteps[:, np.newaxis]-delay)*(timesteps[:, np.newaxis]-delay > 0)), axis=1), timesteps

def create_waves(frequency, amplitude, phase=0, sampling=44100, seconds=1.0):
  timesteps = np.linspace(0,seconds, int(seconds * sampling))
  # return amplitude * np.sin(frequency * 2*np.pi * timesteps[:, np.newaxis]), timesteps
  return amplitude * triangle(frequency, timesteps[:, np.newaxis]), timesteps

def triangle(frequency, t):
  phase = (t * frequency + 0.25) % 1 - 0.25
  return (phase <= 0.25) * phase + (phase > 0.25) * (0.5 - phase)

def nonlinearity(x, param=1.0): # param for scaling of argument; higher values = more nonlinearity
  #return np.exp(x*param)
  return np.sign(x)*np.log(1+np.abs(x*param))

def myAudio(signal, rate=44100, autoplay=False, time_fade=0.2):
  length = signal.shape[0]
  print(length)
  fade = np.minimum(np.ones(length),np.arange(length)/(time_fade * rate))
  return Audio(signal * fade * fade[::-1],rate=rate,autoplay=autoplay)

#frequency = np.array([100., 200., 300, 400])

#frequency_edo12 = 220 * 2**(np.array([5,8,12,14])/12)
#frequency_edo53 = 220 * 2**(np.array([22,36,53,62])/53)
#frequency_edo53 = 220 * 2**(np.array([22,34,53,61])/53)
#frequency_rein1 = 220 * 4/3 * np.array([1, 7/6, 9/6, 10/6])
#frequency_rein2 = 220 * 4/3 * np.array([1, 12/10, 15/10, 17/10])
dursept_edo12 = 220 * 2**(np.array([5,9,12,15])/12) # Durseptakkord in EDO12
dursept_edo53 = 220 * 2**(np.array([22,39,53,65])/53) # Dursept in EDO53
dursept_rein = 220 * 4/3 * np.array([1, 5/4, 6/4, 7/4]) # Dursept rein
mollsept_edo12 = 220 * 2**(np.array([5,8,12,14])/12) # Mollseptakkord in EDO12
mollsept_edo53 = 220 * 2**(np.array([22,36,53,63])/53) # Mollseptakkord in EDO53
mollsept_rein = 220 * 4/3 * np.array([1, 12/10, 15/10, 17/10]) # Mollsept rein
doppeltritone_rein = 220 * 4/3 * np.array([1, 12/10, 14/10, 17/10]) # doppeltritone rein
doppeltritone_edo53 = 220 * 2**(np.array([22,36,48,63])/53) # doppeltritone in EDO53

amplitude = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0])
delay = np.array([0.0,0.2,0.4,0.6,0.8,1.0,1.2])

# list of chords to display.
# chords = [dursept_edo12, dursept_edo53, dursept_rein, ]
# chords = [mollsept_edo12, mollsept_edo53, mollsept_rein, ]
chords = [doppeltritone_rein, doppeltritone_edo53, ]

to_display = []
for chord in chords:
    notes = len(chord)
    signal, timesteps = create_signal(frequency=chord, amplitude=amplitude[:notes], delay=delay[:notes], phase=0, sampling=44100, seconds=5.0)
    to_display.append(signal)

max_play_time = 2.0
for signal in to_display:
    display(myAudio(signal[timesteps < max_play_time],rate=44100, autoplay=False))

plot_time = 1.0

plt.figure(1, figsize=(20,6))
plt.xlabel("time [s]")
for signal in to_display:
    plt.plot(timesteps[timesteps < plot_time], (1*signal[timesteps < plot_time]))

plt.figure(2, figsize=(40,6))
plt.subplot(111, xscale='log', yscale='log')
plt.xlabel("Frequenz [Hz]")
plt.xlim(200,5000)
plt.ylim(bottom=1e-1, top=1e4)
for signal in to_display:
    plt.plot(np.abs(fft(nonlinearity(signal[timesteps > 1.0], 0.1))))
