Skip to content

Instantly share code, notes, and snippets.

@Quasimondo
Last active February 26, 2025 13:02
Show Gist options
  • Save Quasimondo/ad6e3bc3e0e29db42ee9f3bfcf057853 to your computer and use it in GitHub Desktop.
Save Quasimondo/ad6e3bc3e0e29db42ee9f3bfcf057853 to your computer and use it in GitHub Desktop.
Stream WAV Files as Bluetooth Mic Input from Raspberry Pi Zero W

Stream WAV Files as Bluetooth Mic Input from Raspberry Pi Zero W

Author: Mario Klingemann

Description

This guide configures a Raspberry Pi Zero W to stream WAV files (8kHz, mono PCM) to a phone via Bluetooth Hands-Free Profile (HFP), simulating a microphone for calls. Tested on Raspbian Bullseye with a Pixel 8 Pro and Pixel 6 Pro, it should work with similar setups.

Note: due to restrictions of Android it seems that headset microphones are not allowed to use certain features like voice assistant or voice typing on the keyboard. This seems to be an ongoing nuisance and I have not found a solution for it.

Note: this might not work on a Raspberry Zero 2 W, since it is using a different bluetooth chip which seems to have certain quirks.

Prerequisites

  • Hardware: Raspberry Pi Zero W (Cypress CYW43438 Bluetooth)
  • OS: Raspbian Bullseye (kernel 5.10+ recommended)
  • Software: bluez, pulseaudio, pulseaudio-module-bluetooth, alsa-utils, mpg123 (for MP3, optional), sox (for file conversion)
  • WAV File: 8kHz, mono PCM (S16LE or U8), non-empty (check with ls -l)
  • Phone: Bluetooth-capable (e.g., Android/iOS with HFP support)

Steps

1. Install Required Packages

Update and install Bluetooth, audio, and conversion tools:

sudo apt update
sudo apt install bluez pulseaudio pulseaudio-module-bluetooth alsa-utils mpg123 sox

2. Configure Bluetooth

Edit main.conf to enable HFP:

sudo nano /etc/bluetooth/main.conf

Set:

[General]
Name = playpi  # Your device name
Class = 0x200404
Enable = Source,Sink,Headset,Gateway,Control

[Policy]
AutoEnable = true

Save and restart Bluetooth:

sudo systemctl restart bluetooth

3. Configure PulseAudio for System Mode

Create a system service:

sudo nano /etc/systemd/system/pulseaudio.service

Add:

[Unit]
Description=PulseAudio system server
After=sound.target

[Service]
Type=simple
ExecStart=/usr/bin/pulseaudio --system --disallow-exit --disable-shm
Restart=on-failure

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable pulseaudio
sudo systemctl start pulseaudio

Edit system.pa:

sudo nano /etc/pulse/system.pa

Replace with:

### Core defaults
load-module module-device-restore
load-module module-stream-restore
load-module module-card-restore
.ifexists module-udev-detect.so
load-module module-udev-detect
.else
load-module module-detect
.endif
load-module module-native-protocol-unix
load-module module-default-device-restore
load-module module-always-sink
load-module module-suspend-on-idle
load-module module-position-event-sounds

### Virtual sink for WAV playback
load-module module-null-sink sink_name=VirtualMic sink_properties=device.description=VirtualMic

### Bluetooth support
load-module module-bluetooth-policy
load-module module-bluetooth-discover headset=auto

Restart:

sudo systemctl restart pulseaudio

4. Pair with Your Phone

Start bluetoothctl:

bluetoothctl

Run:

power on
discoverable on
pairable on
scan on

On your phone, find and pair with playpi. Note your phone's MAC address (e.g., run scan on to find it—example: AA:BB:CC:DD:EE:FF).

In bluetoothctl:

pair AA:BB:CC:DD:EE:FF
trust AA:BB:CC:DD:EE:FF
connect AA:BB:CC:DD:EE:FF
exit

On phone: Enable "Phone Calls" in Bluetooth settings for playpi.

5. Force HFP Profile

Check profile:

pactl list cards

If Active Profile: off:

pactl set-card-profile bluez_card.AA_BB_CC_DD_EE_FF headset_audio_gateway

Verify:

pactl list sinks

Look for bluez_sink.AA_BB_CC_DD_EE_FF.headset_audio_gateway.

6. Route Audio to HFP

Load loopback (after pairing):

pactl load-module module-loopback source=VirtualMic.monitor sink=bluez_sink.AA_BB_CC_DD_EE_FF.headset_audio_gateway

7. Convert Audio Files to 8kHz PCM

HFP requires 8kHz, mono PCM (S16LE or U8). Convert any file using sox:

Example (MP3 to WAV):

sox input.mp3 -t wav -r 8000 -c 1 -e signed-integer -b 16 output.wav
  • -r 8000: Sets 8kHz sample rate
  • -c 1: Mono
  • -e signed-integer -b 16: S16LE PCM (common for HFP)

Check:

file output.wav

Should show: WAV ... 8000 Hz, mono, 16 bit.

8. Test with a WAV File

Use a non-empty WAV (e.g., from Step 7):

ls -l output.wav  # Ensure size > 0 (e.g., ~16KB/sec)

Example (create test tone):

sox -n -t wav test.wav synth 5 sine 440 rate 8000 channels 1

Make a call:

  1. Call a second phone, ensuring playpi is the audio source (tap Bluetooth icon in call if needed)
  2. Play:
aplay -D pulse:VirtualMic test.wav
  1. Ask: "Do you hear a tone?" (Other party should hear it)

Troubleshooting

No Audio in Call:

Check profile:

pactl list cards

If off, repeat Step 5.

Verify playback:

pactl list sink-inputs

During aplay, ensure it targets VirtualMic.

Logs:

sudo journalctl -u pulseaudio

Profile Reverts:

Reconnect:

bluetoothctl
disconnect AA:BB:CC:DD:EE:FF
connect AA:BB:CC:DD:EE:FF

Silent WAV:

Test file content:

aplay test.wav  # Needs local audio output

Notes

  • MAC Address: Replace AA:BB:CC:DD:EE:FF with your phone's actual MAC
  • WAV Format: 8kHz, mono PCM (S16LE/U8) is critical—use sox to convert
  • No Local Sound: Normal—audio goes to the call, not the phone's speaker

Credit: Developed with Grok (xAI) on Feb 21, 2025—a rare solution for this use case!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment