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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
use std::time::Duration;
use fixed_resample::{
audioadapter_buffers::direct::SequentialSliceOfVecs, PushStatus, ReadStatus,
ResamplingChannelConfig,
};
const IN_SAMPLE_RATE: u32 = 44100;
const OUT_SAMPLE_RATE: u32 = 48000;
const BLOCK_FRAMES: usize = 1024;
const FREQ_HZ: f32 = 440.0;
const GAIN: f32 = 0.5;
const NUM_CHANNELS: usize = 2;
const NON_RT_POLL_INTERVAL: Duration = Duration::from_millis(15);
pub fn main() {
// Note, you can also have the producer end be in a realtime context and the
// consumer end in a non-realtime context (and vice-versa).
// --- Realtime & interleaved example ---------------------------------------------------
let (mut prod1, mut cons1) = fixed_resample::resampling_channel::<f32>(
NUM_CHANNELS,
IN_SAMPLE_RATE,
OUT_SAMPLE_RATE,
true,
Default::default(), // default configuration
);
let in_stream_interval = Duration::from_secs_f64(BLOCK_FRAMES as f64 / IN_SAMPLE_RATE as f64);
let out_stream_interval = Duration::from_secs_f64(BLOCK_FRAMES as f64 / OUT_SAMPLE_RATE as f64);
// Simulate a realtime input stream (i.e. a CPAL input stream).
std::thread::spawn(move || {
let mut phasor: f32 = 0.0;
let phasor_inc: f32 = FREQ_HZ / IN_SAMPLE_RATE as f32;
let mut interleaved_in_buf = vec![0.0; BLOCK_FRAMES * NUM_CHANNELS];
loop {
// Generate a sine wave on all channels.
for chunk in interleaved_in_buf.chunks_exact_mut(NUM_CHANNELS) {
let val = (phasor * std::f32::consts::TAU).sin() * GAIN;
phasor = (phasor + phasor_inc).fract();
for s in chunk.iter_mut() {
*s = val;
}
}
let status = prod1.push_interleaved(&interleaved_in_buf);
match status {
// All samples were successfully pushed to the channel.
PushStatus::Ok => {}
// The output stream is not yet ready to read samples from the channel. No
// samples have been pushed to the channel.
PushStatus::OutputNotReady => {}
// An overflow occured due to the input stream running faster than the output
// stream.
PushStatus::OverflowOccurred { num_frames_pushed } => {
println!(
"Overflow occured in channel 1! Number of input frames dropped: {}",
BLOCK_FRAMES - num_frames_pushed
);
}
// An underflow occured due to the output stream running faster than the
// input stream.
//
// All of the samples were successfully pushed to the channel, however extra
// zero samples were also pushed to the channel to correct for the jitter.
//
// Note, when compiled in debug mode without optimizations, the resampler
// is quite slow, leading to frequent underflows in this example.
PushStatus::UnderflowCorrected {
num_zero_frames_pushed,
} => {
println!(
"Underflow occured in channel 1! Number of zero frames added to channel: {}",
num_zero_frames_pushed
);
}
}
spin_sleep::sleep(in_stream_interval);
}
});
// Simulate a realtime output stream (i.e. a CPAL output stream).
std::thread::spawn(move || {
let mut interleaved_out_buf = vec![0.0; BLOCK_FRAMES * NUM_CHANNELS];
loop {
let status = cons1.read_interleaved(&mut interleaved_out_buf, false);
match status {
// The output buffer was fully filled with samples from the channel.
ReadStatus::Ok => {}
// The input stream is not yet ready to push samples to the channel.
// The output buffer was filled with zeros.
ReadStatus::InputNotReady => {}
// An underflow occured due to the output stream running faster than the input
// stream.
//
// Note, when compiled in debug mode without optimizations, the resampler
// is quite slow, leading to frequent underflows in this example.
ReadStatus::UnderflowOccurred { num_frames_read } => {
println!(
"Underflow occured in channel 1! Number of output frames dropped: {}",
BLOCK_FRAMES - num_frames_read
);
}
// An overflow occured due to the input stream running faster than the output
// stream
//
// All of the samples in the output buffer were successfully filled with samples,
// however a number of frames have also been discarded to correct for the jitter.
ReadStatus::OverflowCorrected {
num_frames_discarded,
} => {
println!(
"Overflow occured in channel 1! Number of frames discarded from channel: {}",
num_frames_discarded
);
}
}
spin_sleep::sleep(out_stream_interval);
}
});
// --- Non-realtime & de-interleaved example --------------------------------------------
let (mut prod2, mut cons2) = fixed_resample::resampling_channel::<f32>(
NUM_CHANNELS,
IN_SAMPLE_RATE,
OUT_SAMPLE_RATE,
false,
ResamplingChannelConfig {
// In this channel we are pushing packets of data that are 1 second long,
// so we need to increase the capacity of this channel to be at least
// twice that.
capacity_seconds: 2.0,
// In this channel we are reading packets of data that are a quarter second
// long, so we need to increase the latency of the channel to be at least
// that (here we go for twice that to be safe).
latency_seconds: 0.5,
// Because the producer end is being used in a non-realtime context, disable
// automatic overflow correction.
overflow_autocorrect_percent_threshold: None,
// Because the consumer end is being used in a non-realtime context, disable
// automatic underflow correction.
underflow_autocorrect_percent_threshold: None,
..Default::default()
},
);
// When a producer or consumer end is being used in a non-realtime context, manually set
// that end as ready so that the other end can push/read immediately without waiting for
// the other end to be ready.
prod2.set_input_stream_ready(true);
cons2.set_output_stream_ready(true);
// Simulate a non-realtime input stream (i.e. streaming data from a network).
std::thread::spawn(move || {
let mut phasor: f32 = 0.0;
let phasor_inc: f32 = FREQ_HZ / IN_SAMPLE_RATE as f32;
// The amount of frames in 1 second.
let packet_frames = IN_SAMPLE_RATE as usize;
let mut deinterleaved_in_buf: Vec<Vec<f32>> = (0..NUM_CHANNELS)
.map(|_| vec![0.0; packet_frames])
.collect();
loop {
// Detect when a new packet of data should be pushed.
//
// Alternatively you could do:
// while prod2.available_frames() >= packet_frames {
while prod2.occupied_seconds() < prod2.latency_seconds() {
// Generate a sine wave on all channels.
for i in 0..packet_frames {
let val = (phasor * std::f32::consts::TAU).sin() * GAIN;
phasor = (phasor + phasor_inc).fract();
for ch in deinterleaved_in_buf.iter_mut() {
ch[i] = val;
}
}
// Push a new packet of data to the stream.
prod2.push(
&SequentialSliceOfVecs::new(&deinterleaved_in_buf, NUM_CHANNELS, packet_frames)
.unwrap(),
None,
None,
);
}
std::thread::sleep(NON_RT_POLL_INTERVAL);
}
});
// Simulate a non-realtime output stream (i.e. streaming data to a network).
std::thread::spawn(move || {
loop {
// The amount of frames in 1/4 of a second.
let packet_frames = OUT_SAMPLE_RATE as usize / 4;
let mut deinterleaved_out_buf: Vec<Vec<f32>> = (0..NUM_CHANNELS)
.map(|_| vec![0.0; packet_frames])
.collect();
while cons2.available_frames() >= packet_frames {
let status = cons2.read(
&mut SequentialSliceOfVecs::new_mut(
&mut deinterleaved_out_buf,
NUM_CHANNELS,
packet_frames,
)
.unwrap(),
None,
None,
false,
);
if let ReadStatus::UnderflowOccurred { num_frames_read } = status {
println!(
"Underflow occured in channel 2! Number of frames dropped {}",
packet_frames - num_frames_read
);
}
}
std::thread::sleep(NON_RT_POLL_INTERVAL);
}
});
// Run for 10 seconds before closing.
std::thread::sleep(std::time::Duration::from_secs(10));
}