2 #include "system/stacktrace.h"
7 #include "sound_samples.h"
8 #include "system/log.h"
10 #include "system/nth_alloc.h"
13 // TODO(#1022): Sound_samples does not implement volume control.
18 SDL_AudioDeviceID dev;
19 uint8_t **audio_buf_array;
20 uint32_t *audio_buf_size_array;
21 uint8_t **active_audio_buf_array;
25 // TODO(#1127): A better solution for optional sound support
26 int failed; // This is hackish
30 int init_buffer_and_device(Sound_samples *sound_samples,
31 const char *sample_files[])
33 // TODO(#1023): init_buffer_and_device uses hard-coded audio specification
34 SDL_AudioSpec destination_spec = { // stereo float32 44100Hz
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");
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");
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");
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;
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());
63 PUSH_LT(sound_samples->lt, wav_buf, SDL_FreeWAV);
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);
68 log_fail("SDL_BuildAudioCVT failed: %s\n", SDL_GetError());
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;
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");
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());
86 sound_samples->audio_buf_array[i] = cvt.buf;
87 sound_samples->audio_buf_size_array[i] = (uint32_t)cvt.len_cvt;
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");
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");
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");
114 log_info("Audio device ID %u opened\n", sound_samples->dev);
118 Sound_samples *create_sound_samples(const char *sample_files[],
119 size_t sample_files_count)
121 trace_assert(sample_files);
122 trace_assert(sample_files_count > 0);
124 Lt *lt = create_lt();
126 Sound_samples *sound_samples = PUSH_LT(lt, nth_calloc(1, sizeof(Sound_samples)), free);
127 if (sound_samples == NULL) {
130 sound_samples->lt = lt;
131 sound_samples->volume = SOUND_SAMPLES_DEFAULT_VOLUME;
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;
139 sound_samples->paused = 0;
140 SDL_PauseAudioDevice(sound_samples->dev, 0);
142 return sound_samples;
145 void destroy_sound_samples(Sound_samples *sound_samples)
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);
155 int sound_samples_play_sound(Sound_samples *sound_samples,
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);
163 /* Premix the audio volume */
164 memset(sound_samples->active_audio_buf_array[sound_index], 0, sound_samples->audio_buf_size_array[sound_index]);
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));
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());
180 SDL_PauseAudioDevice(sound_samples->dev, 0);
184 int sound_samples_toggle_pause(Sound_samples *sound_samples)
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);
194 void sound_samples_update_volume(Sound_samples *sound_samples,
197 trace_assert(sound_samples);
198 if (sound_samples->failed) return;
199 sound_samples->volume = volume;