This repository has been archived by the owner on Jul 7, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaudio.h
272 lines (241 loc) · 10.4 KB
/
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// audio - v4 - simple audio playback library - public domain - by Patrick Gaskin
#ifndef AUDIO_H
#define AUDIO_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifndef AUDIO_NO_AUTODETECT
#ifdef TINYALSA_PCM_H
#define AUDIO_SUPPORT_ALSA
#endif
#ifdef foosimplehfoo
#define AUDIO_SUPPORT_PULSE
#endif
#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
#define AUDIO_SUPPORT_VORBIS
#endif
#ifdef dr_flac_h
#define AUDIO_SUPPORT_FLAC
#endif
#ifdef dr_wav_h
#define AUDIO_SUPPORT_WAV
#endif
#ifdef dr_mp3_h
#define AUDIO_SUPPORT_MP3
#endif
#endif
// audio_output_t implements an output for audio_play.
typedef struct audio_output_t {
// open opens the audio device with the provided configuration. On
// error, NULL should be returned and errno set. An error should be returned
// if the provided channels and rate cannot be met. The returned object
// should be ready to use as-is.
void* (*open)(void* cfg, int channels, int rate);
// close closes the audio device and stops playback.
void (*close)(void* obj);
// stop stops playback immediately.
void (*stop)(void* obj);
// write_frames_s16le writes audio to the device. On error, it should
// return a negative number. Any other number is considered success. buf_sz
// is equal to frame_count*channels*sizeof(buf[0]).
int (*write_frames_s16le)(void* obj, int16_t *buf, size_t buf_sz, int frame_count);
} audio_output_t;
// audio_format_t implements a format for audio_play.
typedef struct audio_format_t {
// open opens a filename and returns the number of channels, sample
// rate, and a pointer to the object which will be passed to the other funcs.
void* (*open)(const char* filename, int* channels_out, int* rate_out);
// close closes the object returned by audio_open.
void (*close)(void* obj);
// read_frames_s16le reads audio samples into buf of size buf_sz and
// returns the number of frames read (samples divided by channels). Zero
// means the stream has ended, and a negative number is an error. The
// channel count is the same as the one returned from audio_open.
int (*read_frames_s16le)(void* obj, int16_t* buf, size_t buf_sz, int channels);
} audio_format_t;
// audio_format returns the audio_format_t for the provided filename if it is
// recognized.
const audio_format_t* audio_format(const char* filename);
// audio_play plays a specified audio file on a device. A nonzero number is
// returned on error. If play_until is not NULL, the audio plays until it
// (called with the argument play_until_data) returns true.
int audio_play(const audio_output_t output, void* output_cfg, const audio_format_t format, const char* filename, bool (*play_until)(void*), void* play_until_data, float volume);
#ifdef AUDIO_SUPPORT_ALSA
// audio_play_alsa plays audio on an ALSA device. If using pulseaudio, you may
// need to run your application with pasuspender.
int audio_play_alsa(int card, int device, const audio_format_t format, const char* filename, bool (*play_until)(void*), void* play_until_data, float volume);
const audio_output_t audio_output_alsa;
typedef struct audio_output_cfg_alsa_t {
int card;
int device;
} audio_output_cfg_alsa_t;
#endif
#ifdef AUDIO_SUPPORT_PULSE
int audio_play_pulse(const audio_format_t format, const char* filename, bool (*play_until)(void*), void* play_until_data, float volume);
const audio_output_t audio_output_pulse;
#endif
#ifdef AUDIO_SUPPORT_VORBIS
const audio_format_t audio_format_vorbis;
#endif
#ifdef AUDIO_SUPPORT_FLAC
const audio_format_t audio_format_flac;
#endif
#ifdef AUDIO_SUPPORT_WAV
const audio_format_t audio_format_wav;
#endif
#ifdef AUDIO_SUPPORT_MP3
const audio_format_t audio_format_mp3;
#endif
#endif
#ifdef AUDIO_IMPLEMENTATION
#include <stdio.h>
#include <errno.h>
#include <string.h>
const audio_format_t* audio_format(const char* filename) {
char* dot = strrchr(filename, '.');
#define audio_format_x(fmt, ext) if (dot && !strcasecmp(dot, ext)) return &fmt;
#ifdef AUDIO_SUPPORT_VORBIS
audio_format_x(audio_format_vorbis, ".ogg");
audio_format_x(audio_format_vorbis, ".oga");
#endif
#ifdef AUDIO_SUPPORT_FLAC
audio_format_x(audio_format_flac, ".flac");
#endif
#ifdef AUDIO_SUPPORT_WAV
audio_format_x(audio_format_wav, ".wav");
audio_format_x(audio_format_wav, ".wave");
audio_format_x(audio_format_wav, ".riff");
#endif
#ifdef AUDIO_SUPPORT_MP3
audio_format_x(audio_format_mp3, ".mp3");
#endif
#undef audio_format_x
return NULL;
}
int audio_play(const audio_output_t output, void* output_cfg, const audio_format_t format, const char* filename, bool (*play_until)(void*), void* play_until_data, float volume) {
int err, channels, rate, frame_count;
int16_t buf[4096];
void *fmt, *out;
if ((fmt = format.open(filename, &channels, &rate)) == NULL)
return 1;
if ((out = output.open(output_cfg, channels, rate)) == NULL) {
format.close(fmt);
return 2;
}
while ((frame_count = format.read_frames_s16le(fmt, buf, 4096, channels))) {
if (frame_count > 0 && volume > 0)
for (int sample = 0; sample < frame_count*channels; sample++)
buf[sample] *= volume;
if ((err = output.write_frames_s16le(out, buf, (size_t)(frame_count*channels)*sizeof(buf[0]), frame_count)) < 0) {
output.close(out);
format.close(fmt);
return err;
} else if (play_until && (*play_until)(play_until_data)) {
output.stop(out);
break;
}
}
output.close(out);
format.close(fmt);
return 0;
}
#define __audio_output__play0(name) int audio_play_ ## name(const audio_format_t format, const char* filename, bool (*play_until)(void*), void* play_until_data, float volume) { return audio_play(audio_output_ ## name, NULL, format, filename, play_until, play_until_data, volume); };
#define __audio_output__play(name, conv, ...) int audio_play_ ## name(__VA_ARGS__, const audio_format_t format, const char* filename, bool (*play_until)(void*), void* play_until_data, float volume) { return audio_play(audio_output_ ## name, conv, format, filename, play_until, play_until_data, volume); };
#define __audio_output__open(name) static void* audio_open_ ## name(void* cfg, int channels, int rate)
#define __audio_output__close(name) static void audio_close_ ## name(void* obj)
#define __audio_output__stop(name) static void audio_stop_ ## name(void* obj)
#define __audio_output__write(name) static int audio_write_frames_s16le_ ## name(void* obj, int16_t *buf, size_t buf_sz, int frame_count)
#define __audio_output(name) const audio_output_t audio_output_ ## name = {\
.open = audio_open_ ## name,\
.close = audio_close_ ## name,\
.stop = audio_stop_ ## name,\
.write_frames_s16le = audio_write_frames_s16le_ ## name,\
}
#define __audio_format__open(format) static void* audio_open_ ## format(const char* filename, int* channels_out, int* rate_out)
#define __audio_format__close(format) static void audio_close_ ## format(void* obj)
#define __audio_format__read(format) static int audio_read_frames_s16le_ ## format(void* obj, int16_t* buf, size_t buf_sz, int channels)
#define __audio_format(format) const audio_format_t audio_format_ ## format = {\
.open = audio_open_ ## format,\
.close = audio_close_ ## format,\
.read_frames_s16le = audio_read_frames_s16le_ ## format,\
}
#ifdef AUDIO_SUPPORT_ALSA
__audio_output__play(alsa, (&(audio_output_cfg_alsa_t){ .card = card, .device = device }), int card, int device);
__audio_output__open(alsa) {
audio_output_cfg_alsa_t *acfg = (audio_output_cfg_alsa_t*)(cfg);
struct pcm *obj = pcm_open(acfg->card, acfg->device, PCM_OUT, &(struct pcm_config) {
.channels = channels,
.rate = rate,
.format = PCM_FORMAT_S16_LE,
.period_size = 1024, // default
.period_count = 2 // default
});
return pcm_is_ready(obj) ? obj : NULL;
}
__audio_output__close(alsa) { pcm_close((struct pcm*)(obj)); }
__audio_output__stop(alsa) { pcm_stop((struct pcm*)(obj)); }
__audio_output__write(alsa) { return pcm_writei((struct pcm*)(obj), buf, frame_count); }
__audio_output(alsa);
#endif
#ifdef AUDIO_SUPPORT_PULSE
__audio_output__play0(pulse);
__audio_output__open(pulse) {
return pa_simple_new(NULL, "audio.h", PA_STREAM_PLAYBACK, NULL, "audio", &(pa_sample_spec) {
.channels = channels,
.rate = rate,
.format = PA_SAMPLE_S16NE,
}, NULL, NULL, NULL);
}
__audio_output__close(pulse) { pa_simple_free((pa_simple*)(obj)); }
__audio_output__stop(pulse) { pa_simple_flush((pa_simple*)(obj), NULL); }
__audio_output__write(pulse) { return pa_simple_write((pa_simple*)(obj), buf, buf_sz, NULL); }
__audio_output(pulse);
#endif
#ifdef AUDIO_SUPPORT_VORBIS
__audio_format__open(vorbis) {
stb_vorbis* v = stb_vorbis_open_filename(filename, NULL, NULL);
stb_vorbis_info i = stb_vorbis_get_info(v);
*channels_out = i.channels;
*rate_out = i.sample_rate;
return v;
}
__audio_format__close(vorbis) { stb_vorbis_close((stb_vorbis*)(obj)); }
__audio_format__read(vorbis) { return stb_vorbis_get_samples_short_interleaved((stb_vorbis*)(obj), channels, buf, buf_sz/channels); }
__audio_format(vorbis);
#endif
#ifdef AUDIO_SUPPORT_FLAC
__audio_format__open(flac) {
drflac* f = drflac_open_file(filename, NULL);
*channels_out = f->channels;
*rate_out = f->sampleRate;
return f;
}
__audio_format__close(flac) { drflac_close((drflac*)(obj)); }
__audio_format__read(flac) { return drflac_read_pcm_frames_s16((drflac*)(obj), buf_sz/channels, buf); }
__audio_format(flac);
#endif
#ifdef AUDIO_SUPPORT_WAV
__audio_format__open(wav) {
drwav* f = drwav_open_file(filename);
*channels_out = f->channels;
*rate_out = f->sampleRate;
return f;
}
__audio_format__close(wav) { drwav_close((drwav*)(obj)); }
__audio_format__read(wav) { return drwav_read_pcm_frames_s16((drwav*)(obj), buf_sz/channels, buf); }
__audio_format(wav);
#endif
#ifdef AUDIO_SUPPORT_MP3
__audio_format__open(mp3) {
drmp3* f = malloc(sizeof(drmp3));
if (!drmp3_init_file(f, filename, NULL))
return NULL;
*channels_out = f->channels;
*rate_out = f->sampleRate;
return f;
}
__audio_format__close(mp3) { drmp3_uninit((drmp3*)(obj)); free(obj); }
__audio_format__read(mp3) { return drmp3_read_pcm_frames_s16((drmp3*)(obj), buf_sz/channels, buf); }
__audio_format(mp3);
#endif
#endif