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
23const uint32 kSampleRates[] = {
24	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100,
25	48000, 64000, 88200, 96000, 176400, 192000, 384000, 1536000
26};
27const 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
43struct cmd_entry {
44	const char*	name;
45	void		(*func)(int argc, char **argv);
46	const char*	help;
47};
48
49
50extern const char* __progname;
51
52static void do_help(int argc, char** argv);
53
54
55static int sDevice;
56static multi_channel_info sChannelInfo[MAX_CHANNELS];
57static multi_description sDescription;
58static uint32 sRate = B_SR_48000;
59static uint32 sFormat = B_FMT_32BIT;
60static uint32 sEnabledChannels = ~0;
61
62
63static uint32
64channel_count()
65{
66	return sDescription.output_channel_count + sDescription.input_channel_count;
67}
68
69
70static void
71set_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
96static uint32
97get_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
110static void
111print_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
122static const char*
123get_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
134static void
135print_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
145static const char*
146get_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
169static void
170do_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
199static void
200do_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
222static void
223do_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
262static void
263do_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
279static void
280do_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
435static 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
447static void
448do_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
461int
462main(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