Data Modulation
Audio Transmission
Demodulation
Source Code
Signal Processing

Data to audio and back. Modulation / demodulation with source code

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Converting digital data into audio and then recovering it again is a classic modulation and demodulation problem. The idea is to map bits onto an audio waveform that can travel through speakers, microphones, or recorded files. A simple frequency-shift keying design is enough to demonstrate the full round trip in code.

Pick a Simple Modulation Scheme

For an introductory implementation, binary frequency-shift keying is practical:

  • one frequency represents bit 0
  • another frequency represents bit 1

Example mapping:

  • '0 uses 1200 hertz'
  • '1 uses 2200 hertz'

Each bit is sent for a fixed duration. The receiver analyzes each chunk and decides which frequency is stronger.

Convert Bits into Audio Samples

The transmitter generates a sine wave for each bit interval.

python
1import numpy as np
2
3SAMPLE_RATE = 8000
4BIT_DURATION = 0.05
5FREQ_ZERO = 1200
6FREQ_ONE = 2200
7
8
9def bits_from_text(text: str) -> str:
10    return "".join(f"{ord(ch):08b}" for ch in text)
11
12
13def modulate(bits: str) -> np.ndarray:
14    t = np.linspace(0, BIT_DURATION, int(SAMPLE_RATE * BIT_DURATION), endpoint=False)
15    chunks = []
16
17    for bit in bits:
18        freq = FREQ_ONE if bit == "1" else FREQ_ZERO
19        wave = 0.6 * np.sin(2 * np.pi * freq * t)
20        chunks.append(wave)
21
22    return np.concatenate(chunks)
23
24
25bits = bits_from_text("Hi")
26signal = modulate(bits)
27print(signal[:10])

This produces a NumPy array of audio samples that can be saved or played.

Save the Audio to a WAV File

Using a WAV container makes inspection and playback easier.

python
1from scipy.io.wavfile import write
2
3
4def save_wav(path: str, samples: np.ndarray) -> None:
5    pcm = np.int16(samples * 32767)
6    write(path, SAMPLE_RATE, pcm)
7
8
9save_wav("message.wav", signal)

At this point, message.wav contains the modulated bitstream.

Demodulate by Measuring Energy

The receiver splits the signal into fixed-size bit windows and measures how strongly each target frequency is present.

python
1def demodulate(samples: np.ndarray) -> str:
2    chunk_size = int(SAMPLE_RATE * BIT_DURATION)
3    bits = []
4
5    t = np.linspace(0, BIT_DURATION, chunk_size, endpoint=False)
6    ref_zero = np.sin(2 * np.pi * FREQ_ZERO * t)
7    ref_one = np.sin(2 * np.pi * FREQ_ONE * t)
8
9    for start in range(0, len(samples), chunk_size):
10        chunk = samples[start:start + chunk_size]
11        if len(chunk) != chunk_size:
12            break
13
14        score_zero = abs(np.dot(chunk, ref_zero))
15        score_one = abs(np.dot(chunk, ref_one))
16        bits.append("1" if score_one > score_zero else "0")
17
18    return "".join(bits)

This is a simple coherent detector. It is not industrial-grade, but it is enough to show the principle.

Convert Recovered Bits Back to Text

Once you have the bitstream, convert it back into bytes.

python
1def text_from_bits(bits: str) -> str:
2    chars = []
3    for i in range(0, len(bits), 8):
4        byte = bits[i:i + 8]
5        if len(byte) == 8:
6            chars.append(chr(int(byte, 2)))
7    return "".join(chars)
8
9
10recovered_bits = demodulate(signal)
11print(recovered_bits)
12print(text_from_bits(recovered_bits))

For a clean generated signal, the recovered text should match the original input.

Add Framing and Error Detection

Real systems need more than raw bits. Without framing, the receiver does not know where the message begins. Without error detection, noise can silently corrupt content.

Practical additions include:

  • a preamble tone or bit pattern
  • a length field
  • a checksum or CRC

Those features are what separate a toy modem from a usable transport.

Real-World Constraints

Audio modulation becomes harder once you leave the ideal all-digital simulation. Real microphones and speakers introduce:

  • noise
  • clipping
  • timing drift
  • frequency response limits

That means a robust modem must handle synchronization, filtering, and threshold tuning. Simple frequency detection still teaches the fundamentals, but real deployments need more engineering.

Common Pitfalls

  • Choosing bit durations too short for reliable frequency detection.
  • Forgetting framing, so the receiver cannot align message boundaries.
  • Using frequencies too close together for the sample rate and channel quality.
  • Assuming a clean generated WAV behaves like a real acoustic path.
  • Ignoring error detection when turning noisy analog signals back into bytes.

Summary

  • Audio data transfer works by mapping bits onto sound wave properties.
  • Binary frequency-shift keying is a simple modulation scheme for demonstrations.
  • Modulation generates waveform chunks, one per bit interval.
  • Demodulation compares energy at expected frequencies for each chunk.
  • Real systems need framing, timing control, and error detection beyond the basic example.

Course illustration
Course illustration

All Rights Reserved.