]> git.lizzy.rs Git - nothing.git/blob - src/game/sound_samples.c
add version to level files
[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 "system/log.h"
9 #include "system/lt.h"
10 #include "system/nth_alloc.h"
11 #include "config.h"
12
13 // TODO(#1022): Sound_samples does not implement volume control.
14
15 struct Sound_samples
16 {
17     Lt *lt;
18     SDL_AudioDeviceID dev;
19     uint8_t **audio_buf_array;
20     uint32_t *audio_buf_size_array;
21     uint8_t **active_audio_buf_array;
22     size_t samples_count;
23     int paused;
24     float volume;
25     // TODO(#1127): A better solution for optional sound support
26     int failed;                 // This is hackish
27 };
28
29 static
30 int init_buffer_and_device(Sound_samples *sound_samples,
31                            const char *sample_files[])
32 {
33     // TODO(#1023): init_buffer_and_device uses hard-coded audio specification
34     SDL_AudioSpec destination_spec = { // stereo float32 44100Hz
35         .format = AUDIO_F32,
36         .channels = 2,
37         .freq = 44100
38     };
39     // TODO(#1024): a return value by SDL_GetNumAudioDevices that is <= 0 may not indicate an error
40     if (SDL_GetNumAudioDevices(0) <= 0) {
41         log_fail("No audio in 2019 LULW\n");
42         return -1;
43     }
44
45     sound_samples->audio_buf_array = PUSH_LT(sound_samples->lt, nth_calloc(sound_samples->samples_count, sizeof(uint8_t*)), free);
46     if (sound_samples->audio_buf_array == NULL) {
47         log_fail("Failed to allocate memory for audio buffer pointer array\n");
48         return -1;
49     }
50     sound_samples->audio_buf_size_array = PUSH_LT(sound_samples->lt, nth_calloc(sound_samples->samples_count, sizeof(uint32_t)), free);
51     if (sound_samples->audio_buf_size_array == NULL) {
52         log_fail("Failed to allocate memory for audio buffer size array\n");
53         return -1;
54     }
55     for (size_t i = 0; i < sound_samples->samples_count; ++i) {
56         uint8_t *wav_buf; uint32_t wav_buf_len; SDL_AudioSpec wav_spec;
57
58         log_info("Loading audio file %s...\n", sample_files[i]);
59         if (SDL_LoadWAV(sample_files[i], &wav_spec, &wav_buf, &wav_buf_len) == NULL) {
60             log_fail("Load WAV file failed: %s\n", SDL_GetError());
61             return -1;
62         }
63         PUSH_LT(sound_samples->lt, wav_buf, SDL_FreeWAV);
64         SDL_AudioCVT cvt;
65         int result = SDL_BuildAudioCVT(&cvt, wav_spec.format, (uint8_t)wav_spec.channels, (int)wav_spec.freq,
66                           destination_spec.format, (uint8_t)destination_spec.channels, (int)destination_spec.freq);
67         if (result < 0) {
68             log_fail("SDL_BuildAudioCVT failed: %s\n", SDL_GetError());
69             return -1;
70         } else if (result == 0) { // no need to do conversion
71             sound_samples->audio_buf_array[i] = wav_buf;
72             sound_samples->audio_buf_size_array[i] = wav_buf_len;
73         } else {
74             cvt.len = (int)wav_buf_len;
75             cvt.buf = PUSH_LT(sound_samples->lt, malloc((size_t)(cvt.len * cvt.len_mult)), free);
76             if (cvt.buf == NULL) {
77                 log_fail("Allocating buffer for conversion failed\n");
78                 return -1;
79             }
80             memcpy(cvt.buf, wav_buf, (size_t)cvt.len);
81             SDL_FreeWAV(RELEASE_LT(sound_samples->lt, wav_buf));
82             if (SDL_ConvertAudio(&cvt) < 0) {
83                 log_fail("SDL_ConvertAudio failed: %s\n", SDL_GetError());
84                 return -1;
85             }
86             sound_samples->audio_buf_array[i] = cvt.buf;
87             sound_samples->audio_buf_size_array[i] = (uint32_t)cvt.len_cvt;
88         }
89     }
90
91     /* Allocating active audio buffer location*/
92     //TODO(#1072): Allocate one huge active audio buffer with length of the maximum of all audio buffer, instead of one active buffer for each audio
93     sound_samples->active_audio_buf_array = PUSH_LT(sound_samples->lt, nth_calloc(sound_samples->samples_count, sizeof(uint8_t*)), free);
94     if (sound_samples->active_audio_buf_array == NULL) {
95       log_fail("Failed to allocate memory for active audio buffer pointer array\n");
96       return -1;
97     }
98     for (size_t i = 0; i < sound_samples->samples_count; ++i) {
99       sound_samples->active_audio_buf_array[i] = PUSH_LT(sound_samples->lt, nth_calloc(sound_samples->audio_buf_size_array[i],
100                                                                                        sizeof(uint8_t)), free);
101       if (sound_samples->active_audio_buf_array == NULL) {
102         log_fail("Failed to allocate memory for active audio buffer array\n");
103         return -1;
104       }
105     }
106
107     /* Opening the device*/
108     sound_samples->dev = SDL_OpenAudioDevice(NULL, 0, &destination_spec, NULL, 0);
109     if (sound_samples->dev == 0) {
110         log_fail("SDL_OpenAudioDevice failed: %s\n", SDL_GetError());
111         log_info("The audio device may not support the hardcoded format\n");
112         return -1;
113     }
114     log_info("Audio device ID %u opened\n", sound_samples->dev);
115     return 0;
116 }
117
118 Sound_samples *create_sound_samples(const char *sample_files[],
119                                     size_t sample_files_count)
120 {
121     trace_assert(sample_files);
122     trace_assert(sample_files_count > 0);
123
124     Lt *lt = create_lt();
125
126     Sound_samples *sound_samples = PUSH_LT(lt, nth_calloc(1, sizeof(Sound_samples)), free);
127     if (sound_samples == NULL) {
128         RETURN_LT(lt, NULL);
129     }
130     sound_samples->lt = lt;
131     sound_samples->volume = SOUND_SAMPLES_DEFAULT_VOLUME;
132
133     sound_samples->samples_count = sample_files_count;
134     if (init_buffer_and_device(sound_samples, sample_files) < 0) {
135         log_fail("init_buffer_and_device failed\n");
136         sound_samples->failed = 1;
137     }
138
139     sound_samples->paused = 0;
140     SDL_PauseAudioDevice(sound_samples->dev, 0);
141
142     return sound_samples;
143 }
144
145 void destroy_sound_samples(Sound_samples *sound_samples)
146 {
147     // TODO(#1025): Use a seperate callback function for audio handling and pass that into SDL_OpenAudioDevice
148     if (sound_samples->failed) return;
149     trace_assert(sound_samples);
150     trace_assert(sound_samples->dev);
151     SDL_CloseAudioDevice(sound_samples->dev);
152     RETURN_LT0(sound_samples->lt);
153 }
154
155 int sound_samples_play_sound(Sound_samples *sound_samples,
156                              size_t sound_index)
157 {
158     trace_assert(sound_samples);
159     if (sound_samples->failed) return 0;
160     trace_assert(sound_index < sound_samples->samples_count);
161     trace_assert(sound_samples->dev);
162
163     /* Premix the audio volume */
164     memset(sound_samples->active_audio_buf_array[sound_index], 0, sound_samples->audio_buf_size_array[sound_index]);
165
166     //TODO(#1073): replace this linear scaling volume with logarithmic scale for better audio perception
167     SDL_MixAudioFormat(sound_samples->active_audio_buf_array[sound_index],
168                        sound_samples->audio_buf_array[sound_index],
169                        AUDIO_F32, //Hardcoded format just like in issue #1023
170                        sound_samples->audio_buf_size_array[sound_index],
171                        (int)((float)SDL_MIX_MAXVOLUME * sound_samples->volume / 100.0f));
172
173     /* Play the audio*/
174     SDL_ClearQueuedAudio(sound_samples->dev);
175     if (SDL_QueueAudio(sound_samples->dev, sound_samples->active_audio_buf_array[sound_index],
176                 sound_samples->audio_buf_size_array[sound_index]) < 0) {
177         log_warn("Failed to queue audio data of sound index %zu to device: %s\n", sound_index, SDL_GetError());
178         return 0;
179     }
180     SDL_PauseAudioDevice(sound_samples->dev, 0);
181     return 0;
182 }
183
184 int sound_samples_toggle_pause(Sound_samples *sound_samples)
185 {
186     trace_assert(sound_samples);
187     if (sound_samples->failed) return 0;
188     sound_samples->paused = !sound_samples->paused;
189     trace_assert(sound_samples->dev);
190     SDL_PauseAudioDevice(sound_samples->dev, sound_samples->paused);
191     return 0;
192 }
193
194 void sound_samples_update_volume(Sound_samples *sound_samples,
195                                  float volume)
196 {
197     trace_assert(sound_samples);
198     if (sound_samples->failed) return;
199     sound_samples->volume = volume;
200 }