Created
May 17, 2026 08:35
-
-
Save peczenyj/4a8991600deb62dfe2b1e93324d1c8bd to your computer and use it in GitHub Desktop.
Wav player
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // minimal_wav_player.c | |
| // A minimalistic mono WAV player for Linux using ALSA | |
| // Compile: gcc -o play play.c -lasound | |
| // Usage: ./play file.wav | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <alsa/asoundlib.h> | |
| // WAV header (44 bytes for PCM) | |
| typedef struct { | |
| char riff[4]; // "RIFF" | |
| uint32_t file_size; // total size - 8 | |
| char wave[4]; // "WAVE" | |
| char fmt_id[4]; // "fmt " | |
| uint32_t fmt_size; // 16 for PCM | |
| uint16_t format; // 1 = PCM | |
| uint16_t channels; // 1 = mono | |
| uint32_t sample_rate; // e.g. 44100 | |
| uint32_t byte_rate; // sample_rate * channels * bits/8 | |
| uint16_t block_align; // channels * bits/8 | |
| uint16_t bits_per_sample;// 16 | |
| char data_id[4]; // "data" | |
| uint32_t data_size; // size of audio data | |
| } WavHeader; | |
| int main(int argc, char *argv[]) { | |
| if (argc != 2) { | |
| fprintf(stderr, "Usage: %s file.wav\n", argv[0]); | |
| return 1; | |
| } | |
| // 1. Open WAV file and read header | |
| FILE *fp = fopen(argv[1], "rb"); | |
| if (!fp) { | |
| perror("fopen"); | |
| return 1; | |
| } | |
| WavHeader hdr; | |
| fread(&hdr, sizeof(hdr), 1, fp); | |
| // Sanity checks | |
| if (memcmp(hdr.riff, "RIFF", 4) != 0 || | |
| memcmp(hdr.wave, "WAVE", 4) != 0 || | |
| hdr.format != 1) { | |
| fprintf(stderr, "Not a PCM WAV file\n"); | |
| return 1; | |
| } | |
| printf("WAV: %d Hz, %d-bit, %d ch, %d bytes of audio\n", | |
| hdr.sample_rate, hdr.bits_per_sample, | |
| hdr.channels, hdr.data_size); | |
| // 2. Open ALSA device and configure it | |
| snd_pcm_t *pcm; | |
| int err = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0); | |
| if (err < 0) { | |
| fprintf(stderr, "snd_pcm_open: %s\n", snd_strerror(err)); | |
| return 1; | |
| } | |
| snd_pcm_set_params(pcm, | |
| SND_PCM_FORMAT_S16_LE, // 16-bit signed little-endian | |
| SND_PCM_ACCESS_RW_INTERLEAVED, // interleaved read/write | |
| hdr.channels, // channels from header | |
| hdr.sample_rate, // sample rate from header | |
| 1, // allow soft resampling | |
| 100000); // latency: 100ms | |
| // 3. Read samples and write to ALSA (the blocking I/O loop) | |
| int frame_size = hdr.channels * (hdr.bits_per_sample / 8); | |
| int buf_frames = 1024; | |
| char *buf = malloc(buf_frames * frame_size); | |
| int frames_read; | |
| while ((frames_read = fread(buf, frame_size, buf_frames, fp)) > 0) { | |
| // This write blocks when the ALSA buffer is full. | |
| // The DAC crystal drains it at sample_rate Hz. | |
| snd_pcm_writei(pcm, buf, frames_read); | |
| } | |
| // 4. Wait for remaining buffered audio to finish playing | |
| snd_pcm_drain(pcm); | |
| // 5. Cleanup | |
| free(buf); | |
| snd_pcm_close(pcm); | |
| fclose(fp); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment