tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE

audio.c (4934B)


      1 #include <stdint.h>
      2 #include <stdbool.h>
      3 #include <stddef.h>
      4 #include "audio.h"
      5 
      6 #define TA_AUDIO_MASK (TA_AUDIO_BUFFER_SIZE - 1)
      7 #define TA_AUDIO_MIX_CHUNK 256
      8 
      9 typedef char ta_audio_size_is_power_of_two[(TA_AUDIO_BUFFER_SIZE & TA_AUDIO_MASK) == 0 ? 1 : -1];
     10 
     11 typedef struct {
     12 	int8_t data[TA_AUDIO_BUFFER_SIZE];
     13 	uint32_t read;
     14 	uint32_t write;
     15 	ta_audio_source source;
     16 	void *user;
     17 	uint8_t volume;
     18 	uint8_t pan;
     19 	bool locked;
     20 } ta_audio_buffer;
     21 
     22 static ta_audio_buffer buffers[TA_AUDIO_BUFFERS];
     23 static int32_t mix_left[TA_AUDIO_MIX_CHUNK];
     24 static int32_t mix_right[TA_AUDIO_MIX_CHUNK];
     25 
     26 static bool buffer_valid(int buffer) {
     27 	return buffer >= 0 && buffer < TA_AUDIO_BUFFERS && buffers[buffer].locked;
     28 }
     29 
     30 int ta_audio_lock(ta_audio_source source, void *user) {
     31 	for (int i = 0; i < TA_AUDIO_BUFFERS; i++) {
     32 		if (!buffers[i].locked) {
     33 			buffers[i].read = 0;
     34 			buffers[i].write = 0;
     35 			buffers[i].source = source;
     36 			buffers[i].user = user;
     37 			buffers[i].volume = TA_AUDIO_VOLUME_MAX;
     38 			buffers[i].pan = TA_AUDIO_PAN_CENTER;
     39 			buffers[i].locked = true;
     40 
     41 			return i;
     42 		}
     43 	}
     44 
     45 	return -1;
     46 }
     47 
     48 void ta_audio_unlock(int buffer) {
     49 	if (!buffer_valid(buffer)) {
     50 		return;
     51 	}
     52 
     53 	buffers[buffer].locked = false;
     54 	buffers[buffer].source = NULL;
     55 	buffers[buffer].user = NULL;
     56 	buffers[buffer].read = 0;
     57 	buffers[buffer].write = 0;
     58 }
     59 
     60 bool ta_audio_is_locked(int buffer) {
     61 	return buffer_valid(buffer);
     62 }
     63 
     64 void ta_audio_set_volume(int buffer, uint8_t volume) {
     65 	if (buffer_valid(buffer)) {
     66 		buffers[buffer].volume = volume;
     67 	}
     68 }
     69 
     70 void ta_audio_set_pan(int buffer, uint8_t pan) {
     71 	if (buffer_valid(buffer)) {
     72 		buffers[buffer].pan = pan;
     73 	}
     74 }
     75 
     76 uint8_t ta_audio_get_volume(int buffer) {
     77 	return buffer_valid(buffer) ? buffers[buffer].volume : 0;
     78 }
     79 
     80 uint8_t ta_audio_get_pan(int buffer) {
     81 	return buffer_valid(buffer) ? buffers[buffer].pan : TA_AUDIO_PAN_CENTER;
     82 }
     83 
     84 uint32_t ta_audio_available(int buffer) {
     85 	if (!buffer_valid(buffer)) {
     86 		return 0;
     87 	}
     88 
     89 	return buffers[buffer].write - buffers[buffer].read;
     90 }
     91 
     92 uint32_t ta_audio_free(int buffer) {
     93 	if (!buffer_valid(buffer)) {
     94 		return 0;
     95 	}
     96 
     97 	return TA_AUDIO_BUFFER_SIZE - (buffers[buffer].write - buffers[buffer].read);
     98 }
     99 
    100 uint32_t ta_audio_write(int buffer, const int8_t *samples, uint32_t count) {
    101 	uint32_t room = ta_audio_free(buffer);
    102 
    103 	if (samples == NULL || room == 0) {
    104 		return 0;
    105 	}
    106 
    107 	if (count > room) {
    108 		count = room;
    109 	}
    110 
    111 	ta_audio_buffer *target = &buffers[buffer];
    112 
    113 	for (uint32_t i = 0; i < count; i++) {
    114 		target->data[(target->write + i) & TA_AUDIO_MASK] = samples[i];
    115 	}
    116 
    117 	target->write += count;
    118 
    119 	return count;
    120 }
    121 
    122 uint32_t ta_audio_read(int buffer, int8_t *samples, uint32_t count) {
    123 	uint32_t ready = ta_audio_available(buffer);
    124 
    125 	if (samples == NULL || ready == 0) {
    126 		return 0;
    127 	}
    128 
    129 	if (count > ready) {
    130 		count = ready;
    131 	}
    132 
    133 	ta_audio_buffer *source = &buffers[buffer];
    134 
    135 	for (uint32_t i = 0; i < count; i++) {
    136 		samples[i] = source->data[(source->read + i) & TA_AUDIO_MASK];
    137 	}
    138 
    139 	source->read += count;
    140 
    141 	return count;
    142 }
    143 
    144 void ta_audio_clear(int buffer) {
    145 	if (buffer_valid(buffer)) {
    146 		buffers[buffer].read = buffers[buffer].write;
    147 	}
    148 }
    149 
    150 void ta_audio_update(void) {
    151 	for (int i = 0; i < TA_AUDIO_BUFFERS; i++) {
    152 		if (!buffers[i].locked || buffers[i].source == NULL) {
    153 			continue;
    154 		}
    155 
    156 		bool already_called = false;
    157 
    158 		for (int j = 0; j < i; j++) {
    159 			if (buffers[j].locked && buffers[j].source == buffers[i].source && buffers[j].user == buffers[i].user) {
    160 				already_called = true;
    161 				break;
    162 			}
    163 		}
    164 
    165 		if (!already_called) {
    166 			buffers[i].source(buffers[i].user);
    167 		}
    168 	}
    169 }
    170 
    171 static int16_t clamp16(int32_t value) {
    172 	if (value > 32767) {
    173 		return 32767;
    174 	}
    175 
    176 	if (value < -32768) {
    177 		return -32768;
    178 	}
    179 
    180 	return (int16_t)value;
    181 }
    182 
    183 uint32_t ta_audio_mix(int16_t *out, uint32_t frames) {
    184 	if (out == NULL) {
    185 		return 0;
    186 	}
    187 
    188 	uint32_t done = 0;
    189 
    190 	while (done < frames) {
    191 		uint32_t chunk = frames - done;
    192 
    193 		if (chunk > TA_AUDIO_MIX_CHUNK) {
    194 			chunk = TA_AUDIO_MIX_CHUNK;
    195 		}
    196 
    197 		for (uint32_t i = 0; i < chunk; i++) {
    198 			mix_left[i] = 0;
    199 			mix_right[i] = 0;
    200 		}
    201 
    202 		for (int b = 0; b < TA_AUDIO_BUFFERS; b++) {
    203 			if (!buffers[b].locked || buffers[b].volume == 0) {
    204 				continue;
    205 			}
    206 
    207 			ta_audio_buffer *source = &buffers[b];
    208 			uint32_t ready = source->write - source->read;
    209 
    210 			if (ready > chunk) {
    211 				ready = chunk;
    212 			}
    213 
    214 			int32_t gain_right = source->pan;
    215 			int32_t gain_left = 255 - gain_right;
    216 			int32_t left_scale = (int32_t)source->volume * gain_left;
    217 			int32_t right_scale = (int32_t)source->volume * gain_right;
    218 
    219 			for (uint32_t i = 0; i < ready; i++) {
    220 				int32_t sample = source->data[(source->read + i) & TA_AUDIO_MASK];
    221 
    222 				mix_left[i] += (sample * left_scale) >> 8;
    223 				mix_right[i] += (sample * right_scale) >> 8;
    224 			}
    225 
    226 			source->read += ready;
    227 		}
    228 
    229 		for (uint32_t i = 0; i < chunk; i++) {
    230 			out[(done + i) * 2] = clamp16(mix_left[i]);
    231 			out[(done + i) * 2 + 1] = clamp16(mix_right[i]);
    232 		}
    233 
    234 		done += chunk;
    235 	}
    236 
    237 	return done;
    238 }