Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7cdbde5

Browse files
audio reactive mesh surface
this builds on the terrain.py file and adds the audio re-activeness using pyaudio
1 parent 142de34 commit 7cdbde5

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

‎terrain_audio.py‎

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""
2+
This creates a 3D mesh with perlin noise to simulate
3+
a terrain. The mesh is animated by shifting the noise
4+
to give a "fly-over" effect.
5+
6+
If you don't have pyOpenGL or opensimplex, then:
7+
8+
- conda install -c anaconda pyopengl
9+
- pip install opensimplex
10+
"""
11+
12+
import numpy as np
13+
from opensimplex import OpenSimplex
14+
import pyqtgraph.opengl as gl
15+
from pyqtgraph.Qt import QtCore, QtGui
16+
import struct
17+
import pyaudio
18+
import sys
19+
20+
21+
class Terrain(object):
22+
def __init__(self):
23+
"""
24+
Initialize the graphics window and mesh surface
25+
"""
26+
27+
# setup the view window
28+
self.app = QtGui.QApplication(sys.argv)
29+
self.window = gl.GLViewWidget()
30+
self.window.setWindowTitle('Terrain')
31+
self.window.setGeometry(0, 110, 1920, 1080)
32+
self.window.setCameraPosition(distance=30, elevation=12)
33+
self.window.show()
34+
35+
# constants and arrays
36+
self.nsteps = 1.3
37+
self.offset = 0
38+
self.ypoints = np.arange(-20, 20 + self.nsteps, self.nsteps)
39+
self.xpoints = np.arange(-20, 20 + self.nsteps, self.nsteps)
40+
self.nfaces = len(self.ypoints)
41+
42+
self.RATE = 44100
43+
self.CHUNK = len(self.xpoints) * len(self.ypoints)
44+
45+
self.p = pyaudio.PyAudio()
46+
self.stream = self.p.open(
47+
format=pyaudio.paInt16,
48+
channels=1,
49+
rate=self.RATE,
50+
input=True,
51+
output=True,
52+
frames_per_buffer=self.CHUNK,
53+
)
54+
55+
# perlin noise object
56+
self.noise = OpenSimplex()
57+
58+
# create the veritices array
59+
verts, faces, colors = self.mesh()
60+
61+
self.mesh1 = gl.GLMeshItem(
62+
faces=faces,
63+
vertexes=verts,
64+
faceColors=colors,
65+
drawEdges=True,
66+
smooth=False,
67+
)
68+
self.mesh1.setGLOptions('additive')
69+
self.window.addItem(self.mesh1)
70+
71+
def mesh(self, offset=0, height=2.5, wf_data=None):
72+
73+
if wf_data is not None:
74+
wf_data = struct.unpack(str(2 * self.CHUNK) + 'B', wf_data)
75+
wf_data = np.array(wf_data, dtype='b')[::2] + 128
76+
wf_data = np.array(wf_data, dtype='int32') - 128
77+
wf_data = wf_data * 0.04
78+
wf_data = wf_data.reshape((len(self.xpoints), len(self.ypoints)))
79+
else:
80+
wf_data = np.array([1] * 1024)
81+
wf_data = wf_data.reshape((len(self.xpoints), len(self.ypoints)))
82+
83+
faces = []
84+
colors = []
85+
verts = np.array([
86+
[
87+
x, y, wf_data[xid][yid] * self.noise.noise2d(x=xid / 5 + offset, y=yid / 5 + offset)
88+
] for xid, x in enumerate(self.xpoints) for yid, y in enumerate(self.ypoints)
89+
], dtype=np.float32)
90+
91+
for yid in range(self.nfaces - 1):
92+
yoff = yid * self.nfaces
93+
for xid in range(self.nfaces - 1):
94+
faces.append([
95+
xid + yoff,
96+
xid + yoff + self.nfaces,
97+
xid + yoff + self.nfaces + 1,
98+
])
99+
faces.append([
100+
xid + yoff,
101+
xid + yoff + 1,
102+
xid + yoff + self.nfaces + 1,
103+
])
104+
colors.append([
105+
xid / self.nfaces, 1 - xid / self.nfaces, yid / self.nfaces, 0.7
106+
])
107+
colors.append([
108+
xid / self.nfaces, 1 - xid / self.nfaces, yid / self.nfaces, 0.8
109+
])
110+
111+
faces = np.array(faces, dtype=np.uint32)
112+
colors = np.array(colors, dtype=np.float32)
113+
114+
return verts, faces, colors
115+
116+
def update(self):
117+
"""
118+
update the mesh and shift the noise each time
119+
"""
120+
121+
wf_data = self.stream.read(self.CHUNK)
122+
123+
verts, faces, colors = self.mesh(offset=self.offset, wf_data=wf_data)
124+
self.mesh1.setMeshData(vertexes=verts, faces=faces, faceColors=colors)
125+
self.offset -= 0.05
126+
127+
def start(self):
128+
"""
129+
get the graphics window open and setup
130+
"""
131+
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
132+
QtGui.QApplication.instance().exec_()
133+
134+
def animation(self, frametime=10):
135+
"""
136+
calls the update method to run in a loop
137+
"""
138+
timer = QtCore.QTimer()
139+
timer.timeout.connect(self.update)
140+
timer.start(frametime)
141+
self.start()
142+
143+
144+
if __name__ == '__main__':
145+
t = Terrain()
146+
t.animation()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /