1 /*
2 * Copyright © 2013 Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
3 *
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
6 */
7
8 /* libcubeb api/function exhaustive test. Plays a series of tones in different
9 * conditions. */
10 #include "gtest/gtest.h"
11 #if !defined(_XOPEN_SOURCE)
12 #define _XOPEN_SOURCE 600
13 #endif
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <math.h>
17 #include <memory>
18 #include <string.h>
19 #include "cubeb/cubeb.h"
20 #include <string>
21
22 //#define ENABLE_NORMAL_LOG
23 //#define ENABLE_VERBOSE_LOG
24 #include "common.h"
25
26 using namespace std;
27
28 #define MAX_NUM_CHANNELS 32
29
30 #if !defined(M_PI)
31 #define M_PI 3.14159265358979323846
32 #endif
33
34 #define VOLUME 0.2
35
get_frequency(int channel_index)36 float get_frequency(int channel_index)
37 {
38 return 220.0f * (channel_index+1);
39 }
40
41 template<typename T> T ConvertSample(double input);
ConvertSample(double input)42 template<> float ConvertSample(double input) { return input; }
ConvertSample(double input)43 template<> short ConvertSample(double input) { return short(input * 32767.0f); }
44
45 /* store the phase of the generated waveform */
46 struct synth_state {
synth_statesynth_state47 synth_state(int num_channels_, float sample_rate_)
48 : num_channels(num_channels_),
49 sample_rate(sample_rate_)
50 {
51 for(int i=0;i < MAX_NUM_CHANNELS;++i)
52 phase[i] = 0.0f;
53 }
54
55 template<typename T>
runsynth_state56 void run(T* audiobuffer, long nframes)
57 {
58 for(int c=0;c < num_channels;++c) {
59 float freq = get_frequency(c);
60 float phase_inc = 2.0 * M_PI * freq / sample_rate;
61 for(long n=0;n < nframes;++n) {
62 audiobuffer[n*num_channels+c] = ConvertSample<T>(sin(phase[c]) * VOLUME);
63 phase[c] += phase_inc;
64 }
65 }
66 }
67
68 private:
69 int num_channels;
70 float phase[MAX_NUM_CHANNELS];
71 float sample_rate;
72 };
73
74 template<typename T>
data_cb(cubeb_stream *,void * user,const void *,void * outputbuffer,long nframes)75 long data_cb(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
76 {
77 synth_state *synth = (synth_state *)user;
78 synth->run((T*)outputbuffer, nframes);
79 return nframes;
80 }
81
state_cb_audio(cubeb_stream *,void *,cubeb_state)82 void state_cb_audio(cubeb_stream * /*stream*/, void * /*user*/, cubeb_state /*state*/)
83 {
84 }
85
86 /* Our android backends don't support float, only int16. */
supports_float32(string backend_id)87 int supports_float32(string backend_id)
88 {
89 return backend_id != "opensl"
90 && backend_id != "audiotrack";
91 }
92
93 /* Some backends don't have code to deal with more than mono or stereo. */
supports_channel_count(string backend_id,int nchannels)94 int supports_channel_count(string backend_id, int nchannels)
95 {
96 return nchannels <= 2 ||
97 (backend_id != "opensl" && backend_id != "audiotrack");
98 }
99
run_test(int num_channels,int sampling_rate,int is_float)100 int run_test(int num_channels, int sampling_rate, int is_float)
101 {
102 int r = CUBEB_OK;
103
104 cubeb *ctx = NULL;
105
106 r = common_init(&ctx, "Cubeb audio test: channels");
107 if (r != CUBEB_OK) {
108 fprintf(stderr, "Error initializing cubeb library\n");
109 return r;
110 }
111 std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
112 cleanup_cubeb_at_exit(ctx, cubeb_destroy);
113
114 const char * backend_id = cubeb_get_backend_id(ctx);
115
116 if ((is_float && !supports_float32(backend_id)) ||
117 !supports_channel_count(backend_id, num_channels)) {
118 /* don't treat this as a test failure. */
119 return CUBEB_OK;
120 }
121
122 fprintf(stderr, "Testing %d channel(s), %d Hz, %s (%s)\n", num_channels, sampling_rate, is_float ? "float" : "short", cubeb_get_backend_id(ctx));
123
124 cubeb_stream_params params;
125 params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
126 params.rate = sampling_rate;
127 params.channels = num_channels;
128 params.layout = CUBEB_LAYOUT_UNDEFINED;
129 params.prefs = CUBEB_STREAM_PREF_NONE;
130
131 synth_state synth(params.channels, params.rate);
132
133 cubeb_stream *stream = NULL;
134 r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, ¶ms,
135 4096, is_float ? &data_cb<float> : &data_cb<short>, state_cb_audio, &synth);
136 if (r != CUBEB_OK) {
137 fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
138 return r;
139 }
140
141 std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
142 cleanup_stream_at_exit(stream, cubeb_stream_destroy);
143
144 cubeb_stream_start(stream);
145 delay(200);
146 cubeb_stream_stop(stream);
147
148 return r;
149 }
150
run_panning_volume_test(int is_float)151 int run_panning_volume_test(int is_float)
152 {
153 int r = CUBEB_OK;
154
155 cubeb *ctx = NULL;
156
157 r = common_init(&ctx, "Cubeb audio test");
158 if (r != CUBEB_OK) {
159 fprintf(stderr, "Error initializing cubeb library\n");
160 return r;
161 }
162
163 std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
164 cleanup_cubeb_at_exit(ctx, cubeb_destroy);
165
166 const char * backend_id = cubeb_get_backend_id(ctx);
167
168 if ((is_float && !supports_float32(backend_id))) {
169 /* don't treat this as a test failure. */
170 return CUBEB_OK;
171 }
172
173 cubeb_stream_params params;
174 params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
175 params.rate = 44100;
176 params.channels = 2;
177 params.layout = CUBEB_LAYOUT_STEREO;
178 params.prefs = CUBEB_STREAM_PREF_NONE;
179
180 synth_state synth(params.channels, params.rate);
181
182 cubeb_stream *stream = NULL;
183 r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, ¶ms,
184 4096, is_float ? &data_cb<float> : &data_cb<short>,
185 state_cb_audio, &synth);
186 if (r != CUBEB_OK) {
187 fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
188 return r;
189 }
190
191 std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
192 cleanup_stream_at_exit(stream, cubeb_stream_destroy);
193
194 fprintf(stderr, "Testing: volume\n");
195 for(int i=0;i <= 4; ++i)
196 {
197 fprintf(stderr, "Volume: %d%%\n", i*25);
198
199 cubeb_stream_set_volume(stream, i/4.0f);
200 cubeb_stream_start(stream);
201 delay(400);
202 cubeb_stream_stop(stream);
203 delay(100);
204 }
205
206 fprintf(stderr, "Testing: panning\n");
207 for(int i=-4;i <= 4; ++i)
208 {
209 fprintf(stderr, "Panning: %.2f\n", i/4.0f);
210
211 cubeb_stream_set_panning(stream, i/4.0f);
212 cubeb_stream_start(stream);
213 delay(400);
214 cubeb_stream_stop(stream);
215 delay(100);
216 }
217
218 return r;
219 }
220
TEST(cubeb,run_panning_volume_test_short)221 TEST(cubeb, run_panning_volume_test_short)
222 {
223 ASSERT_EQ(run_panning_volume_test(0), CUBEB_OK);
224 }
225
TEST(cubeb,run_panning_volume_test_float)226 TEST(cubeb, run_panning_volume_test_float)
227 {
228 ASSERT_EQ(run_panning_volume_test(1), CUBEB_OK);
229 }
230
TEST(cubeb,run_channel_rate_test)231 TEST(cubeb, run_channel_rate_test)
232 {
233 unsigned int channel_values[] = {
234 1,
235 2,
236 3,
237 4,
238 6,
239 };
240
241 int freq_values[] = {
242 16000,
243 24000,
244 44100,
245 48000,
246 };
247
248 for(auto channels : channel_values) {
249 for(auto freq : freq_values) {
250 ASSERT_TRUE(channels < MAX_NUM_CHANNELS);
251 fprintf(stderr, "--------------------------\n");
252 ASSERT_EQ(run_test(channels, freq, 0), CUBEB_OK);
253 ASSERT_EQ(run_test(channels, freq, 1), CUBEB_OK);
254 }
255 }
256 }
257