]> git.lizzy.rs Git - nothing.git/blob - src/game/sound_samples.c
Implement Sound_samples
[nothing.git] / src / game / sound_samples.c
1 #include <SDL.h>
2 #include "system/stacktrace.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include "math/pi.h"
7 #include "sound_samples.h"
8 #include "dynarray.h"
9 #include "system/log.h"
10 #include "system/lt.h"
11 #include "system/nth_alloc.h"
12
13 // TODO(#863): Sound_samples is not implemented
14 // TODO: Volume control?
15
16 struct Sound_samples
17 {
18     Lt *lt;
19     SDL_AudioDeviceID dev;
20     Dynarray *audio_buf_array;
21     Dynarray *audio_buf_size_array;
22     size_t samples_count;
23     int paused;
24 };
25
26 static int init_buffer_and_device(Sound_samples*, const char **);
27 Sound_samples *create_sound_samples(const char *sample_files[],
28                                     size_t sample_files_count)
29 {
30     trace_assert(sample_files);
31     trace_assert(sample_files_count > 0);
32
33     Lt *lt = create_lt();
34
35     Sound_samples *sound_samples = PUSH_LT(lt, nth_calloc(1, sizeof(Sound_samples)), free);
36     if (sound_samples == NULL) {
37         RETURN_LT(lt, NULL);
38     }
39     sound_samples->lt = lt;
40
41     sound_samples->samples_count = sample_files_count;
42     if (init_buffer_and_device(sound_samples, sample_files) < 0) {
43         log_fail("init_buffer_and_device failed\n");
44         RETURN_LT(lt, NULL);
45     }
46
47     sound_samples->paused = 0;
48     SDL_PauseAudioDevice(sound_samples->dev, 0);
49
50     return sound_samples;
51 }
52
53 static 
54 int init_buffer_and_device(Sound_samples *sound_samples, 
55                            const char *sample_files[]) 
56 {
57     // TODO: Select audio specification from a menu
58     // TODO: Use a seperate callback function
59     SDL_AudioSpec destination_spec = { // stereo float32 44100Hz
60         .format = AUDIO_F32,
61         .channels = 2,
62         .freq = 44100
63     }, actual;
64     // TODO: a return value by SDL_GetNumAudioDevices that is <= 0 may not indicate an error
65     if (SDL_GetNumAudioDevices(0) <= 0) {
66         log_fail("No audio in 2019 LULW\n");
67         return -1;
68     }
69     
70     sound_samples->audio_buf_array = PUSH_LT(sound_samples->lt, create_dynarray(sizeof(uint8_t*)), destroy_dynarray);
71     if (sound_samples->audio_buf_array == NULL) {
72         log_fail("Failed to allocate memory for audio buffer pointer array\n");
73         return -1;
74     }
75     sound_samples->audio_buf_size_array = PUSH_LT(sound_samples->lt, create_dynarray(sizeof(uint32_t)), destroy_dynarray);
76     if (sound_samples->audio_buf_size_array == NULL) {
77         log_fail("Failed to allocate memory for audio buffer size array\n");
78         return -1;
79     }
80     for (size_t i = 0; i < sound_samples->samples_count; ++i) {
81         uint8_t *wav_buf; uint32_t wav_buf_len; SDL_AudioSpec wav_spec;
82
83         log_info("Loading audio file %s...\n", sample_files[i]);
84         if (SDL_LoadWAV(sample_files[i], &wav_spec, &wav_buf, &wav_buf_len) == NULL) {
85             log_fail("Load WAV file failed: %s\n", SDL_GetError());
86             return -1;
87         }
88         PUSH_LT(sound_samples->lt, wav_buf, SDL_FreeWAV);
89         SDL_AudioCVT cvt;
90         int result = SDL_BuildAudioCVT(&cvt, wav_spec.format, (uint8_t)wav_spec.channels, (int)wav_spec.freq, 
91                           destination_spec.format, (uint8_t)destination_spec.channels, (int)destination_spec.freq);
92         if (result < 0) {
93             log_fail("SDL_BuildAudioCVT failed: %s\n", SDL_GetError());
94             return -1;
95         } else if (result == 0) { // no need to do conversion
96             if (dynarray_push(sound_samples->audio_buf_array, &wav_buf) != 0) {
97                 log_fail("Failed to push to audio buffer pointer array\n");
98                 return -1;
99             } 
100             if (dynarray_push(sound_samples->audio_buf_size_array, &wav_buf_len) != 0) {
101                 log_fail("Failed to push to audio buffer size array\n");
102                 return -1;
103             }
104         } else {
105             cvt.len = (int)wav_buf_len;
106             cvt.buf = PUSH_LT(sound_samples->lt, malloc((size_t)(cvt.len * cvt.len_mult)), free);
107             if (cvt.buf == NULL) {
108                 log_fail("Allocating buffer for conversion failed\n");
109                 return -1;
110             }
111             memcpy(cvt.buf, wav_buf, (size_t)cvt.len);
112             SDL_FreeWAV(wav_buf);
113             RELEASE_LT(sound_samples->lt, wav_buf);
114             if (SDL_ConvertAudio(&cvt) < 0) {
115                 log_fail("SDL_ConvertAudio failed: %s\n", SDL_GetError());
116                 return -1;
117             }
118             if (dynarray_push(sound_samples->audio_buf_array, &cvt.buf) != 0) {
119                 log_fail("Failed to push to audio buffer pointer array\n");
120                 return -1;
121             } 
122             if (dynarray_push(sound_samples->audio_buf_size_array, &cvt.len_cvt) != 0) {
123                 log_fail("Failed to push to audio buffer size array\n");
124                 return -1;
125             }
126         }
127     }
128     
129     sound_samples->dev = SDL_OpenAudioDevice(NULL, 0, &destination_spec, &actual, 0);
130     if (sound_samples->dev == 0) {
131         log_fail("SDL_OpenAudioDevice failed: %s\n", SDL_GetError());
132         log_info("The audio device may not support the hardcoded format\n");
133         return -1;
134     }
135     log_info("Audio device ID %u opened\n", sound_samples->dev);
136     return 0;
137 }
138
139 void destroy_sound_samples(Sound_samples *sound_samples)
140 {
141     // TODO: Use a seperate callback function for processing audio
142     trace_assert(sound_samples);
143     trace_assert(sound_samples->dev);
144     SDL_PauseAudioDevice(sound_samples->dev, 1);
145     SDL_ClearQueuedAudio(sound_samples->dev);
146     SDL_CloseAudioDevice(sound_samples->dev);
147     RETURN_LT0(sound_samples->lt);
148 }
149
150 int sound_samples_play_sound(Sound_samples *sound_samples,
151                              size_t sound_index)
152 {
153     trace_assert(sound_samples);
154     trace_assert(sound_index < sound_samples->samples_count);
155     uint8_t *audio_buf = *(uint8_t**)dynarray_pointer_at(sound_samples->audio_buf_array, sound_index);
156     uint32_t audio_buf_size = *(uint32_t*)dynarray_pointer_at(sound_samples->audio_buf_size_array, sound_index);
157     trace_assert(sound_samples->dev);
158     SDL_ClearQueuedAudio(sound_samples->dev);
159     if (SDL_QueueAudio(sound_samples->dev, audio_buf, audio_buf_size) < 0) {
160         log_warn("Failed to queue audio data of sound index %zu to device: %s\n", sound_index, SDL_GetError());
161         return -1;
162     }
163     SDL_PauseAudioDevice(sound_samples->dev, 0);
164     return 0;
165 }
166
167 int sound_samples_toggle_pause(Sound_samples *sound_samples)
168 {
169     trace_assert(sound_samples);
170     sound_samples->paused = !sound_samples->paused;
171     SDL_PauseAudioDevice(sound_samples->dev, sound_samples->paused);
172     return 0;
173 }