/* * Copyright 2009, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Jérôme Duval (korli@users.berlios.de) */ #include "hmulti_audio.h" #include "driver.h" #ifdef TRACE # undef TRACE #endif #define TRACE_MULTI_AUDIO #ifdef TRACE_MULTI_AUDIO # define TRACE(a...) dprintf("\33[34mgeode:\33[0m " a) #else # define TRACE(a...) ; #endif static multi_channel_info sChannels[] = { { 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, { 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, { 2, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, { 3, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, { 4, B_MULTI_OUTPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, { 5, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, { 6, B_MULTI_INPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, { 7, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, }; static int32 format2size(uint32 format) { switch (format) { case B_FMT_8BIT_S: case B_FMT_16BIT: return 2; case B_FMT_18BIT: case B_FMT_20BIT: case B_FMT_24BIT: case B_FMT_32BIT: case B_FMT_FLOAT: return 4; default: return -1; } } static status_t get_description(geode_controller* controller, multi_description* data) { data->interface_version = B_CURRENT_INTERFACE_VERSION; data->interface_minimum = B_CURRENT_INTERFACE_VERSION; strcpy(data->friendly_name, "Geode"); strcpy(data->vendor_info, "Haiku"); int32 inChannels = 0; if (controller->record_stream != NULL) inChannels = 2; int32 outChannels = 0; if (controller->playback_stream != NULL) outChannels = 2; data->output_channel_count = outChannels; data->output_bus_channel_count = outChannels; data->input_channel_count = inChannels; data->input_bus_channel_count = inChannels; data->aux_bus_channel_count = 0; dprintf("%s: request_channel_count: %" B_PRId32 "\n", __func__, data->request_channel_count); if (data->request_channel_count >= (int)(sizeof(sChannels) / sizeof(sChannels[0]))) { memcpy(data->channels, &sChannels, sizeof(sChannels)); } /* determine output/input rates */ data->output_rates = B_SR_48000; data->input_rates = B_SR_48000; /* force existance of 48kHz if variable rates are not supported */ if (data->output_rates == 0) data->output_rates = B_SR_48000; if (data->input_rates == 0) data->input_rates = B_SR_48000; data->max_cvsr_rate = 0; data->min_cvsr_rate = 0; data->output_formats = B_FMT_16BIT; data->input_formats = B_FMT_16BIT; data->lock_sources = B_MULTI_LOCK_INTERNAL; data->timecode_sources = 0; data->interface_flags = B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD; data->start_latency = 30000; strcpy(data->control_panel, ""); return B_OK; } static status_t get_enabled_channels(geode_controller* controller, multi_channel_enable* data) { B_SET_CHANNEL(data->enable_bits, 0, true); B_SET_CHANNEL(data->enable_bits, 1, true); B_SET_CHANNEL(data->enable_bits, 2, true); B_SET_CHANNEL(data->enable_bits, 3, true); data->lock_source = B_MULTI_LOCK_INTERNAL; return B_OK; } static status_t get_global_format(geode_controller* controller, multi_format_info* data) { data->output_latency = 0; data->input_latency = 0; data->timecode_kind = 0; if (controller->playback_stream != NULL) { data->output.format = controller->playback_stream->sample_format; data->output.rate = controller->playback_stream->sample_rate; } else { data->output.format = 0; data->output.rate = 0; } if (controller->record_stream != NULL) { data->input.format = controller->record_stream->sample_format; data->input.rate = controller->record_stream->sample_format; } else { data->input.format = 0; data->input.rate = 0; } return B_OK; } static status_t set_global_format(geode_controller* controller, multi_format_info* data) { // TODO: it looks like we're not supposed to fail; fix this! #if 0 if ((data->output.format & audioGroup->supported_formats) == 0) || (data->output.rate & audioGroup->supported_rates) == 0) return B_BAD_VALUE; #endif if (controller->playback_stream != NULL) { controller->playback_stream->sample_format = data->output.format; controller->playback_stream->sample_rate = data->output.rate; controller->playback_stream->sample_size = format2size( controller->playback_stream->sample_format); } if (controller->record_stream != NULL) { controller->record_stream->sample_rate = data->input.rate; controller->record_stream->sample_format = data->input.format; controller->record_stream->sample_size = format2size( controller->record_stream->sample_format); } return B_OK; } static void geode_ac97_get_mix(geode_controller *controller, const void *cookie, int32 type, float *values) { ac97_source_info *info = (ac97_source_info *)cookie; uint16 value, mask; float gain; switch(type) { case B_MIX_GAIN: value = ac97_reg_cached_read(controller->ac97, info->reg); //TRACE("B_MIX_GAIN value : %u\n", value); if (info->type & B_MIX_STEREO) { mask = ((1 << (info->bits + 1)) - 1) << 8; gain = ((value & mask) >> 8) * info->granularity; if (info->polarity == 1) values[0] = info->max_gain - gain; else values[0] = gain - info->min_gain; mask = ((1 << (info->bits + 1)) - 1); gain = (value & mask) * info->granularity; if (info->polarity == 1) values[1] = info->max_gain - gain; else values[1] = gain - info->min_gain; } else { mask = ((1 << (info->bits + 1)) - 1); gain = (value & mask) * info->granularity; if (info->polarity == 1) values[0] = info->max_gain - gain; else values[0] = gain - info->min_gain; } break; case B_MIX_MUTE: mask = ((1 << 1) - 1) << 15; value = ac97_reg_cached_read(controller->ac97, info->reg); //TRACE("B_MIX_MUTE value : %u\n", value); value &= mask; values[0] = ((value >> 15) == 1) ? 1.0 : 0.0; break; case B_MIX_MICBOOST: mask = ((1 << 1) - 1) << 6; value = ac97_reg_cached_read(controller->ac97, info->reg); //TRACE("B_MIX_MICBOOST value : %u\n", value); value &= mask; values[0] = ((value >> 6) == 1) ? 1.0 : 0.0; break; case B_MIX_MUX: mask = ((1 << 3) - 1); value = ac97_reg_cached_read(controller->ac97, AC97_RECORD_SELECT); value &= mask; //TRACE("B_MIX_MUX value : %u\n", value); values[0] = (float)value; break; } } static void geode_ac97_set_mix(geode_controller *controller, const void *cookie, int32 type, float *values) { ac97_source_info *info = (ac97_source_info *)cookie; uint16 value, mask; float gain; switch(type) { case B_MIX_GAIN: value = ac97_reg_cached_read(controller->ac97, info->reg); if (info->type & B_MIX_STEREO) { mask = ((1 << (info->bits + 1)) - 1) << 8; value &= ~mask; if (info->polarity == 1) gain = info->max_gain - values[0]; else gain = values[0] - info->min_gain; value |= ((uint16)(gain / info->granularity) << 8) & mask; mask = ((1 << (info->bits + 1)) - 1); value &= ~mask; if (info->polarity == 1) gain = info->max_gain - values[1]; else gain = values[1] - info->min_gain; value |= ((uint16)(gain / info->granularity)) & mask; } else { mask = ((1 << (info->bits + 1)) - 1); value &= ~mask; if (info->polarity == 1) gain = info->max_gain - values[0]; else gain = values[0] - info->min_gain; value |= ((uint16)(gain / info->granularity)) & mask; } //TRACE("B_MIX_GAIN value : %u\n", value); ac97_reg_cached_write(controller->ac97, info->reg, value); break; case B_MIX_MUTE: mask = ((1 << 1) - 1) << 15; value = ac97_reg_cached_read(controller->ac97, info->reg); value &= ~mask; value |= ((values[0] == 1.0 ? 1 : 0 ) << 15 & mask); if (info->reg == AC97_SURR_VOLUME) { // there is a independent mute for each channel mask = ((1 << 1) - 1) << 7; value &= ~mask; value |= ((values[0] == 1.0 ? 1 : 0 ) << 7 & mask); } //TRACE("B_MIX_MUTE value : %u\n", value); ac97_reg_cached_write(controller->ac97, info->reg, value); break; case B_MIX_MICBOOST: mask = ((1 << 1) - 1) << 6; value = ac97_reg_cached_read(controller->ac97, info->reg); value &= ~mask; value |= ((values[0] == 1.0 ? 1 : 0 ) << 6 & mask); //TRACE("B_MIX_MICBOOST value : %u\n", value); ac97_reg_cached_write(controller->ac97, info->reg, value); break; case B_MIX_MUX: mask = ((1 << 3) - 1); value = ((int32)values[0]) & mask; value = value | (value << 8); //TRACE("B_MIX_MUX value : %u\n", value); ac97_reg_cached_write(controller->ac97, AC97_RECORD_SELECT, value); break; } } static int32 create_group_control(geode_multi *multi, uint32 *index, uint32 parent, strind_id string, const char* name) { int32 i = *index; (*index)++; multi->controls[i].mix_control.id = MULTI_CONTROL_FIRSTID + i; multi->controls[i].mix_control.parent = parent; multi->controls[i].mix_control.flags = B_MULTI_MIX_GROUP; multi->controls[i].mix_control.master = MULTI_CONTROL_MASTERID; multi->controls[i].mix_control.string = string; if (name) strcpy(multi->controls[i].mix_control.name, name); return multi->controls[i].mix_control.id; } static status_t create_controls_list(geode_multi *multi) { uint32 i = 0, index = 0, count, id, parent, parent2, parent3; const ac97_source_info *info; /* AC97 Mixer */ parent = create_group_control(multi, &index, 0, S_null, "AC97 mixer"); count = source_info_size; //Note that we ignore first item in source_info //It's for recording, but do match this with ac97.c's source_info for (i = 1; i < count ; i++) { info = &source_info[i]; TRACE("name : %s\n", info->name); parent2 = create_group_control(multi, &index, parent, S_null, info->name); if (info->type & B_MIX_GAIN) { if (info->type & B_MIX_MUTE) { multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_ENABLE; multi->controls[index].mix_control.master = MULTI_CONTROL_MASTERID; multi->controls[index].mix_control.parent = parent2; multi->controls[index].mix_control.string = S_MUTE; multi->controls[index].cookie = info; multi->controls[index].type = B_MIX_MUTE; multi->controls[index].get = &geode_ac97_get_mix; multi->controls[index].set = &geode_ac97_set_mix; index++; } multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_GAIN; multi->controls[index].mix_control.master = MULTI_CONTROL_MASTERID; multi->controls[index].mix_control.parent = parent2; strcpy(multi->controls[index].mix_control.name, info->name); multi->controls[index].mix_control.gain.min_gain = info->min_gain; multi->controls[index].mix_control.gain.max_gain = info->max_gain; multi->controls[index].mix_control.gain.granularity = info->granularity; multi->controls[index].cookie = info; multi->controls[index].type = B_MIX_GAIN; multi->controls[index].get = &geode_ac97_get_mix; multi->controls[index].set = &geode_ac97_set_mix; id = multi->controls[index].mix_control.id; index++; if (info->type & B_MIX_STEREO) { multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_GAIN; multi->controls[index].mix_control.master = id; multi->controls[index].mix_control.parent = parent2; strcpy(multi->controls[index].mix_control.name, info->name); multi->controls[index].mix_control.gain.min_gain = info->min_gain; multi->controls[index].mix_control.gain.max_gain = info->max_gain; multi->controls[index].mix_control.gain.granularity = info->granularity; multi->controls[index].cookie = info; multi->controls[index].type = B_MIX_GAIN; multi->controls[index].get = &geode_ac97_get_mix; multi->controls[index].set = &geode_ac97_set_mix; index++; } if (info->type & B_MIX_MICBOOST) { multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_ENABLE; multi->controls[index].mix_control.master = MULTI_CONTROL_MASTERID; multi->controls[index].mix_control.parent = parent2; strcpy(multi->controls[index].mix_control.name, "+20 dB"); multi->controls[index].cookie = info; multi->controls[index].type = B_MIX_MICBOOST; multi->controls[index].get = &geode_ac97_get_mix; multi->controls[index].set = &geode_ac97_set_mix; index++; } } } /* AC97 Record */ parent = create_group_control(multi, &index, 0, S_null, "Recording"); info = &source_info[0]; TRACE("name : %s\n", info->name); parent2 = create_group_control(multi, &index, parent, S_null, info->name); if (info->type & B_MIX_GAIN) { if (info->type & B_MIX_MUTE) { multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_ENABLE; multi->controls[index].mix_control.master = MULTI_CONTROL_MASTERID; multi->controls[index].mix_control.parent = parent2; multi->controls[index].mix_control.string = S_MUTE; multi->controls[index].cookie = info; multi->controls[index].type = B_MIX_MUTE; multi->controls[index].get = &geode_ac97_get_mix; multi->controls[index].set = &geode_ac97_set_mix; index++; } multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_GAIN; multi->controls[index].mix_control.master = MULTI_CONTROL_MASTERID; multi->controls[index].mix_control.parent = parent2; strcpy(multi->controls[index].mix_control.name, info->name); multi->controls[index].mix_control.gain.min_gain = info->min_gain; multi->controls[index].mix_control.gain.max_gain = info->max_gain; multi->controls[index].mix_control.gain.granularity = info->granularity; multi->controls[index].cookie = info; multi->controls[index].type = B_MIX_GAIN; multi->controls[index].get = &geode_ac97_get_mix; multi->controls[index].set = &geode_ac97_set_mix; id = multi->controls[index].mix_control.id; index++; if (info->type & B_MIX_STEREO) { multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_GAIN; multi->controls[index].mix_control.master = id; multi->controls[index].mix_control.parent = parent2; strcpy(multi->controls[index].mix_control.name, info->name); multi->controls[index].mix_control.gain.min_gain = info->min_gain; multi->controls[index].mix_control.gain.max_gain = info->max_gain; multi->controls[index].mix_control.gain.granularity = info->granularity; multi->controls[index].cookie = info; multi->controls[index].type = B_MIX_GAIN; multi->controls[index].get = &geode_ac97_get_mix; multi->controls[index].set = &geode_ac97_set_mix; index++; } if (info->type & B_MIX_RECORDMUX) { multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX; multi->controls[index].mix_control.parent = parent2; strcpy(multi->controls[index].mix_control.name, "Record mux"); multi->controls[index].cookie = info; multi->controls[index].type = B_MIX_MUX; multi->controls[index].get = &geode_ac97_get_mix; multi->controls[index].set = &geode_ac97_set_mix; parent3 = multi->controls[index].mix_control.id; index++; multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; multi->controls[index].mix_control.parent = parent3; multi->controls[index].mix_control.string = S_MIC; index++; multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; multi->controls[index].mix_control.parent = parent3; strcpy(multi->controls[index].mix_control.name, "CD in"); index++; multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; multi->controls[index].mix_control.parent = parent3; strcpy(multi->controls[index].mix_control.name, "Video in"); index++; multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; multi->controls[index].mix_control.parent = parent3; strcpy(multi->controls[index].mix_control.name, "Aux in"); index++; multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; multi->controls[index].mix_control.parent = parent3; strcpy(multi->controls[index].mix_control.name, "Line in"); index++; multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; multi->controls[index].mix_control.parent = parent3; multi->controls[index].mix_control.string = S_STEREO_MIX; index++; multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; multi->controls[index].mix_control.parent = parent3; multi->controls[index].mix_control.string = S_MONO_MIX; index++; multi->controls[index].mix_control.id = MULTI_CONTROL_FIRSTID + index; multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; multi->controls[index].mix_control.parent = parent3; strcpy(multi->controls[index].mix_control.name, "TAD"); index++; } } multi->control_count = index; TRACE("multi->control_count %" B_PRIu32 "\n", multi->control_count); return B_OK; } static status_t list_mix_controls(geode_controller* controller, multi_mix_control_info* mmci) { multi_mix_control* mmc = mmci->controls; if (mmci->control_count < 24) return B_ERROR; if (create_controls_list(controller->multi) < B_OK) return B_ERROR; for (uint32 i = 0; i < controller->multi->control_count; i++) { mmc[i] = controller->multi->controls[i].mix_control; } mmci->control_count = controller->multi->control_count; return B_OK; } static status_t list_mix_connections(geode_controller* controller, multi_mix_connection_info* data) { data->actual_count = 0; return B_OK; } static status_t list_mix_channels(geode_controller* controller, multi_mix_channel_info *data) { return B_OK; } static status_t get_mix(geode_controller *controller, multi_mix_value_info * mmvi) { for (int32 i = 0; i < mmvi->item_count; i++) { uint32 id = mmvi->values[i].id - MULTI_CONTROL_FIRSTID; if (id < 0 || id >= controller->multi->control_count) { dprintf("geode_get_mix : invalid control id requested : %" B_PRId32 "\n", id); continue; } multi_mixer_control *control = &controller->multi->controls[id]; if (control->mix_control.flags & B_MULTI_MIX_GAIN) { if (control->get) { float values[2]; control->get(controller, control->cookie, control->type, values); if (control->mix_control.master == MULTI_CONTROL_MASTERID) mmvi->values[i].gain = values[0]; else mmvi->values[i].gain = values[1]; } } if (control->mix_control.flags & B_MULTI_MIX_ENABLE && control->get) { float values[1]; control->get(controller, control->cookie, control->type, values); mmvi->values[i].enable = (values[0] == 1.0); } if (control->mix_control.flags & B_MULTI_MIX_MUX && control->get) { float values[1]; control->get(controller, control->cookie, control->type, values); mmvi->values[i].mux = (int32)values[0]; } } return B_OK; } static status_t set_mix(geode_controller *controller, multi_mix_value_info * mmvi) { geode_multi *multi = controller->multi; for (int32 i = 0; i < mmvi->item_count; i++) { uint32 id = mmvi->values[i].id - MULTI_CONTROL_FIRSTID; if (id < 0 || id >= multi->control_count) { dprintf("geode_set_mix : invalid control id requested : %" B_PRId32 "\n", id); continue; } multi_mixer_control *control = &multi->controls[id]; if (control->mix_control.flags & B_MULTI_MIX_GAIN) { multi_mixer_control *control2 = NULL; if (i+1item_count) { id = mmvi->values[i + 1].id - MULTI_CONTROL_FIRSTID; if (id < 0 || id >= multi->control_count) { dprintf("geode_set_mix : invalid control id requested : %" B_PRId32 "\n", id); } else { control2 = &multi->controls[id]; if (control2->mix_control.master != control->mix_control.id) control2 = NULL; } } if (control->set) { float values[2]; values[0] = 0.0; values[1] = 0.0; if (control->mix_control.master == MULTI_CONTROL_MASTERID) values[0] = mmvi->values[i].gain; else values[1] = mmvi->values[i].gain; if (control2 && control2->mix_control.master != MULTI_CONTROL_MASTERID) values[1] = mmvi->values[i+1].gain; control->set(controller, control->cookie, control->type, values); } if (control2) i++; } if (control->mix_control.flags & B_MULTI_MIX_ENABLE && control->set) { float values[1]; values[0] = mmvi->values[i].enable ? 1.0 : 0.0; control->set(controller, control->cookie, control->type, values); } if (control->mix_control.flags & B_MULTI_MIX_MUX && control->set) { float values[1]; values[0] = (float)mmvi->values[i].mux; control->set(controller, control->cookie, control->type, values); } } return B_OK; } static status_t get_buffers(geode_controller* controller, multi_buffer_list* data) { TRACE("playback: %" B_PRId32 " buffers, %" B_PRId32 " channels, %" B_PRIu32 " samples\n", data->request_playback_buffers, data->request_playback_channels, data->request_playback_buffer_size); TRACE("record: %" B_PRId32 " buffers, %" B_PRId32 " channels, %" B_PRIu32 " samples\n", data->request_record_buffers, data->request_record_channels, data->request_record_buffer_size); /* Determine what buffers we return given the request */ data->return_playback_buffers = data->request_playback_buffers; data->return_playback_channels = data->request_playback_channels; data->return_playback_buffer_size = data->request_playback_buffer_size; data->return_record_buffers = data->request_record_buffers; data->return_record_channels = data->request_record_channels; data->return_record_buffer_size = data->request_record_buffer_size; /* Workaround for Haiku multi_audio API, since it prefers to let the driver pick values, while the BeOS multi_audio actually gives the user's defaults. */ if (data->return_playback_buffers > STREAM_MAX_BUFFERS || data->return_playback_buffers < STREAM_MIN_BUFFERS) data->return_playback_buffers = STREAM_MIN_BUFFERS; if (data->return_record_buffers > STREAM_MAX_BUFFERS || data->return_record_buffers < STREAM_MIN_BUFFERS) data->return_record_buffers = STREAM_MIN_BUFFERS; if (data->return_playback_buffer_size == 0) data->return_playback_buffer_size = DEFAULT_FRAMES_PER_BUFFER; if (data->return_record_buffer_size == 0) data->return_record_buffer_size = DEFAULT_FRAMES_PER_BUFFER; /* ... from here on, we can assume again that a reasonable request is being made */ data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; /* Copy the settings into the streams */ if (controller->playback_stream != NULL) { controller->playback_stream->num_buffers = data->return_playback_buffers; controller->playback_stream->num_channels = data->return_playback_channels; controller->playback_stream->buffer_length = data->return_playback_buffer_size; status_t status = geode_stream_setup_buffers( controller->playback_stream, "Playback"); if (status != B_OK) { dprintf("geode: Error setting up playback buffers: %s\n", strerror(status)); return status; } } if (controller->record_stream != NULL) { controller->record_stream->num_buffers = data->return_record_buffers; controller->record_stream->num_channels = data->return_record_channels; controller->record_stream->buffer_length = data->return_record_buffer_size; status_t status = geode_stream_setup_buffers( controller->record_stream, "Recording"); if (status != B_OK) { dprintf("geode: Error setting up recording buffers: %s\n", strerror(status)); return status; } } /* Setup data structure for multi_audio API... */ if (controller->playback_stream != NULL) { uint32 playbackSampleSize = controller->playback_stream->sample_size; for (int32 i = 0; i < data->return_playback_buffers; i++) { for (int32 channelIndex = 0; channelIndex < data->return_playback_channels; channelIndex++) { data->playback_buffers[i][channelIndex].base = (char*)controller->playback_stream->buffers[i] + playbackSampleSize * channelIndex; data->playback_buffers[i][channelIndex].stride = playbackSampleSize * data->return_playback_channels; } } } if (controller->record_stream != NULL) { uint32 recordSampleSize = controller->record_stream->sample_size; for (int32 i = 0; i < data->return_record_buffers; i++) { for (int32 channelIndex = 0; channelIndex < data->return_record_channels; channelIndex++) { data->record_buffers[i][channelIndex].base = (char*)controller->record_stream->buffers[i] + recordSampleSize * channelIndex; data->record_buffers[i][channelIndex].stride = recordSampleSize * data->return_record_channels; } } } return B_OK; } /*! playback_buffer_cycle is the buffer we want to have played */ static status_t buffer_exchange(geode_controller* controller, multi_buffer_info* data) { static int debug_buffers_exchanged = 0; cpu_status status; status_t err; multi_buffer_info buffer_info; if (controller->playback_stream == NULL) return B_ERROR; if (!controller->playback_stream->running) { geode_stream_start(controller->playback_stream); } if (controller->record_stream && !controller->record_stream->running) { geode_stream_start(controller->record_stream); } #ifdef __HAIKU__ if (user_memcpy(&buffer_info, data, sizeof(buffer_info)) < B_OK) return B_BAD_ADDRESS; #else memcpy(&buffer_info, data, sizeof(buffer_info)); #endif /* do playback */ err = acquire_sem_etc(controller->playback_stream->buffer_ready_sem, 1, B_CAN_INTERRUPT, 0); if (err != B_OK) { dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__, strerror(err)); return err; } status = disable_interrupts(); acquire_spinlock(&controller->playback_stream->lock); buffer_info.playback_buffer_cycle = controller->playback_stream->buffer_cycle; buffer_info.played_real_time = controller->playback_stream->real_time; buffer_info.played_frames_count = controller->playback_stream->frames_count; release_spinlock(&controller->playback_stream->lock); if (controller->record_stream) { acquire_spinlock(&controller->record_stream->lock); buffer_info.record_buffer_cycle = controller->record_stream->buffer_cycle; buffer_info.recorded_real_time = controller->record_stream->real_time; buffer_info.recorded_frames_count = controller->record_stream->frames_count; release_spinlock(&controller->record_stream->lock); } restore_interrupts(status); #ifdef __HAIKU__ if (user_memcpy(data, &buffer_info, sizeof(buffer_info)) < B_OK) return B_BAD_ADDRESS; #else memcpy(data, &buffer_info, sizeof(buffer_info)); #endif debug_buffers_exchanged++; if (((debug_buffers_exchanged % 100) == 1) && (debug_buffers_exchanged < 1111)) { dprintf("%s: %d buffers processed\n", __func__, debug_buffers_exchanged); } return B_OK; } static status_t buffer_force_stop(geode_controller* controller) { if (controller->playback_stream != NULL) { geode_stream_stop(controller->playback_stream); } if (controller->record_stream != NULL) { geode_stream_stop(controller->record_stream); } return B_OK; } status_t multi_audio_control(geode_controller* controller, uint32 op, void* arg, size_t len) { // TODO: make userland-safe when built for Haiku! switch (op) { case B_MULTI_GET_DESCRIPTION: { #ifdef __HAIKU__ multi_description description; multi_channel_info channels[16]; multi_channel_info* originalChannels; if (user_memcpy(&description, arg, sizeof(multi_description)) != B_OK) return B_BAD_ADDRESS; originalChannels = description.channels; description.channels = channels; if (description.request_channel_count > 16) description.request_channel_count = 16; status_t status = get_description(controller, &description); if (status != B_OK) return status; description.channels = originalChannels; if (user_memcpy(arg, &description, sizeof(multi_description)) != B_OK) return B_BAD_ADDRESS; return user_memcpy(originalChannels, channels, sizeof(multi_channel_info) * description.request_channel_count); #else return get_description(controller, (multi_description*)arg); #endif } case B_MULTI_GET_ENABLED_CHANNELS: return get_enabled_channels(controller, (multi_channel_enable*)arg); case B_MULTI_SET_ENABLED_CHANNELS: return B_OK; case B_MULTI_GET_GLOBAL_FORMAT: return get_global_format(controller, (multi_format_info*)arg); case B_MULTI_SET_GLOBAL_FORMAT: return set_global_format(controller, (multi_format_info*)arg); case B_MULTI_LIST_MIX_CHANNELS: return list_mix_channels(controller, (multi_mix_channel_info*)arg); case B_MULTI_LIST_MIX_CONTROLS: return list_mix_controls(controller, (multi_mix_control_info*)arg); case B_MULTI_LIST_MIX_CONNECTIONS: return list_mix_connections(controller, (multi_mix_connection_info*)arg); case B_MULTI_GET_MIX: return get_mix(controller, (multi_mix_value_info *)arg); case B_MULTI_SET_MIX: return set_mix(controller, (multi_mix_value_info *)arg); case B_MULTI_GET_BUFFERS: return get_buffers(controller, (multi_buffer_list*)arg); case B_MULTI_BUFFER_EXCHANGE: return buffer_exchange(controller, (multi_buffer_info*)arg); case B_MULTI_BUFFER_FORCE_STOP: return buffer_force_stop(controller); case B_MULTI_GET_EVENT_INFO: case B_MULTI_SET_EVENT_INFO: case B_MULTI_GET_EVENT: case B_MULTI_GET_CHANNEL_FORMATS: case B_MULTI_SET_CHANNEL_FORMATS: case B_MULTI_SET_BUFFERS: case B_MULTI_SET_START_TIME: return B_ERROR; } return B_BAD_VALUE; }