]> git.lizzy.rs Git - nothing.git/blob - src/game/sound_samples.c
Add TODO(#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     size_t samples_count;
22     int paused;
23 };
24
25 static 
26 int init_buffer_and_device(Sound_samples *sound_samples, 
27                            const char *sample_files[]) 
28 {
29     // TODO: init_buffer_and_device uses hard-coded audio specification
30     SDL_AudioSpec destination_spec = { // stereo float32 44100Hz
31         .format = AUDIO_F32,
32         .channels = 2,
33         .freq = 44100
34     };
35     // TODO: a return value by SDL_GetNumAudioDevices that is <= 0 may not indicate an error
36     if (SDL_GetNumAudioDevices(0) <= 0) {
37         log_fail("No audio in 2019 LULW\n");
38         return -1;
39     }
40     
41     sound_samples->audio_buf_array = PUSH_LT(sound_samples->lt, nth_calloc(sound_samples->samples_count, sizeof(uint8_t*)), free);
42     if (sound_samples->audio_buf_array == NULL) {
43         log_fail("Failed to allocate memory for audio buffer pointer array\n");
44         return -1;
45     }
46     sound_samples->audio_buf_size_array = PUSH_LT(sound_samples->lt, nth_calloc(sound_samples->samples_count, sizeof(uint32_t)), free);
47     if (sound_samples->audio_buf_size_array == NULL) {
48         log_fail("Failed to allocate memory for audio buffer size array\n");
49         return -1;
50     }
51     for (size_t i = 0; i < sound_samples->samples_count; ++i) {
52         uint8_t *wav_buf; uint32_t wav_buf_len; SDL_AudioSpec wav_spec;
53
54         log_info("Loading audio file %s...\n", sample_files[i]);
55         if (SDL_LoadWAV(sample_files[i], &wav_spec, &wav_buf, &wav_buf_len) == NULL) {
56             log_fail("Load WAV file failed: %s\n", SDL_GetError());
57             return -1;
58         }
59         PUSH_LT(sound_samples->lt, wav_buf, SDL_FreeWAV);
60         SDL_AudioCVT cvt;
61         int result = SDL_BuildAudioCVT(&cvt, wav_spec.format, (uint8_t)wav_spec.channels, (int)wav_spec.freq, 
62                           destination_spec.format, (uint8_t)destination_spec.channels, (int)destination_spec.freq);
63         if (result < 0) {
64             log_fail("SDL_BuildAudioCVT failed: %s\n", SDL_GetError());
65             return -1;
66         } else if (result == 0) { // no need to do conversion
67             sound_samples->audio_buf_array[i] = wav_buf;
68             sound_samples->audio_buf_size_array[i] = wav_buf_len;
69         } else {
70             cvt.len = (int)wav_buf_len;
71             cvt.buf = PUSH_LT(sound_samples->lt, malloc((size_t)(cvt.len * cvt.len_mult)), free);
72             if (cvt.buf == NULL) {
73                 log_fail("Allocating buffer for conversion failed\n");
74                 return -1;
75             }
76             memcpy(cvt.buf, wav_buf, (size_t)cvt.len);
77             SDL_FreeWAV(RELEASE_LT(sound_samples->lt, wav_buf));
78             if (SDL_ConvertAudio(&cvt) < 0) {
79                 log_fail("SDL_ConvertAudio failed: %s\n", SDL_GetError());
80                 return -1;
81             }
82             sound_samples->audio_buf_array[i] = cvt.buf;
83             sound_samples->audio_buf_size_array[i] = (uint32_t)cvt.len_cvt;
84         }
85     }
86     
87     sound_samples->dev = SDL_OpenAudioDevice(NULL, 0, &destination_spec, NULL, 0);
88     if (sound_samples->dev == 0) {
89         log_fail("SDL_OpenAudioDevice failed: %s\n", SDL_GetError());
90         log_info("The audio device may not support the hardcoded format\n");
91         return -1;
92     }
93     log_info("Audio device ID %u opened\n", sound_samples->dev);
94     return 0;
95 }
96
97 Sound_samples *create_sound_samples(const char *sample_files[],
98                                     size_t sample_files_count)
99 {
100     trace_assert(sample_files);
101     trace_assert(sample_files_count > 0);
102
103     Lt *lt = create_lt();
104
105     Sound_samples *sound_samples = PUSH_LT(lt, nth_calloc(1, sizeof(Sound_samples)), free);
106     if (sound_samples == NULL) {
107         RETURN_LT(lt, NULL);
108     }
109     sound_samples->lt = lt;
110
111     sound_samples->samples_count = sample_files_count;
112     if (init_buffer_and_device(sound_samples, sample_files) < 0) {
113         log_fail("init_buffer_and_device failed\n");
114         RETURN_LT(lt, NULL);
115     }
116
117     sound_samples->paused = 0;
118     SDL_PauseAudioDevice(sound_samples->dev, 0);
119
120     return sound_samples;
121 }
122
123 void destroy_sound_samples(Sound_samples *sound_samples)
124 {
125     // TODO: Use a seperate callback function for audio handling and pass that into SDL_OpenAudioDevice
126     trace_assert(sound_samples);
127     trace_assert(sound_samples->dev);
128     SDL_CloseAudioDevice(sound_samples->dev);
129     RETURN_LT0(sound_samples->lt);
130 }
131
132 int sound_samples_play_sound(Sound_samples *sound_samples,
133                              size_t sound_index)
134 {
135     trace_assert(sound_samples);
136     trace_assert(sound_index < sound_samples->samples_count);
137     trace_assert(sound_samples->dev);
138     SDL_ClearQueuedAudio(sound_samples->dev);
139     if (SDL_QueueAudio(sound_samples->dev, sound_samples->audio_buf_array[sound_index], 
140                 sound_samples->audio_buf_size_array[sound_index]) < 0) {
141         log_warn("Failed to queue audio data of sound index %zu to device: %s\n", sound_index, SDL_GetError());
142         return 0;
143     }
144     SDL_PauseAudioDevice(sound_samples->dev, 0);
145     return 0;
146 }
147
148 int sound_samples_toggle_pause(Sound_samples *sound_samples)
149 {
150     trace_assert(sound_samples);
151     sound_samples->paused = !sound_samples->paused;
152     trace_assert(sound_samples->dev);
153     SDL_PauseAudioDevice(sound_samples->dev, sound_samples->paused);
154     return 0;
155 }