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