Рет қаралды 162
In this video I show how I wrote a little code that evaluates any 1d or 2d function f(t,x) in such a way that the output is stored as an audio file ready to be imported into wavetable synthesizers (Vital in this case) as sound source.
Here is the code, feel free to copy it and replace y in the function f(t,x) by whatever function you want to sonify:
Make a .wav wavetable out of a 2d function evaluation
Open a folder to let the code know where to store the file
importing necessary packages
from numpy import abs, min, max, sin, pi, array, int16 # Basic mathematical operations
import scipy.io.wavfile as wv # Save the result as a .wav file
define your wavetable function (some sine function by default)
def f(t, x):
'''
Example 2d function: sine wave oscillation
t: vector of time points
x: vector of space points (constant over time)
'''
freq = 20
y = sin(pi*x)**2 * sin(2*pi* (freq (t-t_range[0])/(t_range[1]-t_range[0]))*x) sin(2*pi*t)
return y
evaluate the wavetable function
def evaluate(f,timerange,timesteps,spacerange,spacesteps):
'''
evaluate a 2d function
f: 2d function with t and x input vectors
timerange: start and end points of time evaluation
timesteps: total amount of time steps inbetween the timerange points
spacerange: start and end points of space evaluation
spacesteps: total amount of space steps inbetween the spacerange points
'''
timestep = ( timerange[1] - timerange[0]) / timesteps
spacestep = (spacerange[1] - spacerange[0]) / spacesteps
t_grid = [ timerange[0] + i*timestep for i in range(timesteps)]
x_grid = [spacerange[0] + i*spacestep for i in range(spacesteps)]
y = [[f(t, x) for x in x_grid] for t in t_grid]
return t_grid, x_grid, y
generate the audio file
def generate_wav(t_values,y_values,framerate,name):
u_values_array = array(y_values)
audio_data = []
for t in range(len(t_values)):
frame_data = u_values_array[t, :]
audio_data.extend(frame_data)
audio_data = array(audio_data)
16 bit integer wav audio files can store values from -32768 to 32767
audio_data_normalized = int16(audio_data / max(abs(audio_data)) * 32767)
name += '.wav'
wv.write(name, framerate, audio_data_normalized)
Specify the shape of the result
t_range = [0, 0.5] # start and end value of time evaluation
x_range = [0, 1] # start and end value of space evaluation
frames_amount = 256 # how many frames the wavetable should consist of
dots_per_frame = 2048 # datapoints per frame (2048 points per frame is common)
framerate = 44100 # file resolution in kHz
name = 'Wavetable'
Generate the wavetable output
t_values, x_values, y_values = evaluate(f,t_range,frames_amount,x_range,dots_per_frame)
generate_wav(t_values,y_values,framerate,name)
importing optional packages
from numpy import meshgrid # for plotting
import matplotlib.pyplot as plt # Visualize the result
import matplotlib.animation as anim # Make an animation of the result
Plot equidistant slices of the result in 3D
def plot_slices(t_values, x_values, y_values, num_slices=5):
'''
Plot 5 equidistant slices of the result in 3D
t_values: evaluated time values
x_values: evaluated space values
y_values: evaluated function values
num_slices: number of slices to plot
'''
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
slice_indices = [int(i * len(t_values) / num_slices) for i in range(num_slices)]
for i, slice_idx in enumerate(slice_indices):
x = x_values
y = y_values[slice_idx]
z = [t_values[slice_idx]] * len(x_values)
ax.plot(x, z, y, label=f'Slice at t = {t_values[slice_idx]:.3f}')
ax.set_xlabel('Space')
ax.set_ylabel('Time')
ax.set_zlabel('Amplitude')
ax.view_init(elev=30, azim=30)
plt.legend()
plt.show()
plot the result as an animation
def animate(t_range, t_values, x_values, f_values, frames_amount):
fig, ax = plt.subplots()
line, = ax.plot(x_values, [0 for _ in range(len((x_values)))])
use min and max of space and function to have the function always centered
ax.set_xlim(min(x_values) - 0.1, max(x_values) + 0.1)
ax.set_ylim(min(f_values) - 0.1, max(f_values) + 0.1)
time_text = ax.text(0.02, 0.95, '', transform=ax.transAxes)
frame update
def update(t):
y_values = f_values[t]
line.set_ydata(y_values)
current_time = t_values[t]
time_text.set_text(f'Time: {current_time:.2f} s')
return line, time_text
duration = (t_range[1] - t_range[0]) / frames_amount * 1000
ani = anim.FuncAnimation(fig, update, frames=len(t_values),interval=duration)
plt.show()
plot_surface(t_values, x_values, y_values)
plot_slices(t_values, x_values, y_values)
animate(t_range, t_values, x_values, y_values, frames_amount)