OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2008 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can | |
4 * be found in the LICENSE file. | |
5 */ | |
6 | |
7 // NaCl audio tone demo | |
8 // render a simple tone to the audio device for a couple seconds | |
9 | |
10 #include <errno.h> | |
11 #include <math.h> | |
12 #include <pthread.h> | |
13 #include <stdint.h> | |
14 #include <stdio.h> | |
15 #include <stdlib.h> | |
16 #include <string.h> | |
17 #include <time.h> | |
18 #include <unistd.h> | |
19 #include <sys/time.h> | |
20 #if !defined(STANDALONE) | |
21 #include <nacl/nacl_av.h> | |
22 #endif | |
23 #if defined(STANDALONE) | |
24 #include "native_client/common/standalone.h" | |
25 #endif | |
26 | |
27 const int kMaxDuration = 60; | |
28 const int kMaxFrequency = 24000; | |
29 const int kMaxAmplitude = 32000; | |
30 const int kSampling = 48000; | |
31 const int kDesiredSampleRate = 4096; | |
32 const int kMaxDemoLoop = 500; | |
33 const int kMinAmplitudeCutoff = 200; | |
34 const float kRampTime = 0.25f; | |
35 const float kPI = 3.14159265f; | |
36 int g_duration = 3; | |
37 int g_frequency = 1000; | |
38 int g_amplitude = 16000; | |
39 int g_loopcount = 1; | |
40 | |
41 struct AudioControl { | |
42 int uid; | |
43 volatile bool running; | |
44 volatile bool enable_output; | |
45 pthread_t thread_id; | |
46 int sample_rate; | |
47 pthread_mutex_t start_barrier; | |
48 }; | |
49 | |
50 int global_audio_thread_exit = 0; | |
51 | |
52 | |
53 double gettimed() { | |
54 timeval tv; | |
55 double sec, usec; | |
56 const float kUSec = 1.e6f; | |
57 gettimeofday(&tv, NULL); | |
58 sec = static_cast<double>(tv.tv_sec); | |
59 usec = static_cast<double>(tv.tv_usec); | |
60 return (sec * kUSec + usec) / kUSec; | |
61 } | |
62 | |
63 | |
64 // pumps the audio via nacl_audio_update | |
65 // this is a very simple sine wave generator, with no mixing | |
66 void* AudioThread(void *userdata) { | |
67 AudioControl *ac = reinterpret_cast<AudioControl *>(userdata); | |
68 int16_t sbuffer[kNaClAudioBufferLength]; | |
69 size_t count = 0; | |
70 float delta = static_cast<float>(g_frequency) * (2.0f * kPI / kSampling); | |
71 bool to_silence = false; | |
72 bool from_silence = false; | |
73 float grad = 0.0f, fclock = 0.0f, amplitude = 0.0f; | |
74 | |
75 printf("Audio thread: Starting up...\n"); | |
76 printf("Audio thread: uid = 0x%0X\n", ac->uid); | |
77 printf("Audio thread: running = %s\n", ac->running ? "yes" : "no"); | |
78 // This lock/unlock will stall until main thread releases lock. | |
79 // We do this because we can't call nacl_audio_update until | |
80 // after the audio system has been successfully initialized. | |
81 pthread_mutex_lock(&ac->start_barrier); | |
82 pthread_mutex_unlock(&ac->start_barrier); | |
83 printf("Audio thread: Entering output loop\n"); | |
84 while (ac->running) { | |
85 bool last_enable = ac->enable_output; | |
86 if (0 != nacl_audio_stream(sbuffer, &count)) | |
87 break; | |
88 bool now_enable = ac->enable_output; | |
89 // eliminate audio pops at post-startup and pre-shutdown | |
90 if ((last_enable) && (!now_enable)) { | |
91 // shutting down to silence | |
92 to_silence = true; | |
93 from_silence = false; | |
94 grad = 1.0f; | |
95 } else if ((!last_enable) && (now_enable)) { | |
96 // starting up, reset sine wave clock | |
97 fclock = 0.0f; | |
98 from_silence = true; | |
99 to_silence = false; | |
100 grad = 1.0f; | |
101 } | |
102 // output to buffer | |
103 count = count / 2; | |
104 for (size_t i = 0; i < count; i += 2) { | |
105 // sine wave | |
106 int16_t wave = static_cast<int16_t>(sinf(fclock) * amplitude); | |
107 // left channel | |
108 sbuffer[i+0] = wave; | |
109 // right channel | |
110 sbuffer[i+1] = wave; | |
111 // increment fclock by delta | |
112 fclock += delta; | |
113 // keep fclock within -2PI..2PI | |
114 if (fclock > 2.0f * kPI) | |
115 fclock = fclock - (2.0f * kPI); | |
116 // ramp up / ramp down amplitude in transitions | |
117 if (to_silence) { | |
118 grad *= 0.99f; | |
119 // ramp down amplitude | |
120 amplitude = grad * g_amplitude; | |
121 // if the wave gets close to 0, cut it | |
122 if (abs(wave) < kMinAmplitudeCutoff) | |
123 grad = 0.0f; | |
124 } else if (from_silence) { | |
125 grad *= 0.99f; | |
126 // ramp up amplitude | |
127 amplitude = (1.0f - grad) * g_amplitude; | |
128 } | |
129 } | |
130 } | |
131 printf("Audio thread: Shutting down...\n"); | |
132 global_audio_thread_exit++; | |
133 return NULL; | |
134 } | |
135 | |
136 | |
137 // Initializes the audio system with the desired sample rate. | |
138 // (The audio system might choose a different sample rate.) | |
139 int InitAudioDemo(AudioControl *ac, int desired_samples) { | |
140 int r; | |
141 void *thread_result; | |
142 | |
143 // init basic audio controller | |
144 ac->uid = 0xC0DE5EED; | |
145 ac->running = true; | |
146 ac->enable_output = false; | |
147 pthread_mutex_init(&ac->start_barrier, NULL); | |
148 pthread_mutex_lock(&ac->start_barrier); | |
149 | |
150 // create audio thread | |
151 printf("Main thread: Spawning audio thread...\n"); | |
152 int p = pthread_create(&ac->thread_id, NULL, AudioThread, ac); | |
153 if (0 != p) { | |
154 printf("Main thread: Unable to create audio thread!\n"); | |
155 pthread_mutex_unlock(&ac->start_barrier); | |
156 nacl_audio_shutdown(); | |
157 nacl_multimedia_shutdown(); | |
158 return -1; | |
159 } | |
160 | |
161 // initialize multimedia | |
162 r = nacl_multimedia_init(NACL_SUBSYSTEM_AUDIO); | |
163 if (r != 0) { | |
164 printf("Main thread: Couldn't init multimedia w/ audio\n"); | |
165 ac->running = false; | |
166 pthread_mutex_unlock(&ac->start_barrier); | |
167 pthread_join(ac->thread_id, &thread_result); | |
168 return -1; | |
169 } | |
170 | |
171 // Open the audio device | |
172 printf("Main thread: Desired samples: %d\n", desired_samples); | |
173 r = nacl_audio_init(NACL_AUDIO_FORMAT_STEREO_48K, | |
174 desired_samples, &ac->sample_rate); | |
175 if (r != 0) { | |
176 printf("Main thread: Couldn't init nacl audio\n"); | |
177 nacl_multimedia_shutdown(); | |
178 pthread_mutex_unlock(&ac->start_barrier); | |
179 ac->running = false; | |
180 pthread_join(ac->thread_id, &thread_result); | |
181 return -1; | |
182 } | |
183 printf("Main thread: Obtained samples: %d\n", ac->sample_rate); | |
184 | |
185 // Audio thread can go ahead and start now | |
186 pthread_mutex_unlock(&ac->start_barrier); | |
187 | |
188 return 0; | |
189 } | |
190 | |
191 | |
192 void Sleep(double x) { | |
193 double start, current; | |
194 start = gettimed(); | |
195 while (true) { | |
196 current = gettimed(); | |
197 if ((current - start) > x) break; | |
198 } | |
199 } | |
200 | |
201 | |
202 // Runs the demo loop for a few seconds... | |
203 void RunAudioDemo(AudioControl *ac) { | |
204 // the audio is being continuously output by the AudioThread. | |
205 // So we can just wait g_duration seconds here and do nothing... | |
206 // (we'll be rude and ask for timeofday over and over | |
207 // to exercise the cpu and memory while audio is playing.) | |
208 printf("Main thread: Doing something for a few seconds\n"); | |
209 Sleep(0.2f); | |
210 ac->enable_output = true; | |
211 Sleep(static_cast<float>(g_duration)); | |
212 ac->enable_output = false; | |
213 Sleep(0.2f); | |
214 } | |
215 | |
216 | |
217 void ShutdownAudioDemo(AudioControl *ac) { | |
218 void *thread_result; | |
219 // tell audio thread to exit | |
220 ac->running = false; | |
221 pthread_join(ac->thread_id, &thread_result); | |
222 nacl_audio_shutdown(); | |
223 nacl_multimedia_shutdown(); | |
224 printf("Main thread: Exited gracefully\n"); | |
225 } | |
226 | |
227 | |
228 int ToneTest() { | |
229 AudioControl ac; | |
230 if (0 == InitAudioDemo(&ac, kDesiredSampleRate)) { | |
231 RunAudioDemo(&ac); | |
232 ShutdownAudioDemo(&ac); | |
233 printf("TEST PASSED\n"); | |
234 return 0; | |
235 } else { | |
236 printf("Main thread: Unable to init audio\n"); | |
237 return -1; | |
238 } | |
239 } | |
240 | |
241 | |
242 // If user specifies options on cmd line, parse them | |
243 // here and update global settings as needed. | |
244 void ParseCmdLineArgs(int argc, char **argv) { | |
245 // look for cmd line args | |
246 if (argc > 1) { | |
247 for (int i = 1; i < argc; ++i) { | |
248 if (argv[i] == strstr(argv[i], "-d")) { | |
249 int d = atoi(&argv[i][2]); | |
250 if ((d > 0) && (d < kMaxDuration)) { | |
251 g_duration = d; | |
252 } | |
253 } else if (argv[i] == strstr(argv[i], "-a")) { | |
254 int a = atoi(&argv[i][2]); | |
255 if ((a > 0) && (a < kMaxAmplitude)) { | |
256 g_amplitude = a; | |
257 } | |
258 } else if (argv[i] == strstr(argv[i], "-f")) { | |
259 int f = atoi(&argv[i][2]); | |
260 if ((f > 0) && (f < kMaxFrequency)) { | |
261 g_frequency = f; | |
262 } | |
263 } else if (argv[i] == strstr(argv[i], "-c")) { | |
264 int c = atoi(&argv[i][2]); | |
265 if ((c > 0) && (c < kMaxDemoLoop)) { | |
266 g_loopcount = c; | |
267 } | |
268 } else { | |
269 printf("Tone SDL Demo\n"); | |
270 printf("usage: -f<n> output tone at frequency n.\n"); | |
271 printf(" -a<n> amplitude\n"); | |
272 printf(" -d<n> duration of n seconds.\n"); | |
273 printf(" -c<n> demo loop count.\n"); | |
274 printf(" --help show this screen.\n"); | |
275 exit(0); | |
276 } | |
277 } | |
278 } | |
279 } | |
280 | |
281 | |
282 // Parses cmd line options, initializes surface, runs the demo & shuts down. | |
283 int main(int argc, char **argv) { | |
284 ParseCmdLineArgs(argc, argv); | |
285 for (int i = 0; i < g_loopcount; ++i) { | |
286 printf("Test %d, %d:\n", i, global_audio_thread_exit); | |
287 ToneTest(); | |
288 } | |
289 return 0; | |
290 } | |
OLD | NEW |