summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/pcm2pdm.py74
-rw-r--r--tools/pdm2pcm.py50
-rw-r--r--tools/testsignal.py37
3 files changed, 161 insertions, 0 deletions
diff --git a/tools/pcm2pdm.py b/tools/pcm2pdm.py
new file mode 100644
index 0000000..7cf05fb
--- /dev/null
+++ b/tools/pcm2pdm.py
@@ -0,0 +1,74 @@
+import numpy as np
+import soundfile
+
+def second_order_delta_sigma(pcm):
+ # Normalize input XXX: remove normalization
+ pcm = pcm / np.max(np.abs(pcm))
+
+ n = len(pcm)
+ pdm = np.zeros(n, dtype=np.uint8)
+
+ integrator1 = 0.0
+ integrator2 = 0.0
+ quantized = 0.0
+
+ for i in range(n):
+ integrator1 += pcm[i] - quantized
+ integrator2 += integrator1 - quantized
+
+ if integrator2 >= 0:
+ quantized = 1.0
+ pdm[i] = 1
+ else:
+ quantized = -1.0
+ pdm[i] = 0
+
+ return pdm
+
+def save_pdm_bin(pdm_data, file):
+ packed = np.packbits(pdm_data)
+ file.write(packed)
+
+def convert(pcm_data, oversample = 64):
+ # Oversample
+ pcm_upsampled = np.repeat(pcm_data, oversample)
+
+ # --- Convert to PDM ---
+ pdm_data = second_order_delta_sigma(pcm_upsampled)
+
+ return pdm_data
+
+def convert_file(input_file, **kwargs):
+ # --- Load audio ---
+ pcm_data, samplerate = soundfile.read(input_file)
+
+ # If stereo, use one channel
+ if pcm_data.ndim > 1:
+ pcm_data = pcm_data[:, 0]
+
+ pdm_data = convert(pcm_data, **kwargs)
+ return pdm_data
+
+
+def parse():
+ import argparse
+
+ parser = argparse.ArgumentParser(description='Process an input file and write to an output file.')
+ parser.add_argument('-i', '--input', type=str, required=True, help='Path to the input file')
+ parser.add_argument('-o', '--output', type=str, required=True, help='Path to the output file')
+
+ args = parser.parse_args()
+ return args
+
+def main():
+ args = parse()
+
+ out_path = args.output
+
+ pdm_data = convert_file(args.input, oversample=64)
+ with open(out_path, 'wb') as f:
+ save_pdm_bin(pdm_data, f)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/pdm2pcm.py b/tools/pdm2pcm.py
new file mode 100644
index 0000000..6d2c284
--- /dev/null
+++ b/tools/pdm2pcm.py
@@ -0,0 +1,50 @@
+
+import numpy
+import numpy as np
+from scipy.signal import firwin, lfilter, decimate
+import soundfile
+
+def load_pdm_file(filename):
+ with open(filename, 'rb') as f:
+ byte_data = np.frombuffer(f.read(), dtype=np.uint8)
+ bit_data = np.unpackbits(byte_data)
+ # Convert to +1 (for 1s) and -1 (for 0s)
+ pdm_signal = 2 * bit_data - 1
+ return pdm_signal
+
+def pdm_to_pcm(pdm_signal, decimation_factor=64):
+ # Design a low-pass filter
+ fir_filter = firwin(numtaps=101, cutoff=0.5/decimation_factor)
+ # Filter the PDM signal
+ filtered = lfilter(fir_filter, [1.0], pdm_signal)
+ # Decimate (downsample)
+ pcm_signal = filtered[::decimation_factor]
+ return pcm_signal
+
+def main():
+
+ pdm_path = 'test_tone.pdm'
+ out_path = 'output.wav'
+ pdm_data = load_pdm_file(pdm_path)
+
+ # Convert to PCM
+ oversample = 64
+ samplerate = 16000
+ pcm_data = pdm_to_pcm(pdm_data, decimation_factor=oversample)
+
+ # Normalize and save to WAV
+ pcm_data = np.expand_dims(pcm_data, axis=1)
+ #print(pcm_data.shape)
+
+ print(numpy.min(pcm_data), numpy.max(pcm_data), numpy.mean(pcm_data))
+
+ # Normalize
+ pcm_data -= 128.0
+ pcm_data = 2**15 * (pcm_data / np.max(np.abs(pcm_data)))
+
+ print(numpy.min(pcm_data), numpy.max(pcm_data), numpy.mean(pcm_data))
+
+ soundfile.write(out_path, pcm_data, samplerate)
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/testsignal.py b/tools/testsignal.py
new file mode 100644
index 0000000..0f28e1c
--- /dev/null
+++ b/tools/testsignal.py
@@ -0,0 +1,37 @@
+import numpy as np
+import soundfile as sf
+
+def generate_test_tone(
+ duration_sec=5,
+ sample_rate=44100,
+ freqs=[440, 1000, 3000],
+ noise_level=0.05,
+ amplitude=0.5,
+):
+ t = np.linspace(0, duration_sec, int(sample_rate * duration_sec), endpoint=False)
+ signal = np.zeros_like(t)
+
+ # Add multiple sine waves
+ for freq in freqs:
+ signal += amplitude * np.sin(2 * np.pi * freq * t)
+
+ # Normalize to avoid clipping if multiple frequencies are added
+ signal /= len(freqs)
+
+ # Add white noise
+ noise = noise_level * np.random.normal(0, 1, signal.shape)
+ signal += noise
+
+ return signal
+
+if __name__ == "__main__":
+
+ filename="test_tone.wav"
+ # Save to WAV file
+
+ sample_rate = 16000
+
+ signal = generate_test_tone(sample_rate=sample_rate)
+
+ sf.write(filename, signal, sample_rate)
+ print(f"Saved test tone to '{filename}'.")