GoldFish Engine
Quite simple and lightweight game engine
Loading...
Searching...
No Matches
gf_audio.c
1#define GF_EXPOSE_AUDIO
2
3#include <gf_pre.h>
4
5/* External library */
6#include <miniaudio.h>
7#include <jar_xm.h>
8#include <jar_mod.h>
9
10/* Interface */
11#include <gf_audio.h>
12
13/* Engine */
14#include <gf_log.h>
15
16/* Standard */
17#include <stdlib.h>
18#include <string.h>
19#include <math.h>
20
21const char* gf_audio_mod_sig[] = {"M!K!", "M.K.", "FLT4", "FLT8", "4CHN", "6CHN", "8CHN", "10CH", "12CH", "14CH", "16CH", "18CH", "20CH", "22CH", "24CH", "26CH", "28CH", "30CH", "32CH"};
22
23void gf_audio_callback(ma_device* dev, void* output, const void* input, ma_uint32 frame) {
24 gf_audio_id_t i;
25 gf_audio_t* audio = dev->pUserData;
26 ma_int16* out = (ma_int16*)output;
27 float* tmp = malloc(sizeof(*tmp) * frame * 2);
28 int unlocked = 0;
29
30 for(i = 0; i < frame; i++) {
31 tmp[2 * i + 0] = 0;
32 tmp[2 * i + 1] = 0;
33 }
34
35 ma_mutex_lock(audio->mutex);
36 for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
37 if(audio->decoder[i].used == 1 && audio->decoder[i].decoder != NULL) {
38 ma_uint64 readframe;
39 int j;
40 ma_int16* r = malloc(sizeof(*r) * frame * 2);
41 ma_decoder_read_pcm_frames(audio->decoder[i].decoder, r, frame, &readframe);
42 for(j = 0; j < readframe; j++) {
43 tmp[2 * j + 0] += (double)r[2 * j + 0] / 32768.0;
44 tmp[2 * j + 1] += (double)r[2 * j + 1] / 32768.0;
45 }
46 free(r);
47 if(frame > readframe) {
48 ma_mutex_unlock(audio->mutex);
49 unlocked = 1;
51 }
52 } else if(audio->decoder[i].used == 1 && audio->decoder[i].xm != NULL) {
53 int j;
54 int gotframe;
55 float* r = malloc(sizeof(*r) * frame * 2);
56 jar_xm_generate_samples(audio->decoder[i].xm, r, frame);
57 gotframe = audio->decoder[i].samples > frame ? frame : audio->decoder[i].samples;
58 for(j = 0; j < gotframe; j++) {
59 tmp[2 * j + 0] += (double)r[2 * j + 0];
60 tmp[2 * j + 1] += (double)r[2 * j + 1];
61 }
62 free(r);
63 audio->decoder[i].samples -= frame;
64 if(audio->decoder[i].samples <= 0) {
65 ma_mutex_unlock(audio->mutex);
66 unlocked = 1;
68 }
69 } else if(audio->decoder[i].used == 1 && audio->decoder[i].mod != NULL) {
70 int j;
71 int gotframe;
72 ma_int16* r = malloc(sizeof(*r) * frame * 2);
73 jar_mod_fillbuffer(audio->decoder[i].mod, r, frame, NULL);
74 gotframe = audio->decoder[i].samples > frame ? frame : audio->decoder[i].samples;
75 for(j = 0; j < gotframe; j++) {
76 tmp[2 * j + 0] += (double)r[2 * j + 0] / 32768.0;
77 tmp[2 * j + 1] += (double)r[2 * j + 1] / 32768.0;
78 }
79 free(r);
80 audio->decoder[i].samples -= frame;
81 if(audio->decoder[i].samples <= 0) {
82 ma_mutex_unlock(audio->mutex);
83 unlocked = 1;
85 }
86 }
87 }
88 if(!unlocked) ma_mutex_unlock(audio->mutex);
89
90 for(i = 0; i < frame; i++) {
91 out[2 * i + 0] = tmp[2 * i + 0] * 32768;
92 out[2 * i + 1] = tmp[2 * i + 1] * 32768;
93 }
94 free(tmp);
95}
96
97gf_audio_id_t gf_audio_load(gf_audio_t* audio, const void* data, size_t size) {
98 gf_audio_id_t i;
99 ma_mutex_lock(audio->mutex);
100 for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
101 if(audio->decoder[i].used == 0) {
102 int xm_cond = size > 37 && memcmp(data, "Extended Module: ", 17) == 0 && ((char*)data)[37] == 0x1a;
103 int mod_cond = size > 1080;
104
105 if(mod_cond) {
106 int j;
107 int mod_sig_cond = 0;
108 for(j = 0; j < sizeof(gf_audio_mod_sig) / sizeof(gf_audio_mod_sig[0]); j++) {
109 mod_sig_cond = mod_sig_cond || (memcmp(data + 1080, gf_audio_mod_sig[j], 4) == 0);
110 }
111 mod_cond = mod_cond && mod_sig_cond;
112 }
113
114 if(xm_cond) {
115 if(jar_xm_create_context_safe(&audio->decoder[i].xm, data, size, audio->device_config.sampleRate) == 0) {
116 audio->decoder[i].samples = jar_xm_get_remaining_samples(audio->decoder[i].xm);
117 audio->decoder[i].used = -1;
118 ma_mutex_unlock(audio->mutex);
119 return i;
120 }
121 audio->decoder[i].xm = NULL;
122 } else if(mod_cond) {
123 audio->decoder[i].mod = malloc(sizeof(*audio->decoder[i].mod));
124 jar_mod_init(audio->decoder[i].mod);
125 jar_mod_setcfg(audio->decoder[i].mod, audio->device_config.sampleRate, 16, 1, 0, 0);
126 if(jar_mod_load(audio->decoder[i].mod, (void*)data, size)) {
127 audio->decoder[i].samples = jar_mod_max_samples(audio->decoder[i].mod);
128 audio->decoder[i].used = -1;
129 ma_mutex_unlock(audio->mutex);
130 return i;
131 }
132 free(audio->decoder[i].mod);
133 audio->decoder[i].mod = NULL;
134 }
135 audio->decoder[i].decoder = malloc(sizeof(*audio->decoder[i].decoder));
136 if(ma_decoder_init_memory(data, size, &audio->decoder[i].decoder_config, audio->decoder[i].decoder) == MA_SUCCESS) {
137 audio->decoder[i].used = -1;
138 ma_mutex_unlock(audio->mutex);
139 return i;
140 }
141 free(audio->decoder[i].decoder);
142 audio->decoder[i].decoder = NULL;
143 ma_mutex_unlock(audio->mutex);
144 return -1;
145 }
146 }
147 ma_mutex_unlock(audio->mutex);
148 return -1;
149}
150
151gf_audio_id_t gf_audio_load_file(gf_audio_t* audio, const char* path) {
152 FILE* f = fopen(path, "rb");
153 size_t sz;
154 unsigned char* data;
155 gf_audio_id_t st;
156 if(f == NULL) return -1;
157 fseek(f, 0, SEEK_END);
158 sz = ftell(f);
159 fseek(f, 0, SEEK_SET);
160
161 data = malloc(sz);
162 fread(data, sz, 1, f);
163 st = gf_audio_load(audio, data, sz);
164
165 fclose(f);
166
167 return st;
168}
169
171 gf_audio_t* audio = malloc(sizeof(*audio));
172 int i;
173
174 memset(audio, 0, sizeof(*audio));
175 audio->engine = engine;
176
177 audio->device_config = ma_device_config_init(ma_device_type_playback);
178 audio->device_config.playback.format = ma_format_s16;
179 audio->device_config.playback.channels = 2;
180 audio->device_config.sampleRate = 44100;
181 audio->device_config.dataCallback = gf_audio_callback;
182 audio->device_config.pUserData = audio;
183
184 for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
185 audio->decoder[i].used = 0;
186 audio->decoder[i].audio = NULL;
187 audio->decoder[i].decoder = NULL;
188 audio->decoder[i].xm = NULL;
189 audio->decoder[i].mod = NULL;
190 audio->decoder[i].decoder_config = ma_decoder_config_init(audio->device_config.playback.format, audio->device_config.playback.channels, audio->device_config.sampleRate);
191 }
192
193 audio->device = malloc(sizeof(*audio->device));
194 if(ma_device_init(NULL, &audio->device_config, audio->device) != MA_SUCCESS) {
195 gf_log_function(engine, "Failed to open playback device", "");
196 free(audio->device);
197 audio->device = NULL;
198 gf_audio_destroy(audio);
199 return NULL;
200 }
201
202 audio->mutex = malloc(sizeof(*audio->mutex));
203 if(ma_mutex_init(audio->mutex) != MA_SUCCESS) {
204 gf_log_function(engine, "Failed to create mutex", "");
205 free(audio->mutex);
206 audio->mutex = NULL;
207 gf_audio_destroy(audio);
208 return NULL;
209 }
210
211 if(ma_device_start(audio->device) != MA_SUCCESS) {
212 gf_log_function(engine, "Failed to start playback device", "");
213 gf_audio_destroy(audio);
214 return NULL;
215 }
216
217 for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
218 audio->decoder[i].audio = audio;
219 }
220
221 gf_log_function(engine, "Audio interface started, max %d decoders", GF_AUDIO_MAX_DECODERS);
222
223 return audio;
224}
225
227 ma_mutex_lock(decoder->audio->mutex);
228 if(decoder->decoder != NULL) {
229 ma_decoder_uninit(decoder->decoder);
230 free(decoder->decoder);
231 decoder->decoder = NULL;
232 }
233 if(decoder->xm != NULL) {
234 jar_xm_free_context(decoder->xm);
235 decoder->xm = NULL;
236 }
237 if(decoder->mod != NULL) {
238 jar_mod_unload(decoder->mod);
239 free(decoder->mod);
240 decoder->mod = NULL;
241 }
242 decoder->used = 0;
243 ma_mutex_unlock(decoder->audio->mutex);
244}
245
247 int i;
248 if(audio->device != NULL) {
249 ma_device_uninit(audio->device);
250 free(audio->device);
251 }
252 if(audio->mutex != NULL) {
253 ma_mutex_uninit(audio->mutex);
254 free(audio->mutex);
255 }
256 for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
258 }
259 gf_log_function(audio->engine, "Destroyed audio interface", "");
260 free(audio);
261}
262
263void gf_audio_resume(gf_audio_t* audio, gf_audio_id_t id) {
264 ma_mutex_lock(audio->mutex);
265 if(audio->decoder[id].used != 0) audio->decoder[id].used = 1;
266 ma_mutex_unlock(audio->mutex);
267}
268
269void gf_audio_pause(gf_audio_t* audio, gf_audio_id_t id) {
270 ma_mutex_lock(audio->mutex);
271 if(audio->decoder[id].used != 0) audio->decoder[id].used = -1;
272 ma_mutex_unlock(audio->mutex);
273}
274
275void gf_audio_stop(gf_audio_t* audio, gf_audio_id_t id) { gf_audio_decoder_destroy(&audio->decoder[id]); }
#define GF_AUDIO_MAX_DECODERS
Max decoders audio interface can handle.
Definition audio.h:33
Audio interface.
void gf_audio_stop(gf_audio_t *audio, gf_audio_id_t id)
Stop audio.
Definition gf_audio.c:275
void gf_audio_resume(gf_audio_t *audio, gf_audio_id_t id)
Resume audio.
Definition gf_audio.c:263
gf_audio_id_t gf_audio_load(gf_audio_t *audio, const void *data, size_t size)
Load and play data.
Definition gf_audio.c:97
gf_audio_t * gf_audio_create(gf_engine_t *engine)
Create audio interface.
Definition gf_audio.c:170
void gf_audio_destroy(gf_audio_t *audio)
Destroy audio interface.
Definition gf_audio.c:246
void gf_audio_decoder_destroy(gf_audio_decoder_t *decoder)
Destroy audio decoder.
Definition gf_audio.c:226
void gf_audio_pause(gf_audio_t *audio, gf_audio_id_t id)
Pause audio.
Definition gf_audio.c:269
gf_audio_id_t gf_audio_load_file(gf_audio_t *audio, const char *path)
Load and play file.
Definition gf_audio.c:151
Logger.
#define gf_log_function(engine, fmt,...)
Output log with line number and function name.
Definition gf_log.h:26
Required headers before anything.
Type definitions related to math.
Audio decoder.
Definition audio.h:69
jar_xm_context_t * xm
XM context.
Definition audio.h:69
ma_decoder_config decoder_config
miniaudio decoder config
Definition audio.h:69
int used
1 if used, -1 if used but paused, otherwise 0
Definition audio.h:69
int samples
Remaining samples.
Definition audio.h:69
jar_mod_context_t * mod
MOD context.
Definition audio.h:69
gf_audio_t * audio
Audio interface.
Definition audio.h:69
ma_decoder * decoder
miniaudio decoder
Definition audio.h:69
Audio interface.
Definition audio.h:97
ma_device_config device_config
miniaudio device config
Definition audio.h:97
ma_device * device
miniaudio device
Definition audio.h:97
ma_mutex * mutex
Mutex.
Definition audio.h:97
gf_audio_decoder_t decoder[64]
Decoder.
Definition audio.h:97
gf_engine_t * engine
Engine instance.
Definition audio.h:97
Engine instance.
Definition core.h:46