如何使用C++/C程序播放WAV文件?希望沿用sndfile.h库
Playing WAV Files with C/C++ Using libsndfile + PortAudio
Great question! Since you're already using libsndfile to read WAV files, pairing it with PortAudio (a cross-platform audio I/O library) is a perfect fit—it lets you reuse your existing file-reading logic while handling the low-level audio playback details across Windows, macOS, and Linux.
Step 1: Set Up Dependencies
You already have libsndfile, so just install PortAudio to handle playback:
- On Linux: Use your package manager (e.g.,
sudo apt-get install portaudio19-dev) - On macOS: Use Homebrew (
brew install portaudio) - On Windows: Download precompiled binaries or build from source
Step 2: Full Example Code
This code reads a WAV file with libsndfile, then uses PortAudio to play it. I’ve added comments to walk you through each part:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sndfile.h> #include <portaudio.h> // Struct to pass audio file data and buffer to the PortAudio callback typedef struct { SNDFILE* sndFile; float* buffer; sf_count_t bufferFrameCount; } AudioPlaybackData; // PortAudio callback: Called when the stream needs more audio data static int audioCallback(const void* inputBuffer, void* outputBuffer, unsigned long framesRequested, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData) { AudioPlaybackData* data = (AudioPlaybackData*)userData; float* out = (float*)outputBuffer; sf_count_t framesRead; // Read frames from the WAV file into our buffer framesRead = sf_readf_float(data->sndFile, data->buffer, framesRequested); // If we hit the end of the file, fill remaining frames with silence if (framesRead < framesRequested) { sf_count_t silenceFrames = framesRequested - framesRead; int channelCount = sf_count_to_int(sf_channels(data->sndFile)); for (sf_count_t i = 0; i < silenceFrames; i++) { for (int ch = 0; ch < channelCount; ch++) { *out++ = 0.0f; } } return paComplete; // Signal playback is finished } // Copy buffer data to the output stream int channelCount = sf_count_to_int(sf_channels(data->sndFile)); for (sf_count_t i = 0; i < framesRead; i++) { for (int ch = 0; ch < channelCount; ch++) { *out++ = data->buffer[i * channelCount + ch]; } } return paContinue; // Keep requesting more data } int main(int argc, char* argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <path-to-wav-file>\n", argv[0]); return 1; } SNDFILE* sndFile = NULL; SF_INFO sfInfo; PaStream* stream = NULL; AudioPlaybackData playbackData; PaError paErr; // Initialize libsndfile and open the WAV file memset(&sfInfo, 0, sizeof(SF_INFO)); sndFile = sf_open(argv[1], SFM_READ, &sfInfo); if (!sndFile) { fprintf(stderr, "Error opening WAV: %s\n", sf_strerror(NULL)); return 1; } // Validate file format (supports 16-bit PCM and float WAVs) if (!(sfInfo.format & SF_FORMAT_WAV) || !(sfInfo.format & (SF_FORMAT_PCM_16 | SF_FORMAT_FLOAT))) { fprintf(stderr, "Unsupported WAV format. Use 16-bit PCM or float.\n"); sf_close(sndFile); return 1; } // Initialize PortAudio paErr = Pa_Initialize(); if (paErr != paNoError) { fprintf(stderr, "PortAudio init failed: %s\n", Pa_GetErrorText(paErr)); sf_close(sndFile); return 1; } // Set up playback data struct playbackData.sndFile = sndFile; playbackData.bufferFrameCount = 512; // Adjust for latency (smaller = lower latency, more CPU) playbackData.buffer = (float*)malloc(playbackData.bufferFrameCount * sfInfo.channels * sizeof(float)); if (!playbackData.buffer) { fprintf(stderr, "Failed to allocate audio buffer.\n"); Pa_Terminate(); sf_close(sndFile); return 1; } // Configure PortAudio output parameters PaStreamParameters outputParams; outputParams.device = Pa_GetDefaultOutputDevice(); if (outputParams.device == paNoDevice) { fprintf(stderr, "No default audio output device found.\n"); free(playbackData.buffer); Pa_Terminate(); sf_close(sndFile); return 1; } outputParams.channelCount = sfInfo.channels; outputParams.sampleFormat = paFloat32; // libsndfile converts to float for us outputParams.suggestedLatency = Pa_GetDeviceInfo(outputParams.device)->defaultLowOutputLatency; outputParams.hostApiSpecificStreamInfo = NULL; // Open the audio stream paErr = Pa_OpenStream(&stream, NULL, // No input needed &outputParams, sfInfo.samplerate, playbackData.bufferFrameCount, paNoFlag, audioCallback, &playbackData); if (paErr != paNoError) { fprintf(stderr, "Failed to open stream: %s\n", Pa_GetErrorText(paErr)); free(playbackData.buffer); Pa_Terminate(); sf_close(sndFile); return 1; } // Start playback paErr = Pa_StartStream(stream); if (paErr != paNoError) { fprintf(stderr, "Failed to start stream: %s\n", Pa_GetErrorText(paErr)); Pa_CloseStream(stream); free(playbackData.buffer); Pa_Terminate(); sf_close(sndFile); return 1; } // Wait for playback to finish (or user presses Enter) printf("Playing... Press Enter to stop early.\n"); getchar(); // Clean up resources paErr = Pa_StopStream(stream); if (paErr != paNoError) fprintf(stderr, "Stop stream error: %s\n", Pa_GetErrorText(paErr)); Pa_CloseStream(stream); free(playbackData.buffer); Pa_Terminate(); sf_close(sndFile); printf("Playback complete.\n"); return 0; }
Step 3: Compile the Code
Use these commands to compile (adjust for your platform):
- Linux/macOS:
gcc play_wav.c -o play_wav -lsndfile -lportaudio - Windows (MinGW):
gcc play_wav.c -o play_wav.exe -lsndfile -lportaudio - Windows (MSVC): Link against PortAudio and libsndfile via your Visual Studio project settings.
Key Notes
- Buffer Size: 512 frames is a safe default. If you hear glitches, increase it (e.g., 1024). Smaller buffers reduce latency but use more CPU.
- Format Flexibility:
libsndfileautomatically converts 16-bit PCM WAVs to float when usingsf_readf_float, so this code works with most common WAV files. - Error Handling: Always check return values from
libsndfileand PortAudio functions—their error messages will save you tons of debugging time.
内容的提问来源于stack exchange,提问作者user3147192
相关产品推荐
相关产品推荐

