xref: /haiku/src/tests/add-ons/kernel/drivers/audio/multi_audio_test.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
1 /*
2  * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <errno.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <math.h>
13 
14 #include <hmulti_audio.h>
15 
16 #include "argv.h"
17 
18 
19 #define MAX_CONTROLS	128
20 #define MAX_CHANNELS	32
21 #define NUM_BUFFERS		16
22 
23 const uint32 kSampleRates[] = {
24 	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100,
25 	48000, 64000, 88200, 96000, 176400, 192000, 384000, 1536000
26 };
27 const struct {
28 	uint32	type;
29 	const char*	name;
30 } kFormats[] = {
31 	{B_FMT_8BIT_S, "8bit"},
32 	{B_FMT_8BIT_U, "8bit-unsigned"},
33 	{B_FMT_16BIT, "16bit"},
34 	{B_FMT_18BIT, "18bit"},
35 	{B_FMT_20BIT, "20bit"},
36 	{B_FMT_24BIT, "24bit"},
37 	{B_FMT_32BIT, "32bit"},
38 	{B_FMT_FLOAT, "float"},
39 	{B_FMT_DOUBLE, "double"}
40 };
41 
42 
43 struct cmd_entry {
44 	const char*	name;
45 	void		(*func)(int argc, char **argv);
46 	const char*	help;
47 };
48 
49 
50 extern const char* __progname;
51 
52 static void do_help(int argc, char** argv);
53 
54 
55 static int sDevice;
56 static multi_channel_info sChannelInfo[MAX_CHANNELS];
57 static multi_description sDescription;
58 static uint32 sRate = B_SR_48000;
59 static uint32 sFormat = B_FMT_32BIT;
60 static uint32 sEnabledChannels = ~0;
61 
62 
63 static uint32
64 channel_count()
65 {
66 	return sDescription.output_channel_count + sDescription.input_channel_count;
67 }
68 
69 
70 static void
71 set_frame(char* frame, uint32 format, float value)
72 {
73 	switch (format) {
74 		case B_FMT_8BIT_U:
75 			*(uint8*)frame = uint8(value * INT8_MAX) + 128;
76 			break;
77 		case B_FMT_8BIT_S:
78 			*(int8*)frame = int8(value * INT8_MAX);
79 			break;
80 		case B_FMT_16BIT:
81 			*(int16*)frame = int16(value * INT16_MAX);
82 			break;
83 		case B_FMT_18BIT:
84 		case B_FMT_20BIT:
85 		case B_FMT_24BIT:
86 		case B_FMT_32BIT:
87 			*(int32*)frame = int32(value * INT32_MAX);
88 			break;
89 		default:
90 			*(float*)frame = value;
91 			break;
92 	}
93 }
94 
95 
96 static uint32
97 get_rate(uint32 rateBits)
98 {
99 	uint32 rate = 0;
100 	for (uint32 i = 0; (1UL << i) <= rateBits
101 			&& i < sizeof(kSampleRates) / sizeof(kSampleRates[0]); i++) {
102 		if (((1 << i) & rateBits) != 0)
103 			rate = kSampleRates[i];
104 	}
105 
106 	return rate;
107 }
108 
109 
110 static void
111 print_rates(uint32 rateBits)
112 {
113 	for (uint32 i = 0; i < sizeof(kSampleRates) / sizeof(kSampleRates[0]);
114 			i++) {
115 		if (((1 << i) & rateBits) != 0)
116 			printf("  %lu", kSampleRates[i]);
117 	}
118 	putchar('\n');
119 }
120 
121 
122 static const char*
123 get_format_name(uint32 format)
124 {
125 	for (uint32 i = 0; i < sizeof(kFormats) / sizeof(kFormats[0]); i++) {
126 		if (kFormats[i].type == format)
127 			return kFormats[i].name;
128 	}
129 
130 	return "unknown";
131 }
132 
133 
134 static void
135 print_formats(uint32 formatBits)
136 {
137 	for (uint32 i = 0; i < sizeof(kFormats) / sizeof(kFormats[0]); i++) {
138 		if ((kFormats[i].type & formatBits) != 0)
139 			printf("  %s", kFormats[i].name);
140 	}
141 	putchar('\n');
142 }
143 
144 
145 static const char*
146 get_kind_name(uint32 kind)
147 {
148 	switch (kind) {
149 		case B_MULTI_OUTPUT_CHANNEL:
150 			return "out";
151 		case B_MULTI_INPUT_CHANNEL:
152 			return "in";
153 		case B_MULTI_OUTPUT_BUS:
154 			return "bus-out";
155 		case B_MULTI_INPUT_BUS:
156 			return "bus-in";
157 		case B_MULTI_AUX_BUS:
158 			return "bus-aux";
159 
160 		default:
161 			return "unknown";
162 	}
163 }
164 
165 
166 //	#pragma mark - Commands
167 
168 
169 static void
170 do_rate(int argc, char** argv)
171 {
172 	if (argc > 1) {
173 		uint32 rate = strtoul(argv[1], NULL, 0);
174 		uint32 bits = 0;
175 
176 		for (uint32 i = 0; i < sizeof(kSampleRates) / sizeof(kSampleRates[0]);
177 				i++) {
178 			if (rate == kSampleRates[i])
179 				bits = 1 << i;
180 		}
181 
182 		if (bits == 0) {
183 			fprintf(stderr, "Invalid sample rate %ld!\n", rate);
184 			printf("Valid values are:");
185 			for (uint32 i = 0;
186 					i < sizeof(kSampleRates) / sizeof(kSampleRates[0]); i++) {
187 				printf(" %lu", kSampleRates[i]);
188 			}
189 			putchar('\n');
190 			return;
191 		}
192 		sRate = bits;
193 	}
194 
195 	printf("Current sample rate is %lu Hz (0x%lx)\n", get_rate(sRate), sRate);
196 }
197 
198 
199 static void
200 do_format(int argc, char** argv)
201 {
202 	int32 i = -1;
203 	if (argc == 2)
204 		i = strtoll(argv[1], NULL, 0);
205 
206 	if (i < 1 || i > (int32)(sizeof(kFormats) / sizeof(kFormats[0]))) {
207 		if (argc == 2)
208 			fprintf(stderr, "Invalid format: %ld\n", i);
209 
210 		for (uint32 i = 0; i < sizeof(kFormats) / sizeof(kFormats[0]); i++) {
211 			printf("[%ld] %s\n", i + 1, kFormats[i].name);
212 		}
213 	} else {
214 		sFormat = kFormats[i - 1].type;
215 	}
216 
217 	printf("Current sample format is %s (0x%lx)\n", get_format_name(sFormat),
218 		sFormat);
219 }
220 
221 
222 static void
223 do_desc(int argc, char** argv)
224 {
225 	printf("friendly name:\t\t\t%s\n", sDescription.friendly_name);
226 	printf("vendor:\t\t\t\t%s\n\n", sDescription.vendor_info);
227 
228 	printf("output rates:\t\t\t0x%lx\n", sDescription.output_rates);
229 	print_rates(sDescription.output_rates);
230 	printf("input rates:\t\t\t0x%lx\n", sDescription.input_rates);
231 	print_rates(sDescription.input_rates);
232 	printf("max cont. var. sample rate:\t%.0f\n",
233 		sDescription.max_cvsr_rate);
234 	printf("min cont. var. sample rate:\t%.0f\n",
235 		sDescription.min_cvsr_rate);
236 	printf("output formats:\t\t\t0x%lx\n", sDescription.output_formats);
237 	print_formats(sDescription.output_formats);
238 	printf("input formats:\t\t\t0x%lx\n", sDescription.input_formats);
239 	print_formats(sDescription.input_formats);
240 	printf("lock sources:\t\t\t0x%lx\n", sDescription.lock_sources);
241 	printf("timecode sources:\t\t0x%lx\n", sDescription.timecode_sources);
242 	printf("interface flags:\t\t0x%lx\n", sDescription.interface_flags);
243 	printf("control panel string:\t\t\t%s\n", sDescription.control_panel);
244 
245 	printf("\nchannels:\n");
246 	printf("  %ld outputs, %ld inputs\n", sDescription.output_channel_count,
247 		sDescription.input_channel_count);
248 	printf("  %ld out busses, %ld in busses\n",
249 		sDescription.output_bus_channel_count,
250 		sDescription.input_bus_channel_count);
251 	printf("\n  ID\tkind\t   designations\tconnectors\n");
252 
253 	for (uint32 i = 0 ; i < channel_count(); i++) {
254 		printf("%4ld\t%-10s 0x%lx\t0x%lx\n", sDescription.channels[i].channel_id,
255 			get_kind_name(sDescription.channels[i].kind),
256 			sDescription.channels[i].designations,
257 			sDescription.channels[i].connectors);
258 	}
259 }
260 
261 
262 static void
263 do_channels(int argc, char** argv)
264 {
265 	if (argc == 2)
266 		sEnabledChannels = strtoul(argv[1], NULL, 0);
267 
268 	uint32 enabled = ((1 << channel_count()) - 1) & sEnabledChannels;
269 
270 	printf("%ld channels:\n  ", channel_count());
271 	for (uint32 i = 0; i < channel_count(); i++) {
272 		printf(enabled & 1 ? "x" : "-");
273 		enabled >>= 1;
274 	}
275 	putchar('\n');
276 }
277 
278 
279 static void
280 do_play(int argc, char** argv)
281 {
282 	uint32 playMask = 0xffffffff;
283 	if (argc == 2)
284 		playMask = strtoul(argv[1], NULL, 0);
285 
286 	multi_channel_enable channelEnable;
287 	uint32 enabled = ((1 << channel_count()) - 1) & sEnabledChannels;
288 
289 	channelEnable.enable_bits = (uchar*)&enabled;
290 	channelEnable.lock_source = B_MULTI_LOCK_INTERNAL;
291 	if (ioctl(sDevice, B_MULTI_SET_ENABLED_CHANNELS, &channelEnable,
292 			sizeof(multi_channel_enable)) < B_OK) {
293 		fprintf(stderr, "Setting enabled channels failed: %s\n",
294 			strerror(errno));
295 	}
296 
297 	multi_format_info formatInfo;
298 	formatInfo.info_size = sizeof(multi_format_info);
299 	formatInfo.output.rate = sRate;
300 	formatInfo.output.cvsr = 0;
301 	formatInfo.output.format = sFormat;
302 	formatInfo.input.rate = formatInfo.output.rate;
303 	formatInfo.input.cvsr = formatInfo.output.cvsr;
304 	formatInfo.input.format = formatInfo.output.format;
305 
306 	if (ioctl(sDevice, B_MULTI_SET_GLOBAL_FORMAT, &formatInfo,
307 			sizeof(multi_format_info)) < B_OK) {
308 		printf("Setting global format failed: %s\n", strerror(errno));
309 	}
310 
311 	if (ioctl(sDevice, B_MULTI_GET_GLOBAL_FORMAT, &formatInfo,
312 			sizeof(multi_format_info)) < B_OK) {
313 		printf("Getting global format failed: %s\n", strerror(errno));
314 	}
315 
316 	printf("format %s (0x%lx)\n", get_format_name(formatInfo.output.format),
317 		formatInfo.output.format);
318 	printf("sample rate %lu (0x%lx)\n", get_rate(formatInfo.output.rate),
319 		formatInfo.output.rate);
320 
321 	buffer_desc playBuffers[NUM_BUFFERS * MAX_CHANNELS];
322 	buffer_desc recordBuffers[NUM_BUFFERS * MAX_CHANNELS];
323 	buffer_desc* playBufferDesc[NUM_BUFFERS];
324 	buffer_desc* recordBufferDesc[NUM_BUFFERS];
325 
326 	for (uint32 i = 0; i < NUM_BUFFERS; i++) {
327 		playBufferDesc[i] = &playBuffers[i * MAX_CHANNELS];
328 		recordBufferDesc[i] = &recordBuffers[i * MAX_CHANNELS];
329 	}
330 
331 	multi_buffer_list bufferList;
332 	bufferList.info_size = sizeof(multi_buffer_list);
333 	bufferList.request_playback_buffer_size = 0;
334 	bufferList.request_playback_buffers = NUM_BUFFERS;
335 	bufferList.request_playback_channels = sDescription.output_channel_count;
336 	bufferList.playback_buffers = (buffer_desc**)playBufferDesc;
337 	bufferList.request_record_buffer_size = 0;
338 	bufferList.request_record_buffers = NUM_BUFFERS;
339 	bufferList.request_record_channels = sDescription.input_channel_count;
340 	bufferList.record_buffers = (buffer_desc**)recordBufferDesc;
341 
342 	if (ioctl(sDevice, B_MULTI_GET_BUFFERS, &bufferList,
343 			sizeof(multi_buffer_list)) < B_OK) {
344 		printf("Getting buffers failed: %s\n", strerror(errno));
345 		return;
346 	}
347 
348 	printf("playback: buffer count %ld, channels %ld, buffer size %ld\n",
349 		bufferList.return_playback_buffers, bufferList.return_playback_channels,
350 		bufferList.return_playback_buffer_size);
351 	for (int32 channel = 0; channel < bufferList.return_playback_channels;
352 			channel++) {
353 		printf("  Channel %ld\n", channel);
354 		for (int32 i = 0; i < bufferList.return_playback_buffers; i++) {
355 			printf("    [%ld] buffer %p, stride %ld\n", i,
356 				bufferList.playback_buffers[i][channel].base,
357 				bufferList.playback_buffers[i][channel].stride);
358 		}
359 	}
360 
361 	printf("record: buffer count %ld, channels %ld, buffer size %ld\n",
362 		bufferList.return_record_buffers, bufferList.return_record_channels,
363 		bufferList.return_record_buffer_size);
364 
365 	multi_buffer_info bufferInfo;
366 	memset(&bufferInfo, 0, sizeof(multi_buffer_info));
367 	bufferInfo.info_size = sizeof(multi_buffer_info);
368 
369 	bigtime_t startTime = system_time();
370 	uint32 exchanged = 0;
371 	int32 cycle = -1;
372 	uint32 x = 0;
373 	while (true) {
374 		if (system_time() - startTime > 1000000LL)
375 			break;
376 
377 		if (ioctl(sDevice, B_MULTI_BUFFER_EXCHANGE, &bufferInfo,
378 				sizeof(multi_buffer_list)) < B_OK) {
379 			printf("Getting buffers failed: %s\n", strerror(errno));
380 			continue;
381 		}
382 
383 		// fill buffer with data
384 
385 		// Note: hmulti-audio drivers may actually return more than once
386 		// per buffer...
387 		if (cycle == bufferInfo.playback_buffer_cycle
388 			&& bufferList.return_playback_buffers != 1)
389 			continue;
390 
391 		cycle = bufferInfo.playback_buffer_cycle;
392 
393 		size_t stride = bufferList.playback_buffers[cycle][0].stride;
394 		for (int32 channel = 0; channel < bufferList.return_playback_channels;
395 				channel++) {
396 			if (((1 << channel) & playMask) == 0)
397 				continue;
398 
399 			char* dest = bufferList.playback_buffers[cycle][channel].base;
400 			for (uint32 frame = 0;
401 					frame < bufferList.return_playback_buffer_size; frame++) {
402 				set_frame(dest, formatInfo.output.format, sin((x + frame) / 32.0));
403 				dest += stride;
404 			}
405 		}
406 
407 		x += bufferList.return_playback_buffer_size;
408 		exchanged++;
409 	}
410 
411 	printf("%ld buffers exchanged while playing (%lu frames played (%lld)).\n",
412 		exchanged, x, bufferInfo.played_frames_count);
413 
414 	// clear buffers
415 
416 	for (int32 i = 0; i < bufferList.return_playback_buffers; i++) {
417 		size_t stride = bufferList.playback_buffers[i][0].stride;
418 		for (int32 channel = 0; channel < sDescription.output_channel_count;
419 				channel++) {
420 			char* dest = bufferList.playback_buffers[i][channel].base;
421 			for (uint32 frame = bufferList.return_playback_buffer_size;
422 					frame-- > 0; ) {
423 				set_frame(dest, formatInfo.output.format, 0);
424 				dest += stride;
425 			}
426 		}
427 	}
428 
429 	if (ioctl(sDevice, B_MULTI_BUFFER_FORCE_STOP, NULL, 0) < B_OK) {
430 		printf("Stopping audio failed: %s\n", strerror(errno));
431 	}
432 }
433 
434 
435 static cmd_entry sBuiltinCommands[] = {
436 	{"rate", do_rate, "Set sample rate"},
437 	{"format", do_format, "Set sample format"},
438 	{"desc", do_desc, "Shows description"},
439 	{"channels", do_channels, "Shows enabled/disabled channels"},
440 	{"play", do_play, "Plays a tone"},
441 	{"help", do_help, "prints this help text"},
442 	{"quit", NULL, "exits the application"},
443 	{NULL, NULL, NULL},
444 };
445 
446 
447 static void
448 do_help(int argc, char** argv)
449 {
450 	printf("Available commands:\n");
451 
452 	for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) {
453 		printf("%8s - %s\n", command->name, command->help);
454 	}
455 }
456 
457 
458 //	#pragma mark -
459 
460 
461 int
462 main(int argc, char** argv)
463 {
464 	if (argc != 2) {
465 		fprintf(stderr, "Usage: %s <device>\n", __progname);
466 		return 1;
467 	}
468 
469 	// open driver
470 
471 	sDevice = open(argv[1], O_RDWR);
472 	if (sDevice < 0) {
473 		fprintf(stderr, "%s: Could not open \"%s\": %s\n", __progname, argv[1],
474 			strerror(errno));
475 		return 1;
476 	}
477 
478 	// get description
479 
480 	memset(&sDescription, 0, sizeof(multi_description));
481 	sDescription.info_size = sizeof(multi_description);
482 	sDescription.request_channel_count = MAX_CHANNELS;
483 	sDescription.channels = sChannelInfo;
484 
485 	if (ioctl(sDevice, B_MULTI_GET_DESCRIPTION, &sDescription,
486 			sizeof(multi_description)) < 0) {
487 		fprintf(stderr, "%s: Getting description failed: %s\n", __progname,
488 			strerror(errno));
489 		close(sDevice);
490 		return 1;
491 	}
492 
493 	// get enabled channels
494 
495 	multi_channel_enable channelEnable;
496 	uint32 enabled;
497 
498 	channelEnable.info_size = sizeof(multi_channel_enable);
499 	channelEnable.enable_bits = (uchar*)&enabled;
500 
501 	if (ioctl(sDevice, B_MULTI_GET_ENABLED_CHANNELS, &channelEnable,
502 			sizeof(channelEnable)) < B_OK) {
503 		fprintf(stderr, "Failed on B_MULTI_GET_ENABLED_CHANNELS: %s\n",
504 			strerror(errno));
505 		return 1;
506 	}
507 
508 	sEnabledChannels = enabled;
509 
510 	while (true) {
511 		printf("> ");
512 		fflush(stdout);
513 
514 		char line[1024];
515 		if (fgets(line, sizeof(line), stdin) == NULL)
516 			break;
517 
518         argc = 0;
519         argv = build_argv(line, &argc);
520         if (argv == NULL || argc == 0)
521             continue;
522 
523         int length = strlen(argv[0]);
524 
525 		if (!strcmp(argv[0], "quit")
526 			|| !strcmp(argv[0], "exit")
527 			|| !strcmp(argv[0], "q"))
528 			break;
529 
530 		bool found = false;
531 
532 		for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) {
533 			if (!strncmp(command->name, argv[0], length)) {
534 				command->func(argc, argv);
535 				found = true;
536 				break;
537 			}
538 		}
539 
540 		if (!found)
541 			fprintf(stderr, "Unknown command \"%s\". Type \"help\" for a list of commands.\n", argv[0]);
542 
543 		free(argv);
544 	}
545 
546 	close(sDevice);
547 	return 0;
548 }
549 
550