status_t AudioGain::checkConfig(const struct audio_gain_config *config) { if ((config->mode & ~mGain.mode) != 0) { return BAD_VALUE; } if ((config->mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { if ((config->values[0] < mGain.min_value) || (config->values[0] > mGain.max_value)) { return BAD_VALUE; } } else { if ((config->channel_mask & ~mGain.channel_mask) != 0) { return BAD_VALUE; } uint32_t numValues; if (mUseInChannelMask) { numValues = audio_channel_count_from_in_mask(config->channel_mask); } else { numValues = audio_channel_count_from_out_mask(config->channel_mask); } for (size_t i = 0; i < numValues; i++) { if ((config->values[i] < mGain.min_value) || (config->values[i] > mGain.max_value)) { return BAD_VALUE; } } } if ((config->mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { if ((config->ramp_duration_ms < mGain.min_ramp_ms) || (config->ramp_duration_ms > mGain.max_ramp_ms)) { return BAD_VALUE; } } return NO_ERROR; }
ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes) { // fake timing for audio output usleep(bytes * 1000000 / sizeof(int16_t) / audio_channel_count_from_out_mask(channels()) / sampleRate()); return bytes; }
void AudioPort::pickChannelMask(audio_channel_mask_t &pickedChannelMask, const ChannelsVector &channelMasks) const { pickedChannelMask = AUDIO_CHANNEL_NONE; // For direct outputs, pick minimum channel count: this helps ensuring that the // channel count / sampling rate combination chosen will be supported by the connected // sink if (isDirectOutput()) { uint32_t channelCount = UINT_MAX; for (size_t i = 0; i < channelMasks.size(); i ++) { uint32_t cnlCount; if (useInputChannelMask()) { cnlCount = audio_channel_count_from_in_mask(channelMasks[i]); } else { cnlCount = audio_channel_count_from_out_mask(channelMasks[i]); } if ((cnlCount < channelCount) && (cnlCount > 0)) { pickedChannelMask = channelMasks[i]; channelCount = cnlCount; } } } else { uint32_t channelCount = 0; uint32_t maxCount = MAX_MIXER_CHANNEL_COUNT; // For mixed output and inputs, use max mixer channel count. Do not // limit channel count otherwise if (mType != AUDIO_PORT_TYPE_MIX) { maxCount = UINT_MAX; } for (size_t i = 0; i < channelMasks.size(); i ++) { uint32_t cnlCount; if (useInputChannelMask()) { cnlCount = audio_channel_count_from_in_mask(channelMasks[i]); } else { cnlCount = audio_channel_count_from_out_mask(channelMasks[i]); } if ((cnlCount > channelCount) && (cnlCount <= maxCount)) { pickedChannelMask = channelMasks[i]; channelCount = cnlCount; } } } }
RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask, audio_channel_mask_t outputChannelMask, audio_format_t format, size_t bufferFrameCount) : CopyBufferProvider( audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask), audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask), bufferFrameCount), mFormat(format), mSampleSize(audio_bytes_per_sample(format)), mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)), mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask)) { ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu", this, format, inputChannelMask, outputChannelMask, mInputChannels, mOutputChannels); (void) memcpy_by_index_array_initialization_from_channel_mask( mIdxAry, ARRAY_SIZE(mIdxAry), outputChannelMask, inputChannelMask); }
// Play audio from a WAV file. // // Parameters: // out_stream: A pointer to the output audio stream. // in_file: A pointer to a SNDFILE object. // config: A pointer to struct that contains audio configuration data. // // Returns: An int which has a non-negative number on success. int PlayFile(audio_stream_out_t* out_stream, SNDFILE* in_file, audio_config_t* config) { size_t buffer_size = out_stream->common.get_buffer_size(&out_stream->common); size_t kFrameSize = audio_bytes_per_sample(kAudioPlaybackFormat) * audio_channel_count_from_out_mask(config->channel_mask); short* data = new short[buffer_size / kFrameSize]; int rc = 0; sf_count_t frames_read = 1; while (frames_read != 0) { size_t bytes_wanted = out_stream->common.get_buffer_size(&out_stream->common); frames_read = sf_readf_short(in_file, data, bytes_wanted / kFrameSize); rc = out_stream->write(out_stream, data, frames_read * kFrameSize); if (rc < 0) { LOG(ERROR) << "Writing data to hal failed. (" << strerror(rc) << ")"; break; } } return rc; }
// Play a sine wave. // // Parameters: // out_stream: A pointer to the output audio stream. // config: A pointer to struct that contains audio configuration data. // // Returns: An int which has a non-negative number on success. int PlaySineWave(audio_stream_out_t* out_stream, audio_config_t* config) { // Get buffer size and generate data. size_t buffer_size = out_stream->common.get_buffer_size(&out_stream->common); int num_channels = audio_channel_count_from_out_mask(config->channel_mask); uint8_t* data = GenerateData(config->sample_rate, num_channels, buffer_size); const size_t kNumBuffersToWrite = 1000; int rc = 0; // Write kNumBuffersToWrite buffers to the audio hal. for (size_t i = 0; i < kNumBuffersToWrite; i++) { size_t bytes_wanted = out_stream->common.get_buffer_size(&out_stream->common); rc = out_stream->write( out_stream, data, bytes_wanted <= buffer_size ? bytes_wanted : buffer_size); if (rc < 0) { LOG(ERROR) << "Writing data to hal failed. (" << strerror(rc) << ")"; break; } } return rc; }
void AudioGain::getDefaultConfig(struct audio_gain_config *config) { config->index = mIndex; config->mode = mGain.mode; config->channel_mask = mGain.channel_mask; if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { config->values[0] = mGain.default_value; } else { uint32_t numValues; if (mUseInChannelMask) { numValues = audio_channel_count_from_in_mask(mGain.channel_mask); } else { numValues = audio_channel_count_from_out_mask(mGain.channel_mask); } for (size_t i = 0; i < numValues; i++) { config->values[i] = mGain.default_value; } } if ((mGain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { config->ramp_duration_ms = mGain.min_ramp_ms; } }
static int adev_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out, const char *address /*__unused*/) { ALOGV("adev_open_output_stream() handle:0x%X, device:0x%X, flags:0x%X, addr:%s", handle, devices, flags, address); struct audio_device *adev = (struct audio_device *)dev; struct stream_out *out; out = (struct stream_out *)calloc(1, sizeof(struct stream_out)); if (!out) return -ENOMEM; /* setup function pointers */ out->stream.common.get_sample_rate = out_get_sample_rate; out->stream.common.set_sample_rate = out_set_sample_rate; out->stream.common.get_buffer_size = out_get_buffer_size; out->stream.common.get_channels = out_get_channels; out->stream.common.get_format = out_get_format; out->stream.common.set_format = out_set_format; out->stream.common.standby = out_standby; out->stream.common.dump = out_dump; out->stream.common.set_parameters = out_set_parameters; out->stream.common.get_parameters = out_get_parameters; out->stream.common.add_audio_effect = out_add_audio_effect; out->stream.common.remove_audio_effect = out_remove_audio_effect; out->stream.get_latency = out_get_latency; out->stream.set_volume = out_set_volume; out->stream.write = out_write; out->stream.get_render_position = out_get_render_position; out->stream.get_presentation_position = out_get_presentation_position; out->stream.get_next_write_timestamp = out_get_next_write_timestamp; pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL); pthread_mutex_init(&out->pre_lock, (const pthread_mutexattr_t *) NULL); out->dev = adev; pthread_mutex_lock(&adev->lock); out->profile = &adev->out_profile; // build this to hand to the alsa_device_proxy struct pcm_config proxy_config; memset(&proxy_config, 0, sizeof(proxy_config)); /* Pull out the card/device pair */ parse_card_device_params(true, &(out->profile->card), &(out->profile->device)); profile_read_device_info(out->profile); pthread_mutex_unlock(&adev->lock); int ret = 0; /* Rate */ if (config->sample_rate == 0) { proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(out->profile); } else if (profile_is_sample_rate_valid(out->profile, config->sample_rate)) { proxy_config.rate = config->sample_rate; } else { ALOGE("%s: The requested sample rate (%d) is not valid", __func__, config->sample_rate); proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(out->profile); ret = -EINVAL; } /* Format */ if (config->format == AUDIO_FORMAT_DEFAULT) { proxy_config.format = profile_get_default_format(out->profile); config->format = audio_format_from_pcm_format(proxy_config.format); } else { enum pcm_format fmt = pcm_format_from_audio_format(config->format); if (profile_is_format_valid(out->profile, fmt)) { proxy_config.format = fmt; } else { ALOGE("%s: The requested format (0x%x) is not valid", __func__, config->format); proxy_config.format = profile_get_default_format(out->profile); config->format = audio_format_from_pcm_format(proxy_config.format); ret = -EINVAL; } } /* Channels */ unsigned proposed_channel_count = 0; if (k_force_channels) { proposed_channel_count = k_force_channels; } else if (config->channel_mask == AUDIO_CHANNEL_NONE) { proposed_channel_count = profile_get_default_channel_count(out->profile); } if (proposed_channel_count != 0) { if (proposed_channel_count <= FCC_2) { // use channel position mask for mono and stereo config->channel_mask = audio_channel_out_mask_from_count(proposed_channel_count); } else { // use channel index mask for multichannel config->channel_mask = audio_channel_mask_for_index_assignment_from_count(proposed_channel_count); } out->hal_channel_count = proposed_channel_count; } else { out->hal_channel_count = audio_channel_count_from_out_mask(config->channel_mask); } /* we can expose any channel mask, and emulate internally based on channel count. */ out->hal_channel_mask = config->channel_mask; /* no validity checks are needed as proxy_prepare() forces channel_count to be valid. * and we emulate any channel count discrepancies in out_write(). */ proxy_config.channels = proposed_channel_count; #if TARGET_AUDIO_PRIMARY out->profile->default_config.period_count = 4; #endif proxy_prepare(&out->proxy, out->profile, &proxy_config); /* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger. */ ret = 0; out->conversion_buffer = NULL; out->conversion_buffer_size = 0; out->standby = true; *stream_out = &out->stream; return ret; err_open: free(out); *stream_out = NULL; return -ENOSYS; }
DownmixerBufferProvider::DownmixerBufferProvider( audio_channel_mask_t inputChannelMask, audio_channel_mask_t outputChannelMask, audio_format_t format, uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) : CopyBufferProvider( audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask), audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask), bufferFrameCount) // set bufferFrameCount to 0 to do in-place { ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)", this, inputChannelMask, outputChannelMask, format, sampleRate, sessionId); if (!sIsMultichannelCapable || EffectCreate(&sDwnmFxDesc.uuid, sessionId, SESSION_ID_INVALID_AND_IGNORED, &mDownmixHandle) != 0) { ALOGE("DownmixerBufferProvider() error creating downmixer effect"); mDownmixHandle = NULL; return; } // channel input configuration will be overridden per-track mDownmixConfig.inputCfg.channels = inputChannelMask; // FIXME: Should be bits mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits mDownmixConfig.inputCfg.format = format; mDownmixConfig.outputCfg.format = format; mDownmixConfig.inputCfg.samplingRate = sampleRate; mDownmixConfig.outputCfg.samplingRate = sampleRate; mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; // input and output buffer provider, and frame count will not be used as the downmix effect // process() function is called directly (see DownmixerBufferProvider::getNextBuffer()) mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE; mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask; int cmdStatus; uint32_t replySize = sizeof(int); // Configure downmixer status_t status = (*mDownmixHandle)->command(mDownmixHandle, EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/, &mDownmixConfig /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/); if (status != 0 || cmdStatus != 0) { ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer", status, cmdStatus); EffectRelease(mDownmixHandle); mDownmixHandle = NULL; return; } // Enable downmixer replySize = sizeof(int); status = (*mDownmixHandle)->command(mDownmixHandle, EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/); if (status != 0 || cmdStatus != 0) { ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer", status, cmdStatus); EffectRelease(mDownmixHandle); mDownmixHandle = NULL; return; } // Set downmix type // parameter size rounded for padding on 32bit boundary const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int); const int downmixParamSize = sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t); effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize); CHECK(param != NULL); param->psize = sizeof(downmix_params_t); const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE; memcpy(param->data, &downmixParam, param->psize); const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD; param->vsize = sizeof(downmix_type_t); memcpy(param->data + psizePadded, &downmixType, param->vsize); replySize = sizeof(int); status = (*mDownmixHandle)->command(mDownmixHandle, EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */, param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/); free(param); if (status != 0 || cmdStatus != 0) { ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type", status, cmdStatus); EffectRelease(mDownmixHandle); mDownmixHandle = NULL; return; } ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType); }