circulartext commited on
Commit
b812afe
·
verified ·
1 Parent(s): a7e8153

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +253 -0
app.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from matplotlib.animation import FuncAnimation, PillowWriter
5
+ import scipy.io.wavfile as wavfile
6
+ import io
7
+
8
+ # Constants for sound generation
9
+ SAMPLE_RATE = 48000
10
+ COLUMN_DURATION = 1 # Duration of each column in seconds
11
+
12
+ # Mapping of matrix numbers to musical notes
13
+ notes = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]
14
+ note_map = {2: "A", 3: "A#", 4: "B", 5: "C", 6: "C#", 7: "D", 8: "D#", 9: "E",
15
+ 10: "F", 11: "F#", 12: "G", 13: "G#"}
16
+
17
+ def generate_tone(note, duration):
18
+ """Generates a tone for a specified note and duration."""
19
+ frequency = 440 * 2 ** ((notes.index(note) - 9) / 12)
20
+ t = np.linspace(0, duration, int(SAMPLE_RATE * duration), endpoint=False)
21
+ return 0.5 * np.sin(2 * np.pi * frequency * t)
22
+
23
+ def get_matrix_audio(matrix):
24
+ """Generate audio sequence for one matrix state."""
25
+ audio_sequence = []
26
+
27
+ for col in range(matrix.shape[1]):
28
+ col_audio = np.zeros(0)
29
+ for row in range(matrix.shape[0]):
30
+ num = matrix[row, col]
31
+ if num > 1:
32
+ note = note_map.get(num, "A")
33
+ tone = generate_tone(note, duration=COLUMN_DURATION / matrix.shape[0])
34
+ col_audio = np.concatenate((col_audio, tone))
35
+
36
+ audio_sequence.append(col_audio)
37
+
38
+ return np.concatenate(audio_sequence)
39
+
40
+ class NumberSpreadSimulator:
41
+ def __init__(self, initial_matrix):
42
+ self.grid = np.array(initial_matrix, dtype=int)
43
+ self.audio_frames = []
44
+ self.original_numbers = self.find_original_numbers()
45
+ self.initialize_audio()
46
+
47
+ def find_original_numbers(self):
48
+ """Find all original numbers in the initial matrix."""
49
+ numbers = set()
50
+ for row in range(self.grid.shape[0]):
51
+ for col in range(self.grid.shape[1]):
52
+ if self.grid[row, col] > 1:
53
+ numbers.add(self.grid[row, col])
54
+ return list(numbers)
55
+
56
+ def initialize_audio(self):
57
+ """Generate audio for initial state and store it."""
58
+ self.audio_frames.append(get_matrix_audio(self.grid))
59
+
60
+ def find_original_number(self, current):
61
+ """Find the original source number based on the current number."""
62
+ closest = min(self.original_numbers, key=lambda x: abs(x - current))
63
+ return closest
64
+
65
+ def step(self):
66
+ """Simulates a step in the matrix spread."""
67
+ new_grid = np.zeros_like(self.grid)
68
+ has_changes = False
69
+
70
+ # Process each position in the grid
71
+ for row in range(self.grid.shape[0]):
72
+ for col in range(self.grid.shape[1]):
73
+ current = self.grid[row, col]
74
+
75
+ if current > 1:
76
+ has_changes = True
77
+ half = current // 2
78
+
79
+ # Define potential target positions
80
+ targets = []
81
+ if current % 2 == 0: # Even
82
+ if row > 0: targets.append((row - 1, col, half))
83
+ if col > 0: targets.append((row, col - 1, half))
84
+ else: # Odd
85
+ if col > 0: targets.append((row, col - 1, half))
86
+ if row > 0: targets.append((row - 1, col, half))
87
+ if row > 0 and col > 0: targets.append((row - 1, col - 1, 1))
88
+
89
+ # Process each target position
90
+ for target_row, target_col, value in targets:
91
+ if self.grid[target_row, target_col] == -1:
92
+ # If target is -1, replace with original number
93
+ new_grid[target_row, target_col] = self.find_original_number(current)
94
+ elif self.grid[target_row, target_col] == -2:
95
+ # If target is -2, double the incoming value
96
+ new_grid[target_row, target_col] = value * 2
97
+ elif self.grid[target_row, target_col] == -3:
98
+ # If target is -3, set to 0
99
+ new_grid[target_row, target_col] = 0
100
+ else:
101
+ new_grid[target_row, target_col] += value
102
+
103
+ # Copy over any remaining special values
104
+ if (self.grid[row, col] in [-1, -2, -3]) and new_grid[row, col] == 0:
105
+ new_grid[row, col] = self.grid[row, col]
106
+
107
+ self.grid = new_grid
108
+ if has_changes:
109
+ self.audio_frames.append(get_matrix_audio(self.grid))
110
+
111
+ return has_changes, self.grid
112
+
113
+ def create_animation(matrix):
114
+ # Initialize simulator
115
+ sim = NumberSpreadSimulator(matrix)
116
+
117
+ # Determine matrix dimensions
118
+ rows, cols = sim.grid.shape
119
+
120
+ # Create figure and axis with dynamic limits
121
+ fig, ax = plt.subplots(figsize=(10, 10))
122
+ ax.set_xlim(-0.5, cols - 0.5)
123
+ ax.set_ylim(-0.5, rows - 0.5)
124
+ ax.set_title("Matrix Spread Visualization")
125
+
126
+ # Initialize plot elements
127
+ circles = []
128
+ labels = []
129
+
130
+ for i in range(rows):
131
+ for j in range(cols):
132
+ circle = plt.Circle((j, rows - 1 - i), 0.2,
133
+ color='blue',
134
+ fill=False)
135
+ label = ax.text(j, rows - 1 - i, '', ha='center', va='center', fontsize=12)
136
+ circles.append(circle)
137
+ labels.append(label)
138
+ ax.add_patch(circle)
139
+
140
+ current_frame = [0]
141
+
142
+ def update(frame):
143
+ if current_frame[0] == 0:
144
+ matrix = sim.grid
145
+ else:
146
+ has_changes, matrix = sim.step()
147
+ if not has_changes:
148
+ ani.event_source.stop()
149
+ return
150
+
151
+ for i in range(rows):
152
+ for j in range(cols):
153
+ value = matrix[i, j]
154
+ index = i * cols + j
155
+
156
+ if value != 0:
157
+ circles[index].set_radius(0.1 + 0.1 * (abs(value) / 10))
158
+ if value == -1:
159
+ circles[index].set_facecolor('green')
160
+ elif value == -2:
161
+ circles[index].set_facecolor('purple')
162
+ elif value == -3:
163
+ circles[index].set_facecolor('red')
164
+ else:
165
+ circles[index].set_facecolor('orange')
166
+ else:
167
+ circles[index].set_radius(0.1)
168
+ circles[index].set_facecolor('blue')
169
+ labels[index].set_text(str(value) if value != 0 else '')
170
+
171
+ current_frame[0] += 1
172
+ return circles + labels
173
+
174
+ ani = FuncAnimation(fig, update, frames=None, interval=1000, blit=True)
175
+
176
+ # Save the animation to a GIF
177
+ gif_buffer = io.BytesIO()
178
+ ani.save(gif_buffer, format='gif', writer=PillowWriter(fps=1))
179
+ plt.close(fig)
180
+ gif_buffer.seek(0)
181
+
182
+ return gif_buffer
183
+
184
+ def run_simulation(matrix_input):
185
+ """
186
+ Run the full simulation based on user-input matrix
187
+
188
+ :param matrix_input: 2D list of integers representing the matrix
189
+ :return: tuple of (audio_path, gif_buffer)
190
+ """
191
+ # Convert input to numpy array
192
+ matrix = np.array(matrix_input, dtype=int)
193
+
194
+ # Initialize simulator
195
+ sim = NumberSpreadSimulator(matrix)
196
+
197
+ # Run simulation until no more changes
198
+ while True:
199
+ has_changes, _ = sim.step()
200
+ if not has_changes:
201
+ break
202
+
203
+ # Generate audio
204
+ final_audio = np.concatenate(sim.audio_frames)
205
+ final_audio = np.int16(final_audio * 32767)
206
+
207
+ # Save audio
208
+ audio_path = "matrix_sound.wav"
209
+ wavfile.write(audio_path, SAMPLE_RATE, final_audio)
210
+
211
+ # Create animation
212
+ gif_buffer = create_animation(matrix)
213
+
214
+ return audio_path, gif_buffer
215
+
216
+ # Gradio Interface
217
+ def create_gradio_interface():
218
+ # Default initial matrix
219
+ default_matrix = [
220
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
221
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
222
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
223
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
224
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 47]
225
+ ]
226
+
227
+ # Create Gradio interface
228
+ iface = gr.Interface(
229
+ fn=run_simulation,
230
+ inputs=[
231
+ gr.Dataframe(
232
+ headers=[str(i) for i in range(10)],
233
+ datatype="number",
234
+ value=default_matrix,
235
+ type="numpy",
236
+ label="Edit Matrix Values"
237
+ )
238
+ ],
239
+ outputs=[
240
+ gr.Audio(type="filepath", label="Generated Sound"),
241
+ gr.Image(type="file", label="Matrix Animation")
242
+ ],
243
+ title="Number Spread Simulator",
244
+ description="Edit the matrix and see how numbers spread, generating a unique sound and animation!"
245
+ )
246
+
247
+ return iface
248
+
249
+ # Launch the interface
250
+ iface = create_gradio_interface()
251
+
252
+ if __name__ == "__main__":
253
+ iface.launch()