Register
handmade.network » Wiki » Audio API Examples

PulseAudio

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Build with: gcc sound.c -o sound -lm -lpulse -lpulse-simple

#include <stdlib.h>
#include <pulse/simple.h>

#define SAMPLE_RATE  (44100)
#define CHANNELS     (2)

int main(int argc, char **argv) {
    static const pa_sample_spec streamFormat = {
        .format = PA_SAMPLE_S16LE,
        .rate = SAMPLE_RATE,
        .channels = CHANNELS,
    };

    pa_simple *device = pa_simple_new(NULL, NULL, PA_STREAM_PLAYBACK, NULL, 
            "playback", &streamFormat, NULL, NULL, NULL);

    while (1) {
        uint16_t buffer[1024];

        for (int i = 0; i < 512; i++) {
            buffer[i * 2 + 0] = rand();
            buffer[i * 2 + 1] = rand();
        }

        pa_simple_write(device, buffer, 1024, NULL);
    }

    pa_simple_drain(device, NULL);

    return 0;
}

ALSA

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#if 0
gcc $0 -o ${0%%.*} -lasound -lm
exit
#endif
#include <math.h>
#include <stdint.h>
#include <alsa/asoundlib.h>

#define SAMPLE_RATE  (48000)
#define CHANNELS     (2)
#define BUFFER_SIZE  (1000)

int main(void) {
    snd_pcm_t* pcm;
    snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0);
    snd_pcm_set_params(pcm,
                       SND_PCM_FORMAT_S16_LE,
                       SND_PCM_ACCESS_RW_INTERLEAVED,
                       CHANNELS,
                       SAMPLE_RATE,
                       1,
                       16667);

    int16_t buffer[BUFFER_SIZE];

    const float nfreq = (M_PI * 2.0) / (float) SAMPLE_RATE;
    const float vol   = (1 << 15) * 0.5;
    const float note  = 440.0;
    float ctr = 0.0;

    for(;;) {
        for(int i = 0; i < BUFFER_SIZE; i += 2) {
            buffer[i+0] = buffer[i+1] = vol * sinf(note * nfreq * ctr++);
        }
        snd_pcm_writei(pcm, buffer, BUFFER_SIZE / CHANNELS);
    }
}

SDL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
SDL_Init(... | SDL_INIT_AUDIO | ...);

int audioDevicesLength = SDL_GetNumAudioDevices(0);

SDL_AudioSpec desiredAudioFormat = {}, obtainedAudioFormat;
desiredAudioFormat.freq = 44100;
desiredAudioFormat.format = AUDIO_F32;
desiredAudioFormat.channels = 2;
desiredAudioFormat.samples = 4096;
desiredAudioFormat.callback = [] (void *userData, uint8_t *stream, int length) {
    for (int i = 0; i < length; i++) {
        stream[i] = rand();
    }
};

bool foundAudioDevice = false;

for (int i = 0; i < audioDevicesLength; i++) {
    SDL_AudioDeviceID audioDevice = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(i, 0), 0, 
            &desiredAudioFormat, &obtainedAudioFormat, 0);
    if (!audioDevice) continue;
    SDL_PauseAudioDevice(audioDevice, 0);
    break;
}

Win32 MIDI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <windows.h>
#include <stdint.h>

#pragma comment (lib, "winmm.lib")

/////////////////////////////////////////////////

#pragma pack(push, 1)

struct MIDIHeader {
    DWORD signature, size;
    WORD format, tracks, ticksPerQuarter;
};

struct MIDITrack {
    DWORD signature, length;
};

#pragma pack(pop)

struct MIDIPlayback {
    uint8_t *position, previousCommand;
    DWORD time;
};

DWORD MIDIReadVariable(uint8_t *&position) {
    DWORD value = 0;

    while (true) {
        uint32_t c = *position++;
        value = (value << 7) | (c & 0x7F);
        if (~c & 0x80) break;
    }

    return value;
}

DWORD MIDISwapEndian32(DWORD in) {
    return    ((in & 0xFF000000) >> 24) 
        | ((in & 0x00FF0000) >> 8)
        | ((in & 0x0000FF00) << 8)
        | ((in & 0x000000FF) << 24);
}

WORD MIDISwapEndian16(WORD in) {
    return    ((in & 0xFF00) >> 8)
        | ((in & 0x00FF) << 8);
}

void MIDISleep(DWORD time) {
    if (!time) return;

    uint64_t start, end, frequency;
    QueryPerformanceCounter((LARGE_INTEGER *) &start);
    QueryPerformanceFrequency((LARGE_INTEGER *) &frequency);

    while (true) {
        QueryPerformanceCounter((LARGE_INTEGER *) &end);
        if ((end - start) * 1000000 / frequency >= time) break;
    }
}

void MIDIPlay(HMIDIOUT midiOut, uint8_t *position) {
    MIDIHeader *header = (MIDIHeader *) position;
    position += sizeof(MIDIHeader);

    BYTE trackCount = MIDISwapEndian16(header->tracks);
    MIDIPlayback tracks[16] = {};

    for (int i = 0; i < trackCount; i++) {
        MIDITrack *track = (MIDITrack *) position;
        position += sizeof(MIDITrack);
        tracks[i].position = position;
        position += MIDISwapEndian32(track->length);
    }

    DWORD time = 0, clock = 500000;

    while (trackCount) {
        int next = -1;
        DWORD nextAbsolute = INT_MAX;

        for (int i = 0; i < trackCount; i++) {
            uint8_t *position = tracks[i].position;
            DWORD absolute = MIDIReadVariable(position) + tracks[i].time;

            if (absolute < nextAbsolute) {
                next = i;
                nextAbsolute = absolute;
            }
        }

        MIDIPlayback *track = tracks + next;

        DWORD delta = MIDIReadVariable(track->position);
        MIDISleep(clock / MIDISwapEndian16(header->ticksPerQuarter) * (delta + track->time - time));
        time = (track->time += delta);

        DWORD command = *track->position++;

        if (~command & 0x80) {
            command = track->previousCommand;
            track->position--;
        } else {
            track->previousCommand = command;
        }

        if ((command & 0xF0) != 0xF0) {
            DWORD data1 = *track->position++, data2 = 0;

            if ((command & 0xF0) != 0xC0 && (command & 0xF0) != 0xD0) {
                data2 = *track->position++;
            }

            midiOutShortMsg(midiOut, command | (data1 << 8) | (data2 << 16));
        } else if (command == 0xFF) {
            command = *track->position++;

            if (command == 0x2F) {
                trackCount--;
                tracks[next] = tracks[trackCount];
            } else if (command == 0x51) {
                BYTE length = *track->position++;
                DWORD b1 = *track->position++, b2 = *track->position++, b3 = *track->position++;
                clock = (b1 << 16) | (b2 << 8) | b3;
            } else {
                BYTE length = *track->position++;
                track->position += length;
            }
        }
    }
}

int main(int argc, char **argv) {   
    HMIDIOUT out;

    if (MMSYSERR_NOERROR == midiOutOpen(&out, 0, 0, 0, CALLBACK_NULL)) {
        MIDIPlay(out, buffer /*the data of your MIDI file*/);
    }
}

DirectSound

See the DirectSound article.