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