-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathfaster_mixer.c
125 lines (110 loc) · 4.01 KB
/
faster_mixer.c
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
A stereo floating-point audio mixer.
by Benedict Henshaw (11/2017)
Usage:
Call mix_audio() with a pointer to your audio buffer, the number of
samples you want to be written into it.
New Features:
- Faster mixing.
Limitations:
- Only plays mono sources with panning.
- Only 32-bit float sample format.
*/
// Our main data structures are identical to the stereo mixer.
typedef struct
{
float *samples; // The audio data itself.
int sample_count; // Number of samples in the data.
int sample_index; // Index of the next sample to be played.
float left_gain; // How loud to play the sound in the left channel.
float right_gain; // Same for the right channel.
int loop; // If the sound should repeat.
} Mixer_Channel;
typedef struct
{
Mixer_Channel *channels;
int channel_count;
float gain;
} Mixer;
Mixer create_mixer(int channel_count, float gain)
{
Mixer mixer = {};
mixer.channels = calloc(channel_count, sizeof(Mixer_Channel));
if (mixer.channels)
{
mixer.channel_count = channel_count;
mixer.gain = gain;
}
return mixer;
}
void mix_audio(Mixer *mixer, void *stream, int samples_requested)
{
float *samples = (float *)stream;
// A small change has been made here so that the mixer reads consecutive
// samples from each channel, instead of one sample from each channel at a
// time. Accessing adjacent memory is much faster than jumping to memory
// that is further away. This is due to the way caching is done by the CPU.
// We will need to zero our whole stream first so that, in the case where no
// sound is playing or there isn't enough to fill all the requested samples,
// we will not leave garbage data in the buffer.
for (int sample_index = 0; sample_index < samples_requested; ++sample_index)
{
samples[sample_index] = 0.0f;
}
// This time we will go through each channel, then mix all of that channel's
// samples into the output buffer in one go. Most of this should be familiar
// if you have read the previous mixer examples.
for (int channel_index = 0; channel_index < mixer->channel_count; ++channel_index)
{
Mixer_Channel *channel = &mixer->channels[channel_index];
if (channel->samples)
{
for (int sample_index = 0;
sample_index < samples_requested &&
channel->sample_index < channel->sample_count;
++sample_index)
{
float new_left = channel->samples[channel->sample_index];
float new_right = channel->samples[channel->sample_index];
new_left *= channel->left_gain;
new_left *= mixer->gain;
new_right *= channel->right_gain;
new_right *= mixer->gain;
samples[sample_index] += new_left;
++sample_index;
samples[sample_index] += new_right;
channel->sample_index += 1;
}
// This check can now be made after the main mixing loop.
if (channel->sample_index >= channel->sample_count)
{
if (channel->loop)
{
channel->sample_index = 0;
}
else
{
*channel = (Mixer_Channel){};
}
}
}
}
}
int play_audio(Mixer *mixer, void *stream, int sample_count,
float left_gain, float right_gain, int loop)
{
for (int i = 0; i < mixer->channel_count; ++i)
{
if (mixer->channels[i].samples == NULL)
{
mixer->channels[i].samples = stream;
mixer->channels[i].sample_count = sample_count;
mixer->channels[i].sample_index = 0;
mixer->channels[i].left_gain = left_gain;
mixer->channels[i].right_gain = right_gain;
mixer->channels[i].loop = loop;
return i;
}
}
return -1;
}