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