-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathOutput.cpp
106 lines (79 loc) · 2.86 KB
/
Output.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include "Output.hpp"
#include <SDL.h>
#include <list>
#include <cassert>
#include <exception>
#include <iostream>
#include <algorithm>
//local (to this file) data used by the audio system:
namespace {
//handy constants:
constexpr uint32_t const MIX_SAMPLES = 1024; //number of samples to mix per call of mix_audio callback; n.b. SDL requires this to be a power of two
//The audio device:
SDL_AudioDeviceID device = 0;
}
//public-facing data:
std::vector< Output::Sample > Output::playing_data;
uint32_t Output::playing_position = 0;
float Output::volume = 0.5f;
//This audio-mixing callback is defined below:
void mix_audio(void *, Uint8 *buffer_, int len);
//------------------------ public-facing --------------------------------
void Output::init() {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
std::cerr << "Failed to initialize SDL audio subsytem:\n" << SDL_GetError() << std::endl;
std::cerr << " (Will continue without audio.)\n" << std::endl;
return;
}
//Based on the example on https://wiki.libsdl.org/SDL_OpenAudioDevice
SDL_AudioSpec want, have;
SDL_zero(want);
want.freq = SampleRate;
want.format = AUDIO_F32SYS;
want.channels = 2;
want.samples = MIX_SAMPLES;
want.callback = mix_audio;
device = SDL_OpenAudioDevice(nullptr, 0, &want, &have, 0);
if (device == 0) {
std::cerr << "Failed to open audio device:\n" << SDL_GetError() << std::endl;
std::cerr << " (Will continue without audio.)\n" << std::endl;
} else {
//start audio playback:
SDL_PauseAudioDevice(device, 0);
std::cout << "Audio output initialized." << std::endl;
}
}
void Output::shutdown() {
if (device != 0) {
//stop audio playback:
SDL_PauseAudioDevice(device, 1);
SDL_CloseAudioDevice(device);
device = 0;
}
}
void Output::lock() {
if (device) SDL_LockAudioDevice(device);
}
void Output::unlock() {
if (device) SDL_UnlockAudioDevice(device);
}
//------------------------ internals --------------------------------
//The audio callback -- invoked by SDL when it needs more sound to play:
void mix_audio(void *, Uint8 *buffer_, int len) {
assert(buffer_); //should always have some audio buffer
static_assert(sizeof(Output::Sample) == 8, "Output::Sample is packed");
assert(len == MIX_SAMPLES * sizeof(Output::Sample)); //should always have the expected number of samples
Output::Sample *buffer = reinterpret_cast< Output::Sample * >(buffer_);
uint32_t begin = Output::playing_position;
uint32_t end = std::min< uint32_t >(begin + MIX_SAMPLES, Output::playing_data.size());
begin = std::min(begin, end);
for (uint32_t i = begin; i < end; ++i) {
buffer[i-begin].l = Output::volume * Output::playing_data[i].l;
buffer[i-begin].r = Output::volume * Output::playing_data[i].r;
}
uint32_t written = end - begin;
if (written < MIX_SAMPLES) {
memset(buffer + written, 0, (MIX_SAMPLES - written) * sizeof(Output::Sample));
}
Output::playing_position += written;
}