diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/pcm2pdm.py | 74 | ||||
-rw-r--r-- | tools/pdm2pcm.py | 50 | ||||
-rw-r--r-- | tools/testsignal.py | 37 |
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}'.") |