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