🎯 Voice AI Consulting
Get dedicated support and consultation to ensure your specific needs are met.
Consult an AI Expert

Building reliable wake word detection in C presents unique challenges for developers working on voice-activated applications. Whether you're developing for Linux, Windows, macOS, or Raspberry Pi, your application needs to recognize custom wake words—like "Alexa," "Hey Siri," or your own branded keywords—with minimal latency and maximum reliability.

Many developers initially turn to cloud-based keyword spotting APIs, but these solutions introduce significant drawbacks. Sending audio streams to remote servers introduces network latency, raises privacy concerns with sensitive voice data, and can fail entirely when internet connectivity is unreliable or unavailable. For production voice applications, especially those in IoT devices, embedded systems, or privacy-sensitive environments, on-device wake word detection is essential.

This tutorial demonstrates how to implement cross-platform wake word detection in C using Porcupine Wake Word, a lightweight on-device keyword detection engine. You'll learn how to integrate real-time audio capture, process audio frames efficiently, and trigger custom callbacks when wake words are detected—all without requiring cloud connectivity.

By the end of this tutorial, you'll have a working C application that performs low-latency wake word detection across Linux, Windows, macOS, and Raspberry Pi.

Important: This tutorial builds on the steps in How to Record Audio in C. Follow that tutorial to set up audio capture first if you haven't done so already.

Prerequisites

  • C99-compatible compiler
  • Windows: MinGW

Supported Platforms

  • Linux (x86_64)
  • macOS (x86_64, arm64)
  • Windows (x86_64, arm64)
  • Raspberry Pi (Zero, 3, 4, 5)

Project Setup

The tutorial will use the following directory structure:

For instructions on setting up audio capture (with pvrecorder), see: How to Record Audio in C

Step 1: Add Porcupine library files

  1. Create a folder named porcupine/.
  2. Download the Porcupine header files from GitHub and place them in:
  1. Download the Porcupine model file for your desired language and place it in:

Dynamic Loading Infrastructure

Porcupine Wake Word ships as a shared library (.so, .dylib, .dll). Instead of linking at compile time, we'll load the library at runtime.

We'll build helpers to:

  1. open a shared library
  2. fetch function pointers
  3. close it gracefully

These helpers remain identical whether you're using PvRecorder, Cheetah Streaming Speech-to-Text, Porcupine Wake Word, Rhino Speech-to-Intent, or other Picovoice engines.

Step 2: Platform-specific headers

Explaining the headers

  • On Windows systems, windows.h provides the LoadLibrary function to load a shared library and GetProcAddress to retrieve individual function pointers.
  • On Unix-based systems, dlopen and dlsym from the dlfcn.h header provide the same functionality.
  • Lastly, signal.h allows us to handle Ctrl-C later in this example.

Step 3. Define dynamic loading helper functions

3a. Open the shared library

3b. Load function symbols

3c. Close the library

3d. Print platform-correct errors

Implement Wake Word Detection

Now that loading infrastructure is in place, it's time to initialize Porcupine Wake Word, start capturing audio, and pass frames into the engine.

Step 4: Load the Porcupine library

Downloaded the correct library file for your OS and point library_path to the file.

Step 5. Initialize Porcupine

  1. Sign up for an account on Picovoice Console for free and obtain your AccessKey
  2. Replace ${ACCESS_KEY} with your AccessKey
  3. Download a keyword file and point keyword_path to the file.

You can also train your own custom keyword (like "Hey Jarvis" or "Start Engine") instead of using one of the default keywords.

Call pv_porcupine_init to create a Porcupine instance:

Refer to pv_porcupine_init for detailed explanation of parameters.

Step 6: Start keyword detection

Porcupine expects int16 PCM frames of a specific size. Retrieve that size and start listening for the keyword:

pv_porcupine_process_func processes a frame of the incoming audio stream and emits the detection result.

Incoming audio needs to have pv_porcupine_frame_length samples per frame. If you are not using PvRecorder for audio streaming, ensure the sample rate is equal to pv_sample_rate, 16-bit linearly-encoded, and single-channel.

Step 7: Cleanup

When done, delete Porcupine to free memory and close the library:

Complete Example: On-device Wake Word Detection in C

Here is the complete porcupine_tutorial.c you can copy, build, and run, complete with proper error handling and PvRecorder implementation:

  • Replace ${ACCESS_KEY} with your AccessKey from Picovoice Console
  • update model_path to point to the Porcupine model file (.pv)
  • update library_path to point to the correct Porcupine library for your OS
  • update keyword_paths: to point to your chosen keyword file (.ppn)
  • update pv_recorder_library_path to point to the correct PvRecorder library for your OS

This is a simplified example but includes all the necessary components to get started. Check out the Porcupine C demo on GitHub for a complete demo application.

Build & Run

Build and run the application:

Linux (gcc) and Raspberry Pi (gcc)

macOS (clang)

Windows (MinGW)

Troubleshooting Common Issues

1. Wake Word Never Triggers

Verify that your audio is coming from the intended microphone. If you're using PvRecorder for audio capture, validate that it's working properly first.

Tips:

  • Confirm the microphone is not muted.
  • Make sure your application is reading audio frames at the exact size returned by pv_porcupine_frame_length.
  • Check that your sample rate and PCM format match the engine's requirements (pv_sample_rate, single-channel).

2. False Rejections or Missed Detections

If Porcupine rarely detects the keyword or fails in noisy environments, the sensitivity settings may be too low.

Solution:

Increase the sensitivity value (range 0.0–1.0) when initializing the engine. Higher sensitivity leads to more detections in noisy conditions but may slightly increase false alarms.

3. Too Many False Alarms

If wake word detection triggers too often or reacts to background speech, sensitivity may be set too high, or the keyword file may not be appropriate for the target environment.

Solution:

4. Porcupine Fails to Initialize

Initialization errors usually indicate mismatched files or platform issues. Common causes include using the wrong library, model, or keyword file for your system.

Solution:

  • Download the correct binaries for your OS and architecture from the Porcupine repository.
  • Match the keyword file (.ppn) and model file (.pv) to the same language.
  • Ensure the keyword file and shared library (.so, .dylib, .dll) are compatible with your target platform.

Example (English "bumblebee" keyword on Linux x86_64):

Start Building

Frequently Asked Questions

Can I run multiple wake words at once?
Yes. Porcupine supports multiple keywords simultaneously. Pass additional keyword file paths when initializing the engine and set a sensitivity value for each one.
What frame size should I read from the microphone?
Use the exact number returned by pv_porcupine_frame_length. Porcupine requires fixed-size frames, and incorrect sizes will stop detection from working.
Can I create my own custom wake word?
Absolutely. Use Picovoice Console to train personalized keywords such as "Hey Robot" or "Lights On" and then download the generated .ppn file.