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