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 } 381 382 // fill buffer with data 383 384 // Note: hmulti-audio drivers may actually return more than once 385 // per buffer... 386 if (cycle == bufferInfo.playback_buffer_cycle 387 && bufferList.return_playback_buffers != 1) 388 continue; 389 390 cycle = bufferInfo.playback_buffer_cycle; 391 392 size_t stride = bufferList.playback_buffers[cycle][0].stride; 393 for (int32 channel = 0; channel < bufferList.return_playback_channels; 394 channel++) { 395 if (((1 << channel) & playMask) == 0) 396 continue; 397 398 char* dest = bufferList.playback_buffers[cycle][channel].base; 399 for (uint32 frame = 0; 400 frame < bufferList.return_playback_buffer_size; frame++) { 401 set_frame(dest, formatInfo.output.format, sin((x + frame) / 32.0)); 402 dest += stride; 403 } 404 } 405 406 x += bufferList.return_playback_buffer_size; 407 exchanged++; 408 } 409 410 printf("%ld buffers exchanged while playing (%lu frames played (%Ld)).\n", 411 exchanged, x, bufferInfo.played_frames_count); 412 413 // clear buffers 414 415 for (int32 i = 0; i < bufferList.return_playback_buffers; i++) { 416 size_t stride = bufferList.playback_buffers[i][0].stride; 417 for (int32 channel = 0; channel < sDescription.output_channel_count; 418 channel++) { 419 char* dest = bufferList.playback_buffers[i][channel].base; 420 for (uint32 frame = bufferList.return_playback_buffer_size; 421 frame-- > 0; ) { 422 set_frame(dest, formatInfo.output.format, 0); 423 dest += stride; 424 } 425 } 426 } 427 428 if (ioctl(sDevice, B_MULTI_BUFFER_FORCE_STOP, NULL, 0) < B_OK) { 429 printf("Stopping audio failed: %s\n", strerror(errno)); 430 } 431 } 432 433 434 static cmd_entry sBuiltinCommands[] = { 435 {"rate", do_rate, "Set sample rate"}, 436 {"format", do_format, "Set sample format"}, 437 {"desc", do_desc, "Shows description"}, 438 {"channels", do_channels, "Shows enabled/disabled channels"}, 439 {"play", do_play, "Plays a tone"}, 440 {"help", do_help, "prints this help text"}, 441 {"quit", NULL, "exits the application"}, 442 {NULL, NULL, NULL}, 443 }; 444 445 446 static void 447 do_help(int argc, char** argv) 448 { 449 printf("Available commands:\n"); 450 451 for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) { 452 printf("%8s - %s\n", command->name, command->help); 453 } 454 } 455 456 457 // #pragma mark - 458 459 460 int 461 main(int argc, char** argv) 462 { 463 if (argc != 2) { 464 fprintf(stderr, "Usage: %s <device>\n", __progname); 465 return 1; 466 } 467 468 // open driver 469 470 sDevice = open(argv[1], O_RDWR); 471 if (sDevice < 0) { 472 fprintf(stderr, "%s: Could not open \"%s\": %s\n", __progname, argv[1], 473 strerror(errno)); 474 return 1; 475 } 476 477 // get description 478 479 memset(&sDescription, 0, sizeof(multi_description)); 480 sDescription.info_size = sizeof(multi_description); 481 sDescription.request_channel_count = MAX_CHANNELS; 482 sDescription.channels = sChannelInfo; 483 484 if (ioctl(sDevice, B_MULTI_GET_DESCRIPTION, &sDescription, 485 sizeof(multi_description)) < 0) { 486 fprintf(stderr, "%s: Getting description failed: %s\n", __progname, 487 strerror(errno)); 488 close(sDevice); 489 return 1; 490 } 491 492 // get enabled channels 493 494 multi_channel_enable channelEnable; 495 uint32 enabled; 496 497 channelEnable.info_size = sizeof(multi_channel_enable); 498 channelEnable.enable_bits = (uchar*)&enabled; 499 500 if (ioctl(sDevice, B_MULTI_GET_ENABLED_CHANNELS, &channelEnable, 501 sizeof(channelEnable)) < B_OK) { 502 fprintf(stderr, "Failed on B_MULTI_GET_ENABLED_CHANNELS: %s\n", 503 strerror(errno)); 504 return 1; 505 } 506 507 sEnabledChannels = enabled; 508 509 while (true) { 510 printf("> "); 511 fflush(stdout); 512 513 char line[1024]; 514 if (fgets(line, sizeof(line), stdin) == NULL) 515 break; 516 517 argc = 0; 518 argv = build_argv(line, &argc); 519 if (argv == NULL || argc == 0) 520 continue; 521 522 int length = strlen(argv[0]); 523 524 if (!strcmp(argv[0], "quit") 525 || !strcmp(argv[0], "exit") 526 || !strcmp(argv[0], "q")) 527 break; 528 529 bool found = false; 530 531 for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) { 532 if (!strncmp(command->name, argv[0], length)) { 533 command->func(argc, argv); 534 found = true; 535 break; 536 } 537 } 538 539 if (!found) 540 fprintf(stderr, "Unknown command \"%s\". Type \"help\" for a list of commands.\n", argv[0]); 541 542 free(argv); 543 } 544 545 close(sDevice); 546 return 0; 547 } 548 549