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
27const uint32 kSampleRates[] = {
28	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100,
29	48000, 64000, 88200, 96000, 176400, 192000, 384000, 1536000
30};
31const 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
47struct cmd_entry {
48	const char*	name;
49	void		(*func)(int argc, char **argv);
50	const char*	help;
51};
52
53
54extern const char* __progname;
55
56static void do_help(int argc, char** argv);
57
58
59static int sDevice;
60static multi_channel_info sChannelInfo[MAX_CHANNELS];
61static multi_description sDescription;
62static uint32 sRate = B_SR_48000;
63static uint32 sFormat = B_FMT_32BIT;
64static uint32 sEnabledChannels = ~0;
65
66
67static uint32
68channel_count()
69{
70	return sDescription.output_channel_count + sDescription.input_channel_count;
71}
72
73
74static void
75set_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
100static uint32
101get_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
114static void
115print_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
126static const char*
127get_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
138static void
139print_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
149static const char*
150get_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
173static void
174do_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
203static void
204do_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
226static void
227do_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
266static void
267do_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
283static void
284do_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
438static 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
450static void
451do_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
464int
465main(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