Sound Reconstruction with a Laser and a Photosensor: From Theory to Code

By B.E. Alejandro


The Problem: Can a Beam of Light Carry Sound?

The idea came from a deceptively simple question in physics class: if sound is a mechanical wave that vibrates air, could that vibration modulate a beam of light and be recovered afterward?

The answer is yes, and the principle is elegant. Sound inside a soundproofed box makes a glass panel vibrate. A 650 nm laser points at the glass from outside; the reflection, modulated by the vibrations, is captured by a BPW34 photodiode. The resulting photocurrent passes through a transimpedance amplifier (LM358 TIA) that converts it to voltage, then through a MAX9814 with automatic gain control (AGC), and finally reaches the PC’s microphone jack, where MATLAB digitizes it and reconstructs the original audio:

$$V(t) = A \cdot \sin(2\pi f t) + \eta(t)$$

where $\eta(t)$ is the noise introduced by the optical-electronic system. That’s precisely the challenge: given that $\eta(t)$ always exists, how do we recover the signal $A \cdot \sin(2\pi f t)$ with the highest possible fidelity?

This post documents the full simulation phase in MATLAB that validates the algorithms before building the actual hardware.


Theoretical Foundations

Fourier Transform (FFT)

Fourier’s theorem establishes that any periodic signal can be decomposed as a sum of sines and cosines. The Discrete Fourier Transform (DFT) does this numerically:

$$X[k] = \sum_{n=0}^{N-1} x[n] \cdot e^{-j2\pi kn/N}$$

The FFT (Fast Fourier Transform) is an efficient algorithm for computing the DFT in $O(N \log N)$ instead of $O(N^2)$. For this project, the FFT serves two purposes:

  1. Identify which frequencies make up the captured signal
  2. Filter noise by removing weak spectral components

Singular Value Decomposition (SVD)

Since the project connects to the semester’s vector spaces topic, I also applied SVD to analyze the structure of each signal. Given a trajectory matrix $M$ built from the signal:

$$M = U \cdot \Sigma \cdot V^T$$

The singular values $\sigma_i$ in $\Sigma$ reveal how many “independent modes” the signal needs to be represented. A simple signal (pure tone) has very few large singular values; a complex signal has them spread out.

Connection to the course: SVD is essentially an orthogonal change of basis. The column vectors of $U$ are the new basis in the signal’s space, and the singular values are the “importance coordinates” of each basis vector. Exactly the same orthonormal basis concept we saw in class.


The Hardware

The physical system to be simulated and built is made up of the following components, chosen for their precision and low noise:

ComponentFunctionKey Parameter
D18×65mm 650nm 5mW LaserContinuous-point light sourceFocusable, mounted on a Neiko stand ~30°
BPW34 (PIN photodiode)Light → currentR = 0.3 A/W @ 650 nm, in a black tube
LM358 (TIA)Current → voltageRf = 10 kΩ, Cf = 100 pF
C_ac 10 µF capacitorDC offset blockingfc = 1.6 Hz
MAX9814 HiLetgoAGC amplifierAv = 40 dB (floating GAIN)
C_out 47 µF capacitorOutput offset blockingfc = 0.34 Hz
ShillehTek 3.5mm jack modulePC interfaceMicrophone input, TRRS breakout
ESP325V power supplyVIN pin → protoboard rail
Neiko standPosition the laser at an angle~30° adjustment over the glass

The main advantage of the BPW34 over an LDR is its response time: 20 ns versus ~1 ms, allowing it to capture audio up to 20 kHz with no degradation from the sensor.

Circuit Diagram

Full circuit diagram: BPW34 → LM358 TIA → MAX9814 → PC jack

Experimental Setup

Animated 3D simulation of the complete experimental setup

The 3D scene shows the full setup: a soundproofed box with a vibrating glass panel, an industrial D18×65 mm laser on a Neiko stand aimed at the glass at ~30°, the BPW34 in a black tube at the specular reflection angle, and the full circuit (ESP32 + LM358 + MAX9814 + jack module) on the protoboard. The animation syncs the glass vibration with the real-time signals at each stage of the pipeline.


Simulation Methodology

To validate the algorithms without hardware, I generated four types of signal in MATLAB and simulated the complete process through the real optical-electronic pipeline: generation → optical modulation → BPW34 → TIA → MAX9814 → FFT filtering → reconstruction → validation.

Unlike simply adding Gaussian noise, the model simulates the entire physical chain:

function v_out = pipeline_sensor(audio, P_laser, mod_depth, ...
                                 R_bpw34, Rf_TIA, Av_MAX, ruido)
    % 1. Optical modulation: the vibrating glass modulates the reflection
    P_luz  = P_laser * (1 + mod_depth * audio);

    % 2. BPW34: photocurrent = responsivity x power + shot noise
    I_foto = R_bpw34 * P_luz + ruido * 1e-4 * randn(size(audio));

    % 3. LM358 TIA: I → V, remove DC offset (like the C_ac capacitor)
    V_tia  = I_foto * Rf_TIA;
    V_tia  = V_tia - mean(V_tia);

    % 4. MAX9814: amplify and clip (±1.65V)
    V_max  = Av_MAX * V_tia;
    v_out  = max(-1.65, min(1.65, V_max));
end

The BPW34’s noise (shot noise) is just 0.8% of the signal — notably lower than the ~5% typical of an LDR, which explains the higher SNR results.

The Four Test Signals

SignalFrequenciesWhy it’s interesting
Pure tone1000 HzBaseline case, simplest possible signal
C Major chord262 + 330 + 392 HzThree simultaneous frequencies (C, E, G)
Sweep (chirp)100 → 1000 HzTime-varying frequency
Complex signal262·k Hz, k=1..5Harmonic series, simulates a human voice

The Reconstruction Algorithm

The core of the system is spectral filtering: a binary mask is applied to the FFT spectrum that keeps only the components above 10% of the peak magnitude, and the signal is then reconstructed with the IFFT. The key is preserving the spectrum’s Hermitian symmetry so the IFFT returns real values:

function senial_rec = reconstruir_por_fft(senial_ruidosa)
    N        = length(senial_ruidosa);
    Y        = fft(senial_ruidosa);

    % Magnitude of the one-sided spectrum (DC up to and including Nyquist)
    mag_pos  = abs(Y(1 : floor(N/2) + 1));
    umbral   = max(mag_pos) * 0.1;

    % Binary mask: 1 where the signal exceeds the threshold
    mascara_pos = double(mag_pos > umbral);

    % Full mask with Hermitian symmetry for a real-valued signal
    mascara = zeros(N, 1);
    mascara(1 : floor(N/2) + 1)   = mascara_pos;
    mascara(floor(N/2)+2 : N)     = flipud(mascara_pos(2 : floor(N/2)));

    senial_rec = real(ifft(Y .* mascara));
end

A mistake I made: in earlier versions I used N/2 directly for indexing, which broke whenever N was odd. The correct fix is floor(N/2) in every index, and building the conjugate symmetry explicitly. This guarantees the mask has exactly N elements regardless of the parity of the signal length.


Results

Full Plots

The following figure shows the complete pipeline for all four signals: ideal signal → signal captured by the sensor (BPW34→MAX9814) → reconstructed signal → FFT spectrum with the resulting SNR.

Full pipeline for the 4 test signals: ideal, captured, reconstructed, and FFT spectrum

The most striking visual result: the “Reconstructed” column (green) recovers the waveform with a fidelity that makes the result almost indistinguishable from the original, despite the visible noise in the “Captured” column.

Quantitative Metrics

To avoid relying solely on visual inspection, I computed three error metrics for each signal. The reconstructed signal is scaled before comparison (least-squares inner product) to separate phase/shape error from amplitude error:

SignalRMS ErrorSNR (dB)CorrelationResult
Pure tone2.79 × 10⁻⁶102.1 dB1.0000Excellent
Chord2.87 × 10⁻⁴62.1 dB1.0000Excellent
Sweep1.06 × 10⁻²30.5 dB0.9996Very good
Complex3.00 × 10⁻⁴63.1 dB1.0000Excellent

Reference: in audio, an SNR above 20 dB is already considered acceptable for voice playback. Above 40 dB is transparent to the human ear under most conditions. This system’s results range from 30.5 to 102.1 dB.

SVD Analysis

SVD decomposition of the trajectory matrix for the 4 signals

The singular value analysis confirms the theoretical intuition about the complexity of each signal:

SignalCumulative variance in 5 modes
Pure tone99.0%nearly all the energy in 1–2 modes
Chord84.3%~3 significant modes (one per frequency)
Sweep9.3%energy very spread out
Complex88.6%several modes (harmonics)

The sweep case is the most revealing: since the frequency changes continuously from 100 to 1000 Hz over 3 seconds, no fixed mode can capture the signal well. It needs hundreds of singular vectors to be represented faithfully. This mathematically explains why it has the lowest SNR: a fixed-threshold FFT filter isn’t the best tool for time-varying signals.


The Full Code

The complete script is available in the project’s repository:

Source code: script_prueba_demostracion.m — MATLAB R2016b or later, requires the Signal Processing Toolbox.


Conclusions

The simulation results are clear:

  1. FFT spectral filtering works. SNR of up to 102.1 dB for the pure tone and a correlation of 1.0000 for three of the four signals. The system recovers the waveform with enough fidelity for audio playback.

  2. The hardest signal is the sweep (chirp). At 30.5 dB and a correlation of 0.9996, it’s still very good, but it exposes the limitation of the fixed-threshold filter: it’s not the best choice for signals whose frequency varies over time. The STFT (Short-Time Fourier Transform) or a Wavelet transform would be better tools for that case.

  3. SVD confirms vector-space theory. The effective rank of the signal (as measured by the singular values) directly reflects its complexity: 1–2 modes for the pure tone, hundreds for the sweep.

  4. The BPW34 outperforms the LDR. The BPW34’s shot noise is just 0.8% of the signal, versus the ~5% typical of an LDR. This translates directly into a higher SNR and more faithful reconstruction.

  5. The simulation validates the architecture before the hardware. I now know the algorithms work. I can build the real system with confidence.


Next Steps

The hardware is defined and on its way:

The next post will document the hardware build and the first real audio capture.


This project is part of the Physics II course (Waves, Oscillations, and Vector Spaces), second semester. All the code is freely available on GitHub.