* 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
This commit is contained in:
parent
1134e85609
commit
02b3446666
|
@ -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 ;
|
||||
|
|
|
@ -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 ] ;
|
|
@ -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 <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAIKU_MULTI_AUDIO
|
||||
# include <hmulti_audio.h>
|
||||
#else
|
||||
# include <multi_audio.h>
|
||||
#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 <device>\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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue