Making Music with Python - python

Trying to create some musical notes by combining harmonic series. Very simple code, but the audio turns up blank. Any thoughts?
from IPython.display import Audio
import numpy as np
import matplotlib.pyplot as plt
def Harmonic(i,linComb):
x=np.linspace(0,3,24000)
y = [0 for _ in x]
weights = linComb
for n in range(0,i):
y += np.sin((2*n+1)*(2*np.pi*weights[n])*(x))/(2*n+1)
plt.plot(x,y)
plt.show()
return y
out = Harmonic(3,[0,2,3])
Audio(data=out, rate=8000)
Stuff I've tried:
Changing the rate
Manipulating the y-values
Ensuring the harmonic function does indeed work
Looking at this answer (same function, but still doesn't work)
Would appreciate any help. Thanks.

The sound generated by the code is audible but weak.
I have no experience in audio programming, but some type of noise resembling a loud beep can be generated by the following:
from IPython.display import Audio
import numpy as np
import matplotlib.pyplot as plt
def Harmonic(i, weights):
x=np.linspace(0,3,24000)
y = [0 for _ in x]
for n in range(0,i):
y += np.sin((2*n+1)*(2*np.pi*weights[n])*(x))/(2*n+1)
plt.plot(x,y)
plt.show()
return y
i = 1000
weights = [1000] * 1000 # Length equal to i
out = Harmonic(i, weights)
Audio(data=out, rate=8000)

Related

I can't animate a surface with Mayavi

I'm trying to do a simple animation of a surface with Mayavi, but due to the lack of online examples (the few are extremely unclear or not useful) and official documentation, I'm struggling a lot. I wrote a very simple code to show where I've gone so far
import numpy as np
from mayavi import mlab
import time
#Meshgrid + u + Surface
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
u = np.ones((10,10))
surf = mlab.mesh(X,Y,u)
#Surface animation
#mlab.animate(delay=1000)
def anim():
for n in range(1,10):
global u
print(n)
u = u+1
surf.mlab_source.scalars = u
yield
anim()
mlab.show()
So very simple, every iteration I pass from a 10x10 matrix of ones to a 10x10 matrix of twos and so on.
The problems I've encountered are 3
I really can't understand what mlab_source.scalars does: fundamentally I don't know how to tell it that the z axis is changing and in doing so it follows the variation of u
Mayavi opens up, but the grid is all plane and black, like there is some issues with u
I don't understand what yield does: in the actual code I'm writing it stops the computation in a lot of advance (like in 300 steps it stops it at the step 29), but I can't remove it
I really can't get my head around, it's weeks I'm trying, hope somebody helps. Thanks in advance.
Edit:
I tried to extend #E.Klahn code in a case resembling more mine
import numpy as np
from mayavi import mlab
s = 0.01
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
Z = np.ones((10,10))
m = mlab.mesh(X, Y, Z)
#mlab.animate(delay=20)
def anim():
for i in range(1,100):
m.mlab_source.z = np.ones((10,10))*s*i
yield
anim()
mlab.show()
But I obtain only a flat surface going up, not a tridimensional object evolving, such as in his code.
Edit2:
Here the working code just copying the example of #E.Klahn, which I thank very very much
import numpy as np
from mayavi import mlab
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
X,Y=X.T,Y.T #seems an important command
u = np.ones((10,10))
surf = mlab.surf(X,Y,u)
#mlab.animate(delay=500)
def anim():
for n in range(1,10):
print(n)
surf.mlab_source.scalars = 1+np.sin(X)*np.sin(Y)*np.sin(n)
yield
anim()
mlab.show()
I just print it to give continuity to code above and to spot the differences.
Here I've shown the code to plot a cube and animate it so that it grows out from a height of 0.01 to a height of 1 using mlab.mesh. What yield does is that it returns control to the decorator so that the scene can be updated.
import numpy as np
from mayavi import mlab
s = 0.01
X = np.array([[0,0,1,1],[0,0,1,1],[0,0,0,0],[1,1,1,1]])
Y = np.array([[0,1,1,0],[0,1,1,0],[1,0,0,1],[1,0,0,1]])
Z = np.array([[0,0,0,0],[s,s,s,s],[0,0,s,s],[0,0,s,s]])
m = mlab.mesh(X, Y, Z)
#mlab.animate(delay=20)
def anim():
for i in range(1,101):
print(i, end='\r')
m.mlab_source.z = np.array([[0,0,0,0],[s*i,s*i,s*i,s*i],[0,0,s*i,s*i],[0,0,s*i,s*i]])
yield
anim()
mlab.show()
scalars is one way to access the underlying data structure if that data structure has the scalars attribute. However, for mesh that attribute is not used, rather you want to access z and set those values. I would not know what your specific issue with u if unless there is a traceback to work from. The same with yield - I see no reason why that keyword would stop the animation at any particular step.
The code below shows, instead of a box evolving over time, a surface evolving with time.
import numpy as np
from mayavi import mlab
def evolving_function(X, Y, t, Lx=1, Ly=1):
return np.sin(X+Lx*t)*np.sin(Y+Ly*t)
Lx, Ly = 1,2
x = np.linspace(-10,10,100)
y = np.linspace(-10,10,100)
X,Y = np.meshgrid(x,y)
X, Y = X.T, Y.T
s = evolving_function(X, Y, 0, Lx=Lx, Ly=Ly)
m = mlab.surf(X, Y, s)
dt = 50
t = 10
steps = int(10*1000/dt)
#mlab.animate(delay=dt)
def anim():
for i in range(1,steps):
m.mlab_source.scalars = evolving_function(X, Y, dt*i, Lx=Lx, Ly=Ly)
yield
anim()
mlab.show()

Distribution plot with wrong total value

To create
I have made a distribution plot with code below:
from numpy import *
import numpy as np
import matplotlib.pyplot as plt
sigma = 4.1
x = np.linspace(-6*sigma, 6*sigma, 200)
def distr(n):
def g(x):
return (1/(sigma*sqrt(2*pi)))*exp(-0.5*(x/sigma)**2)
FxSum = 0
a = list()
for i in range(n):
# divide into 200 parts and sum one by one
numb = g(-6*sigma + (12*sigma*i)/n)
FxSum += numb
a.append(FxSum)
return a
plt.plot(x, distr(len(x)))
plt.show()
This is, of course, a way of getting the result without using hist(), cdf() or any other options from Python libraries.
Why the total sum is not 1? It shouldn't depend from (for example) sigma.
Almost right, but in order to integrate you have to multiply the function value g(x) times your tiny interval dx (12*sigma/200). That's the area you sum up:
from numpy import *
import numpy as np
import matplotlib.pyplot as plt
sigma = 4.1
x = np.linspace(-6*sigma, 6*sigma, 200)
def distr(n):
def g(x):
return (1/(sigma*sqrt(2*pi)))*exp(-0.5*(x/sigma)**2)
FxSum = 0
a = list()
for i in range(n):
# divide into 200 parts and sum one by one
numb = g(-6*sigma + (12*sigma*i)/n) * (12*sigma/200)
FxSum += numb
a.append(FxSum)
return a
plt.plot(x, distr(len(x)))
plt.show()

The Birthday paradox - how to plot

from __future__ import division, print_function
from numpy.random import randint
import random
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
def bday(c):
trials = 5000
count = 0
for trial in range(trials):
year = [0]*365
l = False
for i in range(c):
bdayp = randint(1,365)
year[bdayp] = year[bdayp] + 1
if year[bdayp] > 1:
l = True
if l == True:
count = count + 1
prob = count / trials
return prob
for i in range(2,41):
a = bday(i)
print(i,a)
As you can see, I generate the number of people in the class along with the probability that they share a birthday. How can I plot this so that I have n (number of people) on the x-axis and probability on the y-axis using matplotlib.pyplot?
Thanks.
I've linked in the comments the proper documentation to your problem. For the sake of you finding your own solution, perhaps looking at the following might make more sense of how to go about your problem:
def func(x):
return x * 10
x = []
y = []
for i in range(10):
x.append(i)
y.append(func(i))
plt.plot(x, y)
The above can also be achieved by doing the following:
def func(x):
return x * 10
x = np.arange(10)
plt.plot(x, func(x))
Here is the documentation for np.arange; both will plot the following:

Bifurcation diagram in matplotlib

I'm trying to acquire the bifurcation diagram for the equation below:
(x is a function of t)
as:
And here is my snippet:
import numpy as np
import matplotlib.pyplot as plt
def pitch(r, x):
return r * x + np.power(x,3)- np.power(x,5)
n = 10000
r = np.linspace(-200, 200, n)
iterations = 1000
last = 100
x = 0
for i in range(iterations):
x = pitch(r,x)
if i >= (iterations - last):
plt.plot(r,x, ',k', alpha=0.02)
plt.title("Bifurcation diagram")
plt.show()
But the generated plot is not what it is supposed to be:
Edit:
Here is my recent attempt:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def pitch(s,x,r):
x = s[0]
dxdt = r * x + np.power(x,3)- np.power(x,5)
return [dxdt]
t = np.linspace(0,100)
s0=[-50]
r = np.linspace(-200, 200)
for i in r:
s = odeint(pitch,s0,t, args=(i,))
plt.plot(s,i,',k', alpha=0.02)
plt.title("Bifurcation diagram")
plt.show()
With this error:
raise ValueError("x and y must have same first dimension") ValueError:
x and y must have same first dimension
Could you give me some advice to fix this problem?!
I found a link to this post and decided to post a few remarks that might be helpful to someone stumbling upon it in the future.
I did not analyze the equation in detail but it is clear from the first sight that something interesting would happen when r is close to 0.
So we could study the behavior of the system for r in [-10,10]
You are right to use odeint instead of solving the Cauchy problem using Euler method coded by yourself.
This equation has an attractor in that it soon "forgets" the initial condition and slides towards the attractor, yet the choice of the attractor depends on where in relation to 0 do we start. Large positive initial conditions would slide to the negative attractor and vice versa as - x^5 is the term that defines the behavior at large x.
What we need to do is for each r in the range put a mark at the attractor that the solution slides to for each initial condition.
We first create a canvas to put marks into:
diagram = np.zeros((200,200))
And then for each combination of (r,s0) we put a point on the canvas at (r,s[-1]).
Here is the complete code
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def pitch(s,x,r):
x = s[0]
dxdt = r * x + np.power(x,3)- np.power(x,5)
return [dxdt]
t = np.arange(0,100,2)
s0=[-50]
N = 200 # Number of points along each side of the diagram
diagram = np.zeros((N,N))
rmin,rmax = -10,10
rrange = np.arange(rmin, rmax,(rmax-rmin)/N)
smin,smax = -5.0,5.0
srange = np.arange(smin,smax,2*(smax-smin)/N)
for i in rrange:
for s0 in srange:
s = odeint(pitch,[s0],t, args=(i,))
imgx = int((i-rmin)*N/(rmax-rmin))
imgy = int((s[-1]-smin)/(smax-smin)*N)
imgx = min(N-1,max(0,imgx)) # make sure we stay
imgy = min(N-1,max(0,imgy)) # within the diagram bounds
diagram[imgy,imgx] = 1
plt.title("Bifurcation diagram")
plt.imshow(np.flipud(diagram),cmap=cm.Greys,
extent=[rmin,rmax,smin,smax],aspect=(rmax-rmin)/(smax-smin))
plt.xlabel("r")
plt.ylabel("x")
plt.show()
And the resulting plot
When you zoom in into the region around 0 by setting (rmin,rmax) to (-0.5,0.5) you could see that the branches of the diagram do not start at 0
Instead as in the diagram drawn in the original post the branches start at roughly r=-0.25

Heat diffusion on a 2d plate- Python

I need to complete a task, and I'm kind of in the middle of nowhere. I dont know whats wrong with the solver, and I dont know how to visualize the data.
My task is to simulate heat diffusion on a 2D plate. It has to have 2 heaters, and two holes in the middle of the plate. BCs are as always zero.
My questions are:
How do I make simulation run through time? I'm asking this because when I plot it this way it shows me heaters on a plate, but all of the heat is concentrated on them. Another question is, how can I visualize the results as video or as pictures at given time?
Here is my code. Thank you.
import numpy as np
import matplotlib.pyplot as plt
dt=0.1
dx=0.1
L=50 #length of the plate
Ly=np.linspace(0,L,50)
B=50 #width of the plate
Bx=np.linspace(0,B,50)
M=np.zeros([L,B]) #matrix
#heating device shaped like X
Gr=np.eye(10)*2000
for iGr in range(10):
Gr[iGr,-iGr-1]=2000
#implementing heaters to matrix
M[20:30,10:20]=Gr
M[20:30,30:40]=Gr
t=0
#wannabe solver
while t<10:
t=0.1+t
for j in range(1,L-1):
for i in range(1,B-1):
if 24<j<28:
if 29<i<32:
k=0
elif 23<i<20: # holes for liquid
k=0
else:
break
else:
k=0.5
break
M[i,j]=M[i,j]+k*((dt)/dx**2)*(M[i,j+1]-2*M[i,j]+M[i,j-1])+k*((dt)/dx**2)*(M[i+1,j]-2*M[i,j]+M[i-1,j])
plt.pcolormesh(M)
plt.colorbar()
plt.show()
This might get you started. I'm not familiar with your heat transfer function (or heat transfer functions in general) so I used a different one for these purposes.
The following code computes M for each step dt, and appends it to a list MM.
We then use FuncAnimation to step through the elements of MM (recall that the elements of MM are the snapshots of matrix M) and display them.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
dt=0.1
dx=0.1
L=50 # length of the plate
B=50 # width of the plate
#heating device shaped like X
Gr=np.eye(10)*2000
for iGr in range(10):
Gr[iGr,-iGr-1]=2000
# Function to set M values corresponding to non-zero Gr values
def assert_heaters(M, Gr):
M[20:30,10:20] = np.where(Gr > 0, Gr, M[20:30,10:20])
M[20:30,30:40] = np.where(Gr > 0, Gr, M[20:30,30:40])
M=np.zeros([L,B]) # matrix
assert_heaters(M, Gr)
# Build MM, a list of matrices, each element corresponding to M at a given step
T = np.arange(0,10,dt)
MM = []
for itime in xrange(len(T)):
for j in range(1,L-1):
for i in range(1,B-1):
k=0.5 # default k
if 24<j<28:
# holes for liquid
if 29<i<32 or 23<i<20: k=0
#dm = k * ((dt)/dx**2) * (M[i,j+1] + M[i,j-1] - 2*M[i,j]) + \
# k * ((dt)/dx**2) * (M[i+1,j] + M[i-1,j] - 2*M[i,j])
#M[i,j] += dm
M[i,j] = (M[i-1,j] + M[i+1,j] + M[i,j-1] + M[i,j+1])/4
# Re-assert heaters
assert_heaters(M, Gr)
MM.append(M.copy())
fig = plt.figure()
pcm = plt.pcolormesh(MM[0])
plt.colorbar()
# Function called to update the graphic
def step(i):
if i >= len(MM): return
pcm.set_array(MM[i].ravel())
plt.draw()
anim = FuncAnimation(fig, step, interval=50)
plt.show()
You'll have to correct the transfer function to your own of course.
This code produces something like this animation (Note, it's 3M, too big to embed in the answer)
Compressed version:

Categories