| 200 | constexpr uint32_t bytes_per_frame = input_channels * sizeof(float); |
| 201 | |
| 202 | void queue_samples(int16_t* audio_data, size_t sample_count) { |
| 203 | // Buffer for holding the output of swapping the audio channels. This is reused across |
| 204 | // calls to reduce runtime allocations. |
| 205 | static std::vector<float> swap_buffer; |
| 206 | static std::array<float, duplicated_input_frames * input_channels> duplicated_sample_buffer; |
| 207 | |
| 208 | // Make sure the swap buffer is large enough to hold the audio data, including any extra space needed for resampling. |
| 209 | size_t resampled_sample_count = sample_count + duplicated_input_frames * input_channels; |
| 210 | size_t max_sample_count = std::max(resampled_sample_count, resampled_sample_count * audio_convert.len_mult); |
| 211 | if (max_sample_count > swap_buffer.size()) { |
| 212 | swap_buffer.resize(max_sample_count); |
| 213 | } |
| 214 | |
| 215 | // Copy the duplicated frames from last chunk into this chunk |
| 216 | for (size_t i = 0; i < duplicated_input_frames * input_channels; i++) { |
| 217 | swap_buffer[i] = duplicated_sample_buffer[i]; |
| 218 | } |
| 219 | |
| 220 | // Convert the audio from 16-bit values to floats and swap the audio channels into the |
| 221 | // swap buffer to correct for the address xor caused by endianness handling. |
| 222 | float cur_main_volume = zelda64::get_main_volume() / 100.0f; // Get the current main volume, normalized to 0.0-1.0. |
| 223 | for (size_t i = 0; i < sample_count; i += input_channels) { |
| 224 | swap_buffer[i + 0 + duplicated_input_frames * input_channels] = audio_data[i + 1] * (1.0f / 32768.0f) * cur_main_volume; |
| 225 | swap_buffer[i + 1 + duplicated_input_frames * input_channels] = audio_data[i + 0] * (1.0f / 32768.0f) * cur_main_volume; |
| 226 | } |
| 227 | |
| 228 | // TODO handle cases where a chunk is smaller than the duplicated frame count. |
| 229 | assert(sample_count > duplicated_input_frames * input_channels); |
| 230 | |
| 231 | // Copy the last converted samples into the duplicated sample buffer to reuse in resampling the next queued chunk. |
| 232 | for (size_t i = 0; i < duplicated_input_frames * input_channels; i++) { |
| 233 | duplicated_sample_buffer[i] = swap_buffer[i + sample_count]; |
| 234 | } |
| 235 | |
| 236 | audio_convert.buf = reinterpret_cast<Uint8*>(swap_buffer.data()); |
| 237 | audio_convert.len = (sample_count + duplicated_input_frames * input_channels) * sizeof(swap_buffer[0]); |
| 238 | |
| 239 | int ret = SDL_ConvertAudio(&audio_convert); |
| 240 | |
| 241 | if (ret < 0) { |
| 242 | printf("Error using SDL audio converter: %s\n", SDL_GetError()); |
| 243 | throw std::runtime_error("Error using SDL audio converter"); |
| 244 | } |
| 245 | |
| 246 | uint64_t cur_queued_microseconds = uint64_t(SDL_GetQueuedAudioSize(audio_device)) / bytes_per_frame * 1000000 / sample_rate; |
| 247 | uint32_t num_bytes_to_queue = audio_convert.len_cvt - output_channels * discarded_output_frames * sizeof(swap_buffer[0]); |
| 248 | float* samples_to_queue = swap_buffer.data() + output_channels * discarded_output_frames / 2; |
| 249 | |
| 250 | // Prevent audio latency from building up by skipping samples in incoming audio when too many samples are already queued. |
| 251 | // Skip samples based on how many microseconds of samples are queued already. |
| 252 | uint32_t skip_factor = cur_queued_microseconds / 100000; |
| 253 | if (skip_factor != 0) { |
| 254 | uint32_t skip_ratio = 1 << skip_factor; |
| 255 | num_bytes_to_queue /= skip_ratio; |
| 256 | for (size_t i = 0; i < num_bytes_to_queue / (output_channels * sizeof(swap_buffer[0])); i++) { |
| 257 | samples_to_queue[2 * i + 0] = samples_to_queue[2 * skip_ratio * i + 0]; |
| 258 | samples_to_queue[2 * i + 1] = samples_to_queue[2 * skip_ratio * i + 1]; |
| 259 | } |