Skip to content

Commit bb28321

Browse files
Update main.py
1 parent 3b43b8a commit bb28321

File tree

1 file changed

+37
-48
lines changed

1 file changed

+37
-48
lines changed

main.py

Lines changed: 37 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import struct
22
from argparse import ArgumentParser
33
from pathlib import Path
4-
54
import numpy as np
65
import scipy
76

@@ -21,7 +20,12 @@
2120
can_log = args.output is not None or debug_output
2221
spectrogram_period = args.period
2322

24-
input_path = args.input.expanduser()
23+
if can_log:
24+
print(f"Arguments received: {args}")
25+
26+
input_path = args.input.expanduser().resolve()
27+
if can_log:
28+
print(f"Opening audio {input_path}")
2529

2630
# Read WAV file
2731
sample_rate, data = scipy.io.wavfile.read(input_path)
@@ -44,7 +48,8 @@ def constrain(value, min_value, max_value):
4448
def create_sound_instruction(start_freq: int, end_freq: int, start_vol: int,
4549
end_vol: int, duration: int) -> str:
4650
return struct.pack("<BBHHHHH",
47-
3, 0,
51+
3, # sine waveform
52+
0,
4853
max(start_freq, 1),
4954
duration,
5055
constrain(start_vol, 0, 1024),
@@ -53,65 +58,52 @@ def create_sound_instruction(start_freq: int, end_freq: int, start_vol: int,
5358
).hex()
5459

5560

56-
def moving_average_1d(arr, window_size=3):
57-
return np.convolve(arr, np.ones(window_size)/window_size, mode="same")
58-
59-
60-
def moving_average_2d(arr, window_size=3):
61-
smoothed = np.zeros_like(arr)
62-
for i in range(arr.shape[0]):
63-
smoothed[i] = moving_average_1d(arr[i], window_size)
64-
return smoothed
65-
61+
def audio_to_makecode_arcade(data, sample_rate, period, gain=2.5) -> str:
62+
"""Convert audio to MakeCode Arcade code with louder output"""
63+
spectrogram_frequency = period / 1000
64+
if can_log:
65+
print(f"Generating spectrogram with a period of {period} ms.")
6666

67-
def audio_to_makecode_arcade(data, sample_rate, period) -> str:
67+
# Generate spectrogram
6868
f, t, Sxx = scipy.signal.spectrogram(
6969
data,
7070
sample_rate,
71-
nperseg=round((period/1000) * sample_rate)
71+
nperseg=round(spectrogram_frequency * sample_rate)
7272
)
7373

7474
frequency_buckets = [50, 159, 200, 252, 317, 400, 504, 635, 800, 1008,
75-
1270, 1600, 2016, 2504, 3200, 4032, 5080, 7000, 9000]
75+
1270, 1600, 2016, 2504, 3200, 4032, 5080, 7000, 9000, 10240]
7676

7777
max_freqs = 30
7878
loudest_indices = np.argsort(Sxx, axis=0)[-max_freqs:]
7979
loudest_frequencies = f[loudest_indices].transpose()
8080
loudest_amplitudes = Sxx[loudest_indices, np.arange(Sxx.shape[1])].transpose()
81-
max_amp = np.max(Sxx)
8281

83-
# Smooth amplitudes
84-
loudest_amplitudes = moving_average_2d(loudest_amplitudes, window_size=3)
85-
86-
# Create dedicated threads per bucket
87-
threads = [[] for _ in frequency_buckets]
82+
# Apply gain and normalize per bucket
83+
sound_instruction_buffers = [""] * len(frequency_buckets)
84+
max_amp = np.max(loudest_amplitudes)
8885

8986
for slice_index in range(len(loudest_frequencies)):
90-
prev_bucket_high = 0
91-
for bucket_index, bucket_high in enumerate(frequency_buckets):
87+
for bucket_index in range(len(frequency_buckets)):
9288
freqs = loudest_frequencies[slice_index]
93-
amps = loudest_amplitudes[slice_index]
94-
95-
# pick the loudest frequency in this bucket range
96-
freq_idx = -1
97-
for i in range(len(freqs)-1, -1, -1):
98-
if prev_bucket_high <= freqs[i] <= bucket_high:
99-
freq_idx = i
89+
low = frequency_buckets[bucket_index - 1] if bucket_index > 0 else 0
90+
high = frequency_buckets[bucket_index]
91+
freq_index = -1
92+
for i in range(len(freqs) - 1, -1, -1):
93+
if low <= freqs[i] <= high:
94+
freq_index = i
10095
break
101-
102-
if freq_idx != -1:
103-
freq = round(freqs[freq_idx])
104-
amp = round(amps[freq_idx] / max_amp * 1024)
105-
threads[bucket_index].append(create_sound_instruction(freq, freq, amp, amp, period))
96+
if freq_index != -1:
97+
freq = round(freqs[freq_index])
98+
amp = round(min(1024, (loudest_amplitudes[slice_index, freq_index] / max_amp * 1024) * gain))
99+
sound_instruction_buffers[bucket_index] += create_sound_instruction(freq, freq, amp, amp, period)
106100
else:
107-
threads[bucket_index].append(create_sound_instruction(0, 0, 0, 0, period))
108-
109-
prev_bucket_high = bucket_high
101+
sound_instruction_buffers[bucket_index] += create_sound_instruction(0, 0, 0, 0, period)
110102

111-
# Wrap each buffer in hex`` properly
112-
sound_instruction_buffers = [f"hex`{''.join(thread)}`" for thread in threads]
103+
# Wrap buffers in hex`` properly
104+
sound_instruction_buffers = [f"hex`{buf}`" for buf in sound_instruction_buffers]
113105

114-
# MakeCode TS code (only queuePlayInstructions)
106+
# MakeCode output
115107
code = (
116108
"namespace music {\n"
117109
" //% shim=music::queuePlayInstructions\n"
@@ -128,13 +120,10 @@ def audio_to_makecode_arcade(data, sample_rate, period) -> str:
128120

129121

130122
code = audio_to_makecode_arcade(data, sample_rate, spectrogram_period)
131-
132-
# Always overwrite output
133123
if args.output is not None:
134-
output_path = Path(args.output)
135-
output_path.parent.mkdir(parents=True, exist_ok=True)
136-
output_path.write_text(code)
124+
output_path = args.output.expanduser().resolve()
137125
if can_log:
138-
print(f"Written output to {output_path}")
126+
print(f"Writing to {output_path}")
127+
output_path.write_text(code)
139128
else:
140129
print(code)

0 commit comments

Comments
 (0)