Less aggressive colour change in the title
[snowy-minesweeper.git] / cdi / cdi-audio.c
blobbf58f81096b8024ef05a54a41839929872f6e04d
1 #include <audio.h>
2 #include <stdbool.h>
3 #include <stddef.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <cdi/audio.h>
7 #include <cdi/lists.h>
8 #include <cdi/mem.h>
11 #define BUFFER_COUNT 8
12 #define INJECT_OFFSET 256 /* arbitrary */
15 static struct cdi_audio_device *audio_device;
16 static struct cdi_audio_driver *audio_driver;
17 static struct cdi_audio_stream *audio_stream;
19 static void (*buffer_cb)(int16_t *data, size_t samples);
20 static int current_device_buffer;
22 static struct cdi_mem_area *buffers[BUFFER_COUNT];
23 static int prebuffered_buffers;
26 void register_cdi_audio_device(struct cdi_audio_device *dev)
28 if (audio_device || dev->record) {
29 return;
32 audio_device = dev;
33 audio_driver = (struct cdi_audio_driver *)dev->dev.driver;
34 audio_stream = cdi_list_get(audio_device->streams, 0);
36 if (audio_stream->sample_format != CDI_AUDIO_16SI) {
37 audio_device = NULL;
38 return;
41 audio_driver->set_sample_rate(audio_stream, 44100);
42 audio_driver->set_number_of_channels(audio_device, 2);
43 audio_driver->set_volume(audio_stream, 0xff);
46 bool init_audio(void (*callback)(int16_t *data, size_t samples))
48 if (!audio_device) {
49 return false;
52 for (int i = 0; i < BUFFER_COUNT; i++) {
53 buffers[i] = cdi_mem_alloc(audio_stream->buffer_size * 2, CDI_MEM_VIRT_ONLY);
56 buffer_cb = callback;
58 return true;
61 static void fetch_buffers(void)
63 int hw_index = (current_device_buffer + prebuffered_buffers) % audio_stream->num_buffers;
65 for (; prebuffered_buffers < BUFFER_COUNT; prebuffered_buffers++) {
66 buffer_cb(buffers[prebuffered_buffers]->vaddr, audio_stream->buffer_size);
67 audio_driver->transfer_data(audio_stream, hw_index, buffers[prebuffered_buffers], 0);
69 hw_index = (hw_index + 1) % audio_stream->num_buffers;
73 void audio_play(bool play)
75 if (!audio_driver) {
76 return;
79 audio_driver->change_device_status(audio_device, play ? CDI_AUDIO_PLAY : CDI_AUDIO_STOP);
81 if (play) {
82 fetch_buffers();
86 void audio_mute(bool mute)
88 audio_driver->set_volume(audio_stream, mute ? 0x00 : 0xff);
91 void inject_samples(int16_t *data, size_t count)
93 cdi_audio_position_t pos;
94 audio_driver->get_position(audio_stream, &pos);
96 size_t position = pos.frame * 2 + INJECT_OFFSET;
97 int buffer = position / audio_stream->buffer_size;
98 int offset = position % audio_stream->buffer_size;
100 while (count && buffer < prebuffered_buffers) {
101 /* I need variables like this all the time but I still don't know a good
102 * way to name them... */
103 size_t this_buffer_count = audio_stream->buffer_size - offset;
104 if (this_buffer_count > count) {
105 this_buffer_count = count;
108 int16_t *out = buffers[buffer]->vaddr;
109 out += offset;
110 for (size_t i = 0; i < this_buffer_count; i++) {
111 int val = out[i] + data[i];
112 if (val < -0x8000) {
113 val = -0x8000;
114 } else if (val > 0x7fff) {
115 val = 0x7fff;
117 out[i] = val;
119 data += this_buffer_count;
121 offset = 0;
122 buffer++;
123 count -= this_buffer_count;
126 int hw_index = current_device_buffer;
127 for (int i = 0; i < buffer; i++) {
128 audio_driver->transfer_data(audio_stream, hw_index, buffers[i], 0);
129 hw_index = (hw_index + 1) % audio_stream->num_buffers;
133 void cdi_audio_buffer_completed(struct cdi_audio_stream *stream, size_t buffer)
135 (void)stream;
137 buffer = (buffer + 1) % audio_stream->num_buffers;
139 cdi_audio_position_t pos;
140 audio_driver->get_position(audio_stream, &pos);
142 while (current_device_buffer != buffer) {
143 struct cdi_mem_area *popped = buffers[0];
144 memmove(&buffers[0], &buffers[1], sizeof(buffers[0]) * (BUFFER_COUNT - 1));
145 buffers[BUFFER_COUNT - 1] = popped;
147 current_device_buffer = (current_device_buffer + 1) % audio_stream->num_buffers;
149 if (prebuffered_buffers) {
150 prebuffered_buffers--;
154 fetch_buffers();