Skip to content

Instantly share code, notes, and snippets.

@Gumball2415
Last active December 1, 2023 03:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gumball2415/366fa863fd33cf4eb73507692b41d4d8 to your computer and use it in GitHub Desktop.
Save Gumball2415/366fa863fd33cf4eb73507692b41d4d8 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# apu_dac_to_lut.py
# for use with this ROM
# https://github.com/Gumball2415/nes-scribbles/tree/main/nrom-dac-test
#
# This Python script, along with the C headers it generates are licensed under the MIT-0 license.
# MIT No Attribution
#
# Copyright 2023 Persune
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify,
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
plot_plots = False
C_float_array = True
ROM_VERSION = [0, 0, 1]
# raw headerless float32 encoded capture data
with open("APU2_select.raw", mode="rb") as floatarray_file:
plt.title(floatarray_file.name)
floatarray_data = floatarray_file.read()
mainbuffer = np.frombuffer(floatarray_data, dtype=np.float32)
# pro tip: do not try and plot 2GB .wav file
# x = np.array(range(0, mainbuffer.size))
# plt.xlabel("Sample count")
# plt.ylabel("Voltage")
# plt.plot(x, mainbuffer, color = "red", linewidth=1)
# plt.show()
# plt.close()
TRI_size = 16
NOI_size = 16
DMC_size = 128
total_size = TRI_size * NOI_size * DMC_size
sample_clip = 1000
abs_values = np.empty((DMC_size, NOI_size, TRI_size), dtype=np.float32)
for DMC_level in range(DMC_size):
for NOI_level in range(NOI_size):
for TRI_level in range(TRI_size):
# select and average a level
if ROM_VERSION == [0, 0, 1]:
linear_index_level = (DMC_level * NOI_size * TRI_size) + (NOI_level * TRI_size) + ((TRI_size - 1) - TRI_level)
else:
linear_index_level = (DMC_level * NOI_size * TRI_size) + (NOI_level * TRI_size) + TRI_level
levelbuffer = mainbuffer[
((linear_index_level * int(np.round(mainbuffer.size / total_size)))):
(((linear_index_level + 1) * int(np.round(mainbuffer.size / total_size))))]
# lowpass level
b_filt, a_filt = signal.butter(1, 800, 'low', fs=1000000, analog=False)
levelbuffer = signal.filtfilt(b_filt, a_filt, levelbuffer)
# insert averaged level into table
abs_values[DMC_level, NOI_level, TRI_level] = np.average(
levelbuffer[sample_clip:(levelbuffer.size-sample_clip)])
# debug graphs
if plot_plots:
plt.title("TRI {0:02X}, NOI {1:02X}, DMC {2:02X}".format(TRI_level, NOI_level, DMC_level))
plt.plot(levelbuffer[sample_clip:(levelbuffer.size-sample_clip)])
plt.axhline(abs_values[DMC_level, NOI_level, TRI_level], color="red")
plt.tight_layout()
plt.show()
plt.close()
norm_values = abs_values.copy()
norm_values -= np.amin(norm_values)
norm_values /= (np.amax(norm_values) - np.amin(norm_values))
if True:
plt.step(np.arange(total_size), norm_values.reshape(total_size), c="blue")
plt.step(np.arange(total_size), abs_values.reshape(total_size), c="red")
# plt.step(np.arange(64), np.arange(64) / 64, c="gray")
plt.tight_layout()
plt.show()
plt.close()
print("absolute values:")
print(abs_values)
if C_float_array:
with open("APU2_LUT_abs.h", mode="wt") as APU2_LUT_abs_c_file:
APU2_LUT_abs_c_file.write("// [DMC_level][NOI_level][TRI_level]\n")
APU2_LUT_abs_c_file.write(f"const float APU2_LUT_abs[{DMC_size}][{NOI_size}][{TRI_size}]")
APU2_LUT_abs_c_file.write(" = {\n")
for DMC_level in range(DMC_size):
APU2_LUT_abs_c_file.write("\t{\n")
for NOI_level in range(NOI_size):
APU2_LUT_abs_c_file.write("\t\t{ ")
for TRI_level in range(TRI_size):
APU2_LUT_abs_c_file.write("%20s" % float(abs_values[DMC_level, NOI_level, TRI_level]).hex())
if TRI_level < (TRI_size - 1):
APU2_LUT_abs_c_file.write(", ")
if NOI_level == (NOI_size - 1):
APU2_LUT_abs_c_file.write(" }\n")
else:
APU2_LUT_abs_c_file.write(" },\n")
if DMC_level == (DMC_size - 1):
APU2_LUT_abs_c_file.write("\t}\n")
else:
APU2_LUT_abs_c_file.write("\t},\n")
APU2_LUT_abs_c_file.write("};\n")
with open("APU2_LUT_abs.bin", mode="wb") as APU1_LUT_abs_file:
APU1_LUT_abs_file.write(abs_values)
print("normalized values:")
print(norm_values)
if C_float_array:
with open("APU2_LUT_norm.h", mode="wt") as APU2_LUT_norm_c_file:
APU2_LUT_norm_c_file.write("// [DMC_level][NOI_level][TRI_level]\n")
APU2_LUT_norm_c_file.write(f"const float APU2_LUT_norm[{DMC_size}][{NOI_size}][{TRI_size}]")
APU2_LUT_norm_c_file.write(" = {\n")
for DMC_level in range(DMC_size):
APU2_LUT_norm_c_file.write("\t{\n")
for NOI_level in range(NOI_size):
APU2_LUT_norm_c_file.write("\t\t{ ")
for TRI_level in range(TRI_size):
APU2_LUT_norm_c_file.write("%20s" % float(norm_values[DMC_level, NOI_level, TRI_level]).hex())
if TRI_level < (TRI_size - 1):
APU2_LUT_norm_c_file.write(", ")
if NOI_level == (NOI_size - 1):
APU2_LUT_norm_c_file.write(" }\n")
else:
APU2_LUT_norm_c_file.write(" },\n")
if DMC_level == (DMC_size - 1):
APU2_LUT_norm_c_file.write("\t}\n")
else:
APU2_LUT_norm_c_file.write("\t},\n")
APU2_LUT_norm_c_file.write("};\n")
with open("APU2_LUT_norm.bin", mode="wb") as APU1_LUT_norm_file:
APU1_LUT_norm_file.write(norm_values)
# raw headerless float32 encoded capture data
with open("APU1_select.raw", mode="rb") as floatarray_file:
plt.title(floatarray_file.name)
floatarray_data = floatarray_file.read()
mainbuffer = np.frombuffer(floatarray_data, dtype=np.float32);
x = np.array(range(0, mainbuffer.size))
if plot_plots:
plt.xlabel("Sample count")
plt.ylabel("Voltage")
plt.plot(x, mainbuffer, color = "red", linewidth=1)
plt.tight_layout()
plt.show()
plt.close()
SQR1_size = 16
SQR2_size = 16
total_size = SQR1_size * SQR2_size
sample_clip = 1000
abs_values = np.empty((SQR1_size, SQR1_size), dtype=np.float32)
for SQR2_level in range(SQR2_size):
for SQR1_level in range(SQR1_size):
# select and average a level
linear_index_level = (SQR1_size * SQR2_level) + SQR1_level
levelbuffer = mainbuffer[
((linear_index_level * int(np.round(mainbuffer.size / total_size)))):
(((linear_index_level + 1) * int(np.round(mainbuffer.size / total_size))))]
# lowpass level
b_filt, a_filt = signal.butter(1, 800, 'low', fs=1000000, analog=False)
levelbuffer = signal.filtfilt(b_filt, a_filt, levelbuffer)
# insert averaged level into table
abs_values[SQR2_level, SQR1_level] = np.average(levelbuffer[sample_clip:(levelbuffer.size-sample_clip)])
# debug graphs
if plot_plots:
plt.title("SQR1 {0:0X}, SQR2 {1:0X}".format(SQR1_level, SQR2_level))
plt.plot(levelbuffer[sample_clip:(levelbuffer.size-sample_clip)])
plt.axhline(abs_values[SQR2_level, SQR1_level], color="red")
plt.tight_layout()
plt.show()
plt.close()
norm_values = abs_values.copy()
norm_values -= np.amin(norm_values)
norm_values /= (np.amax(norm_values) - np.amin(norm_values))
if True:
plt.step(np.arange(total_size), norm_values.reshape(total_size), c="blue")
plt.step(np.arange(total_size), abs_values.reshape(total_size), c="red")
# plt.step(np.arange(64), np.arange(64) / 64, c="gray")
plt.tight_layout()
plt.show()
plt.close()
print("absolute values:")
print(abs_values)
if C_float_array:
with open("APU1_LUT_abs.h", mode="wt") as APU1_LUT_abs_c_file:
APU1_LUT_abs_c_file.write("// [SQR2_level][SQR1_level]\n")
APU1_LUT_abs_c_file.write(f"const float APU1_LUT_abs[{SQR2_size}][{SQR1_size}]")
APU1_LUT_abs_c_file.write(" = {\n")
for SQR2_level in range(SQR2_size):
APU1_LUT_abs_c_file.write("\t{ ")
for SQR1_level in range(SQR1_size):
APU1_LUT_abs_c_file.write("%20s" % float(abs_values[SQR2_level, SQR1_level]))
if SQR1_level < (SQR1_size - 1):
APU1_LUT_abs_c_file.write(", ")
if SQR2_level == (SQR2_size - 1):
APU1_LUT_abs_c_file.write(" }\n")
else:
APU1_LUT_abs_c_file.write(" },\n")
APU1_LUT_abs_c_file.write("};\n")
with open("APU1_LUT_abs.bin", mode="wb") as APU1_LUT_abs_file:
APU1_LUT_abs_file.write(abs_values)
print("normalized values:")
print(norm_values)
if C_float_array:
with open("APU1_LUT_norm.h", mode="wt") as APU1_LUT_norm_c_file:
APU1_LUT_norm_c_file.write("// [SQR2_level][SQR1_level]\n")
APU1_LUT_norm_c_file.write(f"const float APU1_LUT_norm[{SQR2_size}][{SQR1_size}]")
APU1_LUT_norm_c_file.write(" = {\n")
for SQR2_level in range(SQR2_size):
APU1_LUT_norm_c_file.write("\t{ ")
for SQR1_level in range(SQR1_size):
APU1_LUT_norm_c_file.write("%20s" % float(norm_values[SQR2_level, SQR1_level]))
if SQR1_level < (SQR1_size - 1):
APU1_LUT_norm_c_file.write(", ")
if SQR2_level == (SQR2_size - 1):
APU1_LUT_norm_c_file.write(" }\n")
else:
APU1_LUT_norm_c_file.write(" },\n")
APU1_LUT_norm_c_file.write("};\n")
with open("APU1_LUT_norm.bin", mode="wb") as APU1_LUT_norm_file:
APU1_LUT_norm_file.write(norm_values)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment