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, &params,
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_volume_test(int is_float)151 int run_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, &params,
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   return r;
207 }
208 
TEST(cubeb,run_volume_test_short)209 TEST(cubeb, run_volume_test_short)
210 {
211   ASSERT_EQ(run_volume_test(0), CUBEB_OK);
212 }
213 
TEST(cubeb,run_volume_test_float)214 TEST(cubeb, run_volume_test_float)
215 {
216   ASSERT_EQ(run_volume_test(1), CUBEB_OK);
217 }
218 
TEST(cubeb,run_channel_rate_test)219 TEST(cubeb, run_channel_rate_test)
220 {
221   unsigned int channel_values[] = {
222     1,
223     2,
224     3,
225     4,
226     6,
227   };
228 
229   int freq_values[] = {
230     16000,
231     24000,
232     44100,
233     48000,
234   };
235 
236   for(auto channels : channel_values) {
237     for(auto freq : freq_values) {
238       ASSERT_TRUE(channels < MAX_NUM_CHANNELS);
239       fprintf(stderr, "--------------------------\n");
240       ASSERT_EQ(run_test(channels, freq, 0), CUBEB_OK);
241       ASSERT_EQ(run_test(channels, freq, 1), CUBEB_OK);
242     }
243   }
244 }
245