From 02b3446666d04e12e8fe8caea11c6f2f0962fd8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 18 Feb 2008 18:04:30 +0000 Subject: [PATCH] * Wrote a multi audio test application to debug the HD-Audio driver. * Right now, it even plays something, but it doesn't sound like it should (more like noise). * Also, the hda driver only works once, unlike the auich driver. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24007 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/tests/add-ons/kernel/drivers/Jamfile | 1 + .../add-ons/kernel/drivers/audio/Jamfile | 17 + .../kernel/drivers/audio/multi_audio_test.cpp | 497 ++++++++++++++++++ 3 files changed, 515 insertions(+) create mode 100644 src/tests/add-ons/kernel/drivers/audio/Jamfile create mode 100644 src/tests/add-ons/kernel/drivers/audio/multi_audio_test.cpp diff --git a/src/tests/add-ons/kernel/drivers/Jamfile b/src/tests/add-ons/kernel/drivers/Jamfile index 0918bb9a9f..e98372a8dc 100644 --- a/src/tests/add-ons/kernel/drivers/Jamfile +++ b/src/tests/add-ons/kernel/drivers/Jamfile @@ -1,4 +1,5 @@ SubDir HAIKU_TOP src tests add-ons kernel drivers ; +SubInclude HAIKU_TOP src tests add-ons kernel drivers audio ; SubInclude HAIKU_TOP src tests add-ons kernel drivers random ; SubInclude HAIKU_TOP src tests add-ons kernel drivers tty ; diff --git a/src/tests/add-ons/kernel/drivers/audio/Jamfile b/src/tests/add-ons/kernel/drivers/audio/Jamfile new file mode 100644 index 0000000000..a437cd0ac2 --- /dev/null +++ b/src/tests/add-ons/kernel/drivers/audio/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src tests add-ons kernel drivers audio ; + +SubDirHdrs [ FDirName $(HAIKU_TOP) src tests add-ons kernel file_systems fs_shell ] ; +UsePrivateHeaders [ FDirName media ] ; + +SubDirCcFlags [ FDefines HAIKU_MULTI_AUDIO=1 ] ; + +SimpleTest multi_audio_test : + multi_audio_test.cpp + argv.c + + : libroot.so +; + +SEARCH on [ FGristFiles + argv.c + ] = [ FDirName $(HAIKU_TOP) src tests add-ons kernel file_systems fs_shell ] ; diff --git a/src/tests/add-ons/kernel/drivers/audio/multi_audio_test.cpp b/src/tests/add-ons/kernel/drivers/audio/multi_audio_test.cpp new file mode 100644 index 0000000000..de743c4356 --- /dev/null +++ b/src/tests/add-ons/kernel/drivers/audio/multi_audio_test.cpp @@ -0,0 +1,497 @@ +/* + * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include +#include + +#ifdef HAIKU_MULTI_AUDIO +# include +#else +# include +#endif + +#include "argv.h" + + +#define MAX_CONTROLS 128 +#define MAX_CHANNELS 32 +#define NUM_BUFFERS 16 + +const uint32 kSampleRates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000, 384000, 1536000 +}; +const struct { + uint32 type; + char* name; +} kFormats[] = { + {B_FMT_8BIT_S, "8bit"}, + {B_FMT_8BIT_U, "8bit-unsigned"}, + {B_FMT_16BIT, "16bit"}, + {B_FMT_18BIT, "18bit"}, + {B_FMT_20BIT, "20bit"}, + {B_FMT_24BIT, "24bit"}, + {B_FMT_32BIT, "32bit"}, + {B_FMT_FLOAT, "float"}, + {B_FMT_DOUBLE, "double"} +}; + + +struct cmd_entry { + char* name; + void (*func)(int argc, char **argv); + char* help; +}; + + +extern const char* __progname; + +static void do_help(int argc, char** argv); + + +static int sDevice; +static multi_channel_info sChannelInfo[MAX_CHANNELS]; +static multi_description sDescription; +static uint32 sRate = B_SR_48000; +static uint32 sFormat = B_FMT_32BIT;//B_FMT_FLOAT;//B_FMT_16BIT; +static uint32 sEnabledChannels = ~0; + + +static uint32 +channel_count() +{ + return sDescription.output_channel_count + sDescription.input_channel_count; +} + + +static void +set_frame(char* frame, uint32 format, float value) +{ + switch (format) { + case B_FMT_16BIT: + *(int16*)frame = int16(value * INT16_MAX); + break; + case B_FMT_32BIT: + *(int32*)frame = int32(value * INT32_MAX); + break; + default: + *(float*)frame = value; + break; + } +} + + +static uint32 +get_rate(uint32 rateBits) +{ + uint32 rate = 0; + for (uint32 i = 0; (1UL << i) <= rateBits; i++) { + if ((1UL << i) == rateBits) + rate = kSampleRates[i]; + } + + return rate; +} + + +static const char* +get_format_name(uint32 format) +{ + for (uint32 i = 0; i < sizeof(kFormats) / sizeof(kFormats[0]); i++) { + if (kFormats[i].type == format) + return kFormats[i].name; + } + + return "unknown"; +} + + +static const char* +get_kind_name(uint32 kind) +{ + switch (kind) { + case B_MULTI_OUTPUT_CHANNEL: + return "out"; + case B_MULTI_INPUT_CHANNEL: + return "in"; + case B_MULTI_OUTPUT_BUS: + return "bus-out"; + case B_MULTI_INPUT_BUS: + return "bus-in"; + case B_MULTI_AUX_BUS: + return "bus-aux"; + + default: + return "unknown"; + } +} + + +// #pragma mark - Commands + + +static void +do_rate(int argc, char** argv) +{ + if (argc > 1) { + uint32 rate = strtoul(argv[1], NULL, 0); + uint32 bits = 0; + + for (uint32 i = 0; i < sizeof(kSampleRates) / sizeof(kSampleRates[0]); + i++) { + if (rate == kSampleRates[i]) + bits = 1 << i; + } + + if (bits == 0) { + fprintf(stderr, "Invalid sample rate %ld!\n", rate); + printf("Valid values are:"); + for (uint32 i = 0; + i < sizeof(kSampleRates) / sizeof(kSampleRates[0]); i++) { + printf(" %lu", kSampleRates[i]); + } + putchar('\n'); + return; + } + sRate = bits; + } + + printf("Current sample rate is %lu Hz (0x%lx)\n", get_rate(sRate), sRate); +} + + +static void +do_format(int argc, char** argv) +{ + printf("Current sample format is %s (0x%lx)\n", get_format_name(sFormat), + sFormat); +} + + +static void +do_desc(int argc, char** argv) +{ + printf("friendly name:\t\t\t%s\n", sDescription.friendly_name); + printf("vendor:\t\t\t\t%s\n\n", sDescription.vendor_info); + + printf("output rates:\t\t\t0x%lx (max %lu)\n", sDescription.output_rates, + get_rate(sDescription.output_rates)); + printf("input rates:\t\t\t0x%lx (max %lu)\n", sDescription.input_rates, + get_rate(sDescription.input_rates)); + printf("max cont. var. sample rate:\t%.0f\n", + sDescription.max_cvsr_rate); + printf("min cont. var. sample rate:\t%.0f\n", + sDescription.min_cvsr_rate); + printf("output formats:\t\t\t0x%lx\n", sDescription.output_formats); + printf("input formats:\t\t\t0x%lx\n", sDescription.input_formats); + printf("lock sources:\t\t\t0x%lx\n", sDescription.lock_sources); + printf("timecode sources:\t\t0x%lx\n", sDescription.timecode_sources); + printf("interface flags:\t\t0x%lx\n", sDescription.interface_flags); + printf("control panel string:\t\t\t%s\n", sDescription.control_panel); + + printf("\nchannels:\n"); + printf(" %ld outputs, %ld inputs\n", sDescription.output_channel_count, + sDescription.input_channel_count); + printf(" %ld out busses, %ld in busses\n", + sDescription.output_bus_channel_count, + sDescription.input_bus_channel_count); + printf("\n ID\tkind\t designations\tconnectors\n"); + + for (uint32 i = 0 ; i < channel_count(); i++) { + printf("%4ld\t%-10s 0x%lx\t0x%lx\n", sDescription.channels[i].channel_id, + get_kind_name(sDescription.channels[i].kind), + sDescription.channels[i].designations, + sDescription.channels[i].connectors); + } +} + + +static void +do_channels(int argc, char** argv) +{ + uint32 enabled = ((1 << channel_count()) - 1) & sEnabledChannels; + + printf("%ld channels:\n ", channel_count()); + for (uint32 i = 0; i < channel_count(); i++) { + printf(enabled & 1 ? "x" : "-"); + enabled >>= 1; + } + putchar('\n'); + +/* + sEnabledChannels = enabled; + + multi_channel_enable channelEnable; + uint32 enabled; + + channelEnable.info_size = sizeof(multi_channel_enable); + channelEnable.enable_bits = (uchar*)&enabled; + + if (ioctl(sDevice, B_MULTI_GET_ENABLED_CHANNELS, &channelEnable, + sizeof(channelEnable)) < B_OK) { + fprintf(stderr, "Failed on B_MULTI_GET_ENABLED_CHANNELS: %s\n", + strerror(errno)); + return; + } + + + multi_channel_enable channelEnable; + uint32 enabled = ((1 << channel_count()) - 1) & sEnabledChannels; + + channelEnable.lock_source = B_MULTI_LOCK_INTERNAL; + if (ioctl(sDevice, B_MULTI_SET_ENABLED_CHANNELS, &channelEnable, + sizeof(multi_channel_enable)) < B_OK) { + fprintf(stderr, "Setting enabled channels failed: %s\n", + strerror(errno)); + } +*/ +} + + +static void +do_play(int argc, char** argv) +{ + multi_channel_enable channelEnable; + uint32 enabled = ((1 << channel_count()) - 1) & sEnabledChannels; + + channelEnable.enable_bits = (uchar*)&enabled; + channelEnable.lock_source = B_MULTI_LOCK_INTERNAL; + if (ioctl(sDevice, B_MULTI_SET_ENABLED_CHANNELS, &channelEnable, + sizeof(multi_channel_enable)) < B_OK) { + fprintf(stderr, "Setting enabled channels failed: %s\n", + strerror(errno)); + } + + multi_format_info formatInfo; + formatInfo.info_size = sizeof(multi_format_info); + formatInfo.output.rate = sRate; + formatInfo.output.cvsr = 0; + formatInfo.output.format = sFormat; + formatInfo.input.rate = formatInfo.output.rate; + formatInfo.input.cvsr = formatInfo.output.cvsr; + formatInfo.input.format = formatInfo.output.format; + + if (ioctl(sDevice, B_MULTI_SET_GLOBAL_FORMAT, &formatInfo, + sizeof(multi_format_info)) < B_OK) { + printf("Setting global format failed: %s\n", strerror(errno)); + } + + if (ioctl(sDevice, B_MULTI_GET_GLOBAL_FORMAT, &formatInfo, + sizeof(multi_format_info)) < B_OK) { + printf("Getting global format failed: %s\n", strerror(errno)); + } + + printf("format %s (0x%lx)\n", get_format_name(formatInfo.output.format), + formatInfo.output.format); + printf("sample rate %lu (0x%lx)\n", get_rate(formatInfo.output.rate), + formatInfo.output.rate); + + buffer_desc playBuffers[NUM_BUFFERS * MAX_CHANNELS]; + buffer_desc recordBuffers[NUM_BUFFERS * MAX_CHANNELS]; + buffer_desc* playBufferDesc[NUM_BUFFERS]; + buffer_desc* recordBufferDesc[NUM_BUFFERS]; + + for (uint32 i = 0; i < NUM_BUFFERS; i++) { + playBufferDesc[i] = &playBuffers[i * MAX_CHANNELS]; + recordBufferDesc[i] = &recordBuffers[i * MAX_CHANNELS]; + } + + multi_buffer_list bufferList; + bufferList.info_size = sizeof(multi_buffer_list); + bufferList.request_playback_buffer_size = 0; + bufferList.request_playback_buffers = NUM_BUFFERS; + bufferList.request_playback_channels = sDescription.output_channel_count; + bufferList.playback_buffers = (buffer_desc**)playBufferDesc; + bufferList.request_record_buffer_size = 0; + bufferList.request_record_buffers = NUM_BUFFERS; + bufferList.request_record_channels = sDescription.input_channel_count; + bufferList.record_buffers = (buffer_desc**)recordBufferDesc; + + if (ioctl(sDevice, B_MULTI_GET_BUFFERS, &bufferList, + sizeof(multi_buffer_list)) < B_OK) { + printf("Getting buffers failed: %s\n", strerror(errno)); + return; + } + + printf("playback: buffer count %ld, channels %ld, buffer size %ld\n", + bufferList.return_playback_buffers, bufferList.return_playback_channels, + bufferList.return_playback_buffer_size); + printf("record: buffer count %ld, channels %ld, buffer size %ld\n", + bufferList.return_record_buffers, bufferList.return_record_channels, + bufferList.return_record_buffer_size); + + // fill buffers with data + + for (int32 i = 0; i < bufferList.return_playback_buffers; i++) { + size_t stride = bufferList.playback_buffers[i][0].stride; + for (int32 channel = 0; channel < sDescription.output_channel_count; + channel++) { + char* dest = bufferList.playback_buffers[i][channel].base; + for (uint32 frame = 0; + frame < bufferList.return_playback_buffer_size; frame++) { + set_frame(dest, formatInfo.output.format, sin(frame / 1000.0)); + dest += stride; + } + } + } + + multi_buffer_info bufferInfo; + memset(&bufferInfo, 0, sizeof(multi_buffer_info)); + bufferInfo.info_size = sizeof(multi_buffer_info); + + bigtime_t startTime = system_time(); + while (true) { + if (system_time() - startTime > 1000000LL) + break; + + if (ioctl(sDevice, B_MULTI_BUFFER_EXCHANGE, &bufferInfo, + sizeof(multi_buffer_list)) < B_OK) { + printf("Getting buffers failed: %s\n", strerror(errno)); + } + + bufferInfo.playback_buffer_cycle = (bufferInfo.playback_buffer_cycle + 1) + % bufferList.request_playback_buffers; + } + + // clear buffers + + for (int32 i = 0; i < bufferList.return_playback_buffers; i++) { + size_t stride = bufferList.playback_buffers[i][0].stride; + for (int32 channel = 0; channel < sDescription.output_channel_count; + channel++) { + char* dest = bufferList.playback_buffers[i][channel].base; + for (uint32 frame = bufferList.return_playback_buffer_size; + frame-- > 0; ) { + set_frame(dest, formatInfo.output.format, 0); + dest += stride; + } + } + } + + if (ioctl(sDevice, B_MULTI_BUFFER_FORCE_STOP, NULL, 0) < B_OK) { + printf("Stopping audio failed: %s\n", strerror(errno)); + } +} + + +static cmd_entry sBuiltinCommands[] = { + {"rate", do_rate, "Set sample rate"}, + {"format", do_format, "Set sample format"}, + {"desc", do_desc, "Shows description"}, + {"channels", do_channels, "Shows enabled/disabled channels"}, + {"play", do_play, "Plays a tone"}, + {"help", do_help, "prints this help text"}, + {"quit", NULL, "exits the application"}, + {NULL, NULL, NULL}, +}; + + +static void +do_help(int argc, char** argv) +{ + printf("Available commands:\n"); + + for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) { + printf("%8s - %s\n", command->name, command->help); + } +} + + +// #pragma mark - + + +int +main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", __progname); + return 1; + } + + // open driver + + sDevice = open(argv[1], O_RDWR); + if (sDevice < 0) { + fprintf(stderr, "%s: Could not open \"%s\": %s\n", __progname, argv[1], + strerror(errno)); + return 1; + } + + // get description + + memset(&sDescription, 0, sizeof(multi_description)); + sDescription.info_size = sizeof(multi_description); + sDescription.request_channel_count = MAX_CHANNELS; + sDescription.channels = sChannelInfo; + + if (ioctl(sDevice, B_MULTI_GET_DESCRIPTION, &sDescription, + sizeof(multi_description)) < 0) { + fprintf(stderr, "%s: Getting description failed: %s\n", __progname, + strerror(errno)); + close(sDevice); + return 1; + } + + // get enabled channels + + multi_channel_enable channelEnable; + uint32 enabled; + + channelEnable.info_size = sizeof(multi_channel_enable); + channelEnable.enable_bits = (uchar*)&enabled; + + if (ioctl(sDevice, B_MULTI_GET_ENABLED_CHANNELS, &channelEnable, + sizeof(channelEnable)) < B_OK) { + fprintf(stderr, "Failed on B_MULTI_GET_ENABLED_CHANNELS: %s\n", + strerror(errno)); + return 1; + } + + sEnabledChannels = enabled; + + while (true) { + printf("> "); + fflush(stdout); + + char line[1024]; + if (fgets(line, sizeof(line), stdin) == NULL) + break; + + argc = 0; + argv = build_argv(line, &argc); + if (argv == NULL || argc == 0) + continue; + + int length = strlen(argv[0]); + + if (!strcmp(argv[0], "quit") + || !strcmp(argv[0], "exit") + || !strcmp(argv[0], "q")) + break; + + bool found = false; + + for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) { + if (!strncmp(command->name, argv[0], length)) { + command->func(argc, argv); + found = true; + break; + } + } + + if (!found) + fprintf(stderr, "Unknown command \"%s\". Type \"help\" for a list of commands.\n", argv[0]); + + free(argv); + } + + close(sDevice); + return 0; +} +