-
Notifications
You must be signed in to change notification settings - Fork 42
/
fenster_audio.h
160 lines (154 loc) · 5.21 KB
/
fenster_audio.h
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#ifndef FENSTER_AUDIO_H
#define FENSTER_AUDIO_H
#ifndef FENSTER_SAMPLE_RATE
#define FENSTER_SAMPLE_RATE 44100
#endif
#ifndef FENSTER_AUDIO_BUFSZ
#define FENSTER_AUDIO_BUFSZ 8192
#endif
#if defined(__APPLE__)
#include <AudioToolbox/AudioQueue.h>
struct fenster_audio {
AudioQueueRef queue;
size_t pos;
float buf[FENSTER_AUDIO_BUFSZ];
dispatch_semaphore_t drained;
dispatch_semaphore_t full;
};
#elif defined(_WIN32)
#include <mmsystem.h>
#include <windows.h>
struct fenster_audio {
WAVEHDR header;
HWAVEOUT wo;
WAVEHDR hdr[2];
int16_t buf[2][FENSTER_AUDIO_BUFSZ];
};
#elif defined(__linux__)
struct fenster_audio {
void *pcm;
float buf[FENSTER_AUDIO_BUFSZ];
size_t pos;
};
#endif
#ifndef FENSTER_API
#define FENSTER_API extern
#endif
FENSTER_API int fenster_audio_open(struct fenster_audio *f);
FENSTER_API int fenster_audio_available(struct fenster_audio *f);
FENSTER_API void fenster_audio_write(struct fenster_audio *f, float *buf,
size_t n);
FENSTER_API void fenster_audio_close(struct fenster_audio *f);
#ifndef FENSTER_HEADER
#if defined(__APPLE__)
static void fenster_audio_cb(void *p, AudioQueueRef q, AudioQueueBufferRef b) {
struct fenster_audio *fa = (struct fenster_audio *)p;
dispatch_semaphore_wait(fa->full, DISPATCH_TIME_FOREVER);
memmove(b->mAudioData, fa->buf, sizeof(fa->buf));
dispatch_semaphore_signal(fa->drained);
AudioQueueEnqueueBuffer(q, b, 0, NULL);
}
FENSTER_API int fenster_audio_open(struct fenster_audio *fa) {
AudioStreamBasicDescription format = {0};
format.mSampleRate = FENSTER_SAMPLE_RATE;
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagIsPacked;
format.mBitsPerChannel = 32;
format.mFramesPerPacket = format.mChannelsPerFrame = 1;
format.mBytesPerPacket = format.mBytesPerFrame = 4;
fa->drained = dispatch_semaphore_create(1);
fa->full = dispatch_semaphore_create(0);
AudioQueueNewOutput(&format, fenster_audio_cb, fa, NULL, NULL, 0, &fa->queue);
for (int i = 0; i < 2; i++) {
AudioQueueBufferRef buffer = NULL;
AudioQueueAllocateBuffer(fa->queue, FENSTER_AUDIO_BUFSZ * 4, &buffer);
buffer->mAudioDataByteSize = FENSTER_AUDIO_BUFSZ * 4;
memset(buffer->mAudioData, 0, buffer->mAudioDataByteSize);
AudioQueueEnqueueBuffer(fa->queue, buffer, 0, NULL);
}
AudioQueueStart(fa->queue, NULL);
return 0;
}
FENSTER_API void fenster_audio_close(struct fenster_audio *fa) {
AudioQueueStop(fa->queue, false);
AudioQueueDispose(fa->queue, false);
}
FENSTER_API int fenster_audio_available(struct fenster_audio *fa) {
if (dispatch_semaphore_wait(fa->drained, DISPATCH_TIME_NOW))
return 0;
return FENSTER_AUDIO_BUFSZ - fa->pos;
}
FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf,
size_t n) {
while (fa->pos < FENSTER_AUDIO_BUFSZ && n > 0) {
fa->buf[fa->pos++] = *buf++, n--;
}
if (fa->pos >= FENSTER_AUDIO_BUFSZ) {
fa->pos = 0;
dispatch_semaphore_signal(fa->full);
}
}
#elif defined(_WIN32)
FENSTER_API int fenster_audio_open(struct fenster_audio *fa) {
WAVEFORMATEX wfx = {WAVE_FORMAT_PCM, 1, FENSTER_SAMPLE_RATE, FENSTER_SAMPLE_RATE * 2, 1, 16, 0};
waveOutOpen(&fa->wo, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
for (int i = 0; i < 2; i++) {
fa->hdr[i].lpData = fa->buf[i];
fa->hdr[i].dwBufferLength = FENSTER_AUDIO_BUFSZ * 2;
waveOutPrepareHeader(fa->wo, &fa->hdr[i], sizeof(WAVEHDR));
waveOutWrite(fa->wo, &fa->hdr[i], sizeof(WAVEHDR));
}
return 0;
}
FENSTER_API int fenster_audio_available(struct fenster_audio *fa) {
for (int i = 0; i < 2; i++)
if (fa->hdr[i].dwFlags & WHDR_DONE)
return FENSTER_AUDIO_BUFSZ;
return 0;
}
FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf,
size_t n) {
for (int i = 0; i < 2; i++) {
if (fa->hdr[i].dwFlags & WHDR_DONE) {
for (unsigned j = 0; j < n; j++) {
fa->buf[i][j] = (int16_t)(buf[j] * 32767);
}
waveOutWrite(fa->wo, &fa->hdr[i], sizeof(WAVEHDR));
return;
}
}
}
FENSTER_API void fenster_audio_close(struct fenster_audio *fa) {
waveOutClose(fa->wo);
}
#elif defined(__linux__)
int snd_pcm_open(void **, const char *, int, int);
int snd_pcm_set_params(void *, int, int, int, int, int, int);
int snd_pcm_avail(void *);
int snd_pcm_writei(void *, const void *, unsigned long);
int snd_pcm_recover(void *, int, int);
int snd_pcm_close(void *);
FENSTER_API int fenster_audio_open(struct fenster_audio *fa) {
if (snd_pcm_open(&fa->pcm, "default", 0, 0))
return -1;
int fmt = (*(unsigned char *)(&(uint16_t){1})) ? 14 : 15;
return snd_pcm_set_params(fa->pcm, fmt, 3, 1, FENSTER_SAMPLE_RATE, 1, 100000);
}
FENSTER_API int fenster_audio_available(struct fenster_audio *fa) {
int n = snd_pcm_avail(fa->pcm);
if (n < 0)
snd_pcm_recover(fa->pcm, n, 0);
return n;
}
FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf,
size_t n) {
int r = snd_pcm_writei(fa->pcm, buf, n);
if (r < 0)
snd_pcm_recover(fa->pcm, r, 0);
}
FENSTER_API void fenster_audio_close(struct fenster_audio *fa) {
snd_pcm_close(fa->pcm);
}
#endif
#endif /* FENSTER_HEADER */
#endif /* FENSTER_AUDIO_H */