4450 lines
132 KiB
C
Executable File
4450 lines
132 KiB
C
Executable File
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* @file audio_hw.c
|
|
* @brief
|
|
* ALSA Audio Git Log
|
|
* - V0.1.0:add alsa audio hal,just support 312x now.
|
|
* - V0.2.0:remove unused variable.
|
|
* - V0.3.0:turn off device when do_standby.
|
|
* - V0.4.0:turn off device before open pcm.
|
|
* - V0.4.1:Need to re-open the control to fix no sound when suspend.
|
|
* - V0.5.0:Merge the mixer operation from legacy_alsa.
|
|
* - V0.6.0:Merge speex denoise from legacy_alsa.
|
|
* - V0.7.0:add copyright.
|
|
* - V0.7.1:add support for box audio
|
|
* - V0.7.2:add support for dircet output
|
|
* - V0.8.0:update the direct output for box, add the DVI mode
|
|
* - V1.0.0:stable version
|
|
*
|
|
* @author RkAudio
|
|
* @version 1.0.5
|
|
* @date 2015-08-24
|
|
*/
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#ifdef PRIMARY_HAL
|
|
#define LOG_TAG "modules.primary.audio_hal"
|
|
#endif
|
|
#ifdef EXT_1_HAL
|
|
#define LOG_TAG "modules.ext_1.audio_hal"
|
|
#endif
|
|
#ifdef EXT_2_HAL
|
|
#define LOG_TAG "modules.ext_2.audio_hal"
|
|
#endif
|
|
#ifdef EXT_3_HAL
|
|
#define LOG_TAG "modules.ext_3.audio_hal"
|
|
#endif
|
|
#ifdef EXT_4_HAL
|
|
#define LOG_TAG "modules.ext_4.audio_hal"
|
|
#endif
|
|
|
|
#include "alsa_audio.h"
|
|
#include "audio_hw.h"
|
|
#include <system/audio.h>
|
|
#include "codec_config/config.h"
|
|
#include "utils/audio_time.h"
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#define SNDRV_CARDS 8
|
|
#define SNDRV_DEVICES 8
|
|
|
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
|
#define SND_CARDS_NODE "/proc/asound/cards"
|
|
#define SAMPLECOUNT 441*5*2*2
|
|
|
|
#define CHR_VALID (1 << 1)
|
|
#define CHL_VALID (1 << 0)
|
|
#define CH_CHECK (1 << 2)
|
|
|
|
#define HDMI_BITSTREAM_BYPASS "ELD Bypass Switch"
|
|
|
|
static struct pcm_config pcm_config = {
|
|
.channels = 2,
|
|
.rate = 48000,
|
|
.period_size = 480,
|
|
.period_count = 4,
|
|
.format = PCM_FORMAT_S16_LE,
|
|
};
|
|
|
|
static struct pcm_config pcm_config_in = {
|
|
.channels = 2,
|
|
.rate = 48000,
|
|
.period_size = 480, // 10ms
|
|
.period_count = 4,
|
|
.format = PCM_FORMAT_S16_LE,
|
|
};
|
|
|
|
static struct pcm_config pcm_config_in_low_latency = {
|
|
.channels = 2,
|
|
.rate = 48000,
|
|
.period_size = 256,
|
|
.period_count = 4,
|
|
.format = PCM_FORMAT_S16_LE,
|
|
};
|
|
|
|
static struct pcm_config pcm_config_sco = {
|
|
.channels = 1,
|
|
.rate = 8000,
|
|
.period_size = 128,
|
|
.period_count = 4,
|
|
.format = PCM_FORMAT_S16_LE,
|
|
};
|
|
|
|
/* for bt client call*/
|
|
static struct pcm_config pcm_config_hfp = {
|
|
.channels = 2,
|
|
.rate = 48000,
|
|
.period_size = 256,
|
|
.period_count = 4,
|
|
};
|
|
|
|
static struct pcm_config pcm_config_ap_sco = {
|
|
.channels = 2,
|
|
.rate = 8000,
|
|
.period_size = 80,
|
|
.period_count = 4,
|
|
};
|
|
|
|
static struct pcm_config pcm_config_in_bt = {
|
|
.channels = 2,
|
|
.rate = 8000,
|
|
.period_size = 120,
|
|
.period_count = 4,
|
|
.format = PCM_FORMAT_S16_LE,
|
|
};
|
|
|
|
static struct pcm_config pcm_config_deep = {
|
|
.channels = 2,
|
|
.rate = 48000,
|
|
/* FIXME This is an arbitrary number, may change.
|
|
* Dynamic configuration based on screen on/off is not implemented;
|
|
* let's see what power consumption is first to see if necessary.
|
|
*/
|
|
.period_size = 8192,
|
|
.period_count = 4,
|
|
.format = PCM_FORMAT_S16_LE,
|
|
};
|
|
|
|
static struct pcm_config pcm_config_hdmi_multi = {
|
|
.channels = 6, /* changed when the stream is opened */
|
|
.rate = HDMI_MULTI_DEFAULT_SAMPLING_RATE,
|
|
.period_size = 1024,
|
|
.period_count = 4,
|
|
.format = PCM_FORMAT_S16_LE,
|
|
};
|
|
|
|
static struct pcm_config pcm_config_direct = {
|
|
.channels = 2,
|
|
.rate = 48000,
|
|
.period_size = 1024,
|
|
.period_count = 4,
|
|
|
|
#ifdef IEC958_FORAMT
|
|
.format = PCM_FORMAT_IEC958_SUBFRAME_LE,
|
|
#else
|
|
.format = PCM_FORMAT_S24_LE,
|
|
#endif
|
|
};
|
|
|
|
struct SurroundFormat {
|
|
audio_format_t format;
|
|
const char *value;
|
|
};
|
|
|
|
|
|
const struct SurroundFormat sSurroundFormat[] = {
|
|
{AUDIO_FORMAT_AC3,"AUDIO_FORMAT_AC3"},
|
|
{AUDIO_FORMAT_E_AC3,"AUDIO_FORMAT_E_AC3"},
|
|
{AUDIO_FORMAT_DTS,"AUDIO_FORMAT_DTS"},
|
|
{AUDIO_FORMAT_DTS_HD,"AUDIO_FORMAT_DTS_HD"},
|
|
{AUDIO_FORMAT_AAC_LC,"AUDIO_FORMAT_AAC_LC"},
|
|
{AUDIO_FORMAT_DOLBY_TRUEHD,"AUDIO_FORMAT_DOLBY_TRUEHD"},
|
|
{AUDIO_FORMAT_AC4,"AUDIO_FORMAT_E_AC3_JOC"}
|
|
};
|
|
|
|
enum SOUND_CARD_OWNER{
|
|
SOUND_CARD_HDMI = 0,
|
|
SOUND_CARD_SPDIF = 1,
|
|
};
|
|
|
|
/*
|
|
* mute audio datas when screen off or standby
|
|
* The MediaPlayer no stop/pause when screen off, they may be just play in background,
|
|
* so they still send audio datas to audio hal.
|
|
* HDMI may disconnet and enter stanby status, this means no voice output on HDMI
|
|
* but speaker/av and spdif still work, and voice may output on them.
|
|
* Some customer need to mute the audio datas in this condition.
|
|
* If need mute datas when screen off, define this marco.
|
|
*/
|
|
//#define MUTE_WHEN_SCREEN_OFF
|
|
|
|
/*
|
|
* if current audio stream bitstream over hdmi,
|
|
* and hdmi is removed and reconnected later,
|
|
* the driver of hdmi may config it with pcm mode automatically,
|
|
* which is according the implement of hdmi driver.
|
|
* If hdmi driver implement in this way, in order to output audio
|
|
* bitstream stream after hdmi reconnected,
|
|
* we must close sound card of hdmi and reopen/config
|
|
* it in bitstream mode. If need this, define this macro.
|
|
*/
|
|
#define AUDIO_BITSTREAM_REOPEN_HDMI
|
|
|
|
//#define ALSA_DEBUG
|
|
#ifdef ALSA_IN_DEBUG
|
|
FILE *in_debug;
|
|
#endif
|
|
|
|
int in_dump(const struct audio_stream *stream, int fd);
|
|
int out_dump(const struct audio_stream *stream, int fd);
|
|
|
|
/**
|
|
* @brief get_output_device_id
|
|
*
|
|
* @param device
|
|
*
|
|
* @returns
|
|
*/
|
|
int get_output_device_id(audio_devices_t device)
|
|
{
|
|
if (device == AUDIO_DEVICE_NONE)
|
|
return OUT_DEVICE_NONE;
|
|
|
|
if (popcount(device) == 2) {
|
|
if ((device == (AUDIO_DEVICE_OUT_SPEAKER |
|
|
AUDIO_DEVICE_OUT_WIRED_HEADSET)) ||
|
|
(device == (AUDIO_DEVICE_OUT_SPEAKER |
|
|
AUDIO_DEVICE_OUT_WIRED_HEADPHONE)))
|
|
return OUT_DEVICE_SPEAKER_AND_HEADSET;
|
|
else
|
|
return OUT_DEVICE_NONE;
|
|
}
|
|
|
|
if (popcount(device) != 1)
|
|
return OUT_DEVICE_NONE;
|
|
|
|
switch (device) {
|
|
case AUDIO_DEVICE_OUT_SPEAKER:
|
|
return OUT_DEVICE_SPEAKER;
|
|
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
|
|
return OUT_DEVICE_HEADSET;
|
|
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
|
|
return OUT_DEVICE_HEADPHONES;
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
|
|
return OUT_DEVICE_BT_SCO;
|
|
default:
|
|
return OUT_DEVICE_NONE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief get_input_source_id
|
|
*
|
|
* @param source
|
|
*
|
|
* @returns
|
|
*/
|
|
int get_input_source_id(audio_source_t source)
|
|
{
|
|
switch (source) {
|
|
case AUDIO_SOURCE_DEFAULT:
|
|
return IN_SOURCE_NONE;
|
|
case AUDIO_SOURCE_MIC:
|
|
return IN_SOURCE_MIC;
|
|
case AUDIO_SOURCE_CAMCORDER:
|
|
return IN_SOURCE_CAMCORDER;
|
|
case AUDIO_SOURCE_VOICE_RECOGNITION:
|
|
return IN_SOURCE_VOICE_RECOGNITION;
|
|
case AUDIO_SOURCE_VOICE_COMMUNICATION:
|
|
return IN_SOURCE_VOICE_COMMUNICATION;
|
|
default:
|
|
return IN_SOURCE_NONE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief force_non_hdmi_out_standby
|
|
* must be called with hw device outputs list, all out streams, and hw device mutexes locked
|
|
*
|
|
* @param adev
|
|
*/
|
|
static void force_non_hdmi_out_standby(struct audio_device *adev)
|
|
{
|
|
enum output_type type;
|
|
struct stream_out *out;
|
|
for (type = 0; type < OUTPUT_TOTAL; ++type) {
|
|
out = adev->outputs[type];
|
|
if (type == OUTPUT_HDMI_MULTI|| !out)
|
|
continue;
|
|
/* This will never recurse more than 2 levels deep. */
|
|
do_out_standby(out);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @brief getOutputRouteFromDevice
|
|
*
|
|
* @param device
|
|
*
|
|
* @returns
|
|
*/
|
|
unsigned getOutputRouteFromDevice(uint32_t device)
|
|
{
|
|
switch (device) {
|
|
case AUDIO_DEVICE_OUT_SPEAKER:
|
|
return SPEAKER_NORMAL_ROUTE;
|
|
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
|
|
return HEADSET_NORMAL_ROUTE;
|
|
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
|
|
return HEADPHONE_NORMAL_ROUTE;
|
|
case (AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADPHONE):
|
|
case (AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET):
|
|
return SPEAKER_HEADPHONE_NORMAL_ROUTE;
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
|
|
return BLUETOOTH_NORMAL_ROUTE;
|
|
case AUDIO_DEVICE_OUT_AUX_DIGITAL:
|
|
#ifdef SUPPORT_VX_ROCKCHIP
|
|
case VX_ROCKCHIP_OUT_HDMI0:
|
|
#endif
|
|
return HDMI_NORMAL_ROUTE;
|
|
default:
|
|
return PLAYBACK_OFF_ROUTE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief getVoiceRouteFromDevice
|
|
*
|
|
* @param device
|
|
*
|
|
* @returns
|
|
*/
|
|
uint32_t getVoiceRouteFromDevice(uint32_t device)
|
|
{
|
|
ALOGE("not support now");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief getInputRouteFromDevice
|
|
*
|
|
* @param device
|
|
*
|
|
* @returns
|
|
*/
|
|
uint32_t getInputRouteFromDevice(uint32_t device)
|
|
{
|
|
/*if (mMicMute) {
|
|
return CAPTURE_OFF_ROUTE;
|
|
}*/
|
|
ALOGE("%s:device:%x",__FUNCTION__,device);
|
|
switch (device) {
|
|
case AUDIO_DEVICE_IN_BUILTIN_MIC:
|
|
return MAIN_MIC_CAPTURE_ROUTE;
|
|
case AUDIO_DEVICE_IN_WIRED_HEADSET:
|
|
return HANDS_FREE_MIC_CAPTURE_ROUTE;
|
|
case AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET:
|
|
return BLUETOOTH_SOC_MIC_CAPTURE_ROUTE;
|
|
case AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET:
|
|
return USB_CAPTURE_ROUTE;
|
|
case AUDIO_DEVICE_IN_HDMI:
|
|
return HDMI_IN_CAPTURE_ROUTE;
|
|
default:
|
|
return CAPTURE_OFF_ROUTE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief getRouteFromDevice
|
|
*
|
|
* @param device
|
|
*
|
|
* @returns
|
|
*/
|
|
uint32_t getRouteFromDevice(uint32_t device)
|
|
{
|
|
if (device & AUDIO_DEVICE_BIT_IN)
|
|
return getInputRouteFromDevice(device);
|
|
else
|
|
return getOutputRouteFromDevice(device);
|
|
}
|
|
|
|
static struct stream_out* adev_get_stream_out_by_io_handle_l(
|
|
struct audio_device* adev, audio_io_handle_t handle) {
|
|
struct listnode *node;
|
|
list_for_each (node, &adev->output_stream_list) {
|
|
struct stream_out *out = node_to_item(node, struct stream_out, list_node);
|
|
if (out->handle == handle) {
|
|
return out;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool is_type_in_outdevices(struct stream_out *out, audio_devices_t type) {
|
|
for (int i = 0; i < out->num_configs; ++i)
|
|
{
|
|
if (out->devices[i] == type) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static struct stream_in* adev_get_stream_in_by_io_handle_l(
|
|
struct audio_device* adev, audio_io_handle_t handle) {
|
|
struct listnode *node;
|
|
list_for_each (node, &adev->input_stream_list) {
|
|
struct stream_in *in = node_to_item(node, struct stream_in, list_node);
|
|
if (in->handle == handle) {
|
|
return in;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct stream_out* adev_get_stream_out_by_patch_handle_l(
|
|
struct audio_device* adev, audio_patch_handle_t patch_handle) {
|
|
struct listnode *node;
|
|
list_for_each (node, &adev->output_stream_list) {
|
|
struct stream_out *out = node_to_item(node, struct stream_out, list_node);
|
|
if (out->patch_handle == patch_handle) {
|
|
return out;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void device_lock(struct audio_device *adev) {
|
|
pthread_mutex_lock(&adev->lock);
|
|
}
|
|
|
|
static int device_try_lock(struct audio_device *adev) {
|
|
return pthread_mutex_trylock(&adev->lock);
|
|
}
|
|
|
|
static void device_unlock(struct audio_device *adev) {
|
|
pthread_mutex_unlock(&adev->lock);
|
|
}
|
|
|
|
static struct stream_in* adev_get_stream_in_by_patch_handle_l(
|
|
struct audio_device* adev, audio_patch_handle_t patch_handle) {
|
|
struct listnode *node;
|
|
list_for_each (node, &adev->input_stream_list) {
|
|
struct stream_in *in = node_to_item(node, struct stream_in, list_node);
|
|
if (in->patch_handle == patch_handle) {
|
|
return in;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* streams list management
|
|
*/
|
|
static void adev_add_stream_to_list(
|
|
struct audio_device* adev, struct listnode* list, struct listnode* stream_node) {
|
|
device_lock(adev);
|
|
list_add_tail(list, stream_node);
|
|
device_unlock(adev);
|
|
}
|
|
|
|
struct dev_proc_info SPEAKER_OUT_NAME[] = /* add codes& dai name here*/
|
|
{
|
|
{"rockchipcarrk33", NULL},
|
|
{"realtekrt5616c", NULL,},
|
|
{"rockchiprt5651c", "rt5651-aif1",},
|
|
{"realtekrt5670c", NULL,},
|
|
{"realtekrt5672c", NULL,},
|
|
{"realtekrt5678co", NULL,},
|
|
{"rkhdmianalogsnd", NULL,},
|
|
{"rockchipcx2072x", NULL,},
|
|
{"rockchipes8316c", NULL,},
|
|
{"rockchipes8323c", NULL,},
|
|
{"rockchipes8388c", NULL,},
|
|
{"rockchipes8388", NULL,},
|
|
{"rockchipes8396c", NULL,},
|
|
{"rockchiprk", NULL, },
|
|
{"rockchiprk809co", NULL,},
|
|
{"rockchiprk817co", NULL,},
|
|
{"rockchiprt5640c", "rt5640-aif1",},
|
|
{"rockchipes8311", NULL,},
|
|
{"rockchiprt5670c", NULL,},
|
|
{"rockchiprt5672c", NULL,},
|
|
{"rk3528acodec", NULL},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
struct dev_proc_info HDMI_OUT_NAME[] =
|
|
{
|
|
{"rockchiprt5651c", "i2s-hifi",},
|
|
{"realtekrt5670co", "i2s-hifi",},
|
|
{"rkhdmidpsound", NULL,},
|
|
{"hdmisound", NULL},
|
|
{"rockchiphdmi", NULL,},
|
|
{"rockchiphdmi0", NULL,},
|
|
{"rockchiprt5640c", "i2s-hifi",},
|
|
{"rockchipes8311", "i2s-hifi",},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
struct dev_proc_info HDMI_1_OUT_NAME[] =
|
|
{
|
|
{"rockchiphdmi1", NULL,},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
struct dev_proc_info SPDIF_OUT_NAME[] =
|
|
{
|
|
{"ROCKCHIPSPDIF", "dit-hifi",},
|
|
{"rockchipspdif", NULL,},
|
|
{"rockchipcdndp", NULL,},
|
|
{"rockchipdp0", NULL,},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
struct dev_proc_info SPDIF_1_OUT_NAME[] =
|
|
{
|
|
{"rockchipdp1", NULL,},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
struct dev_proc_info BT_OUT_NAME[] =
|
|
{
|
|
{"rockchipbt", NULL,},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
struct dev_proc_info MIC_IN_NAME[] =
|
|
{
|
|
{"rockchipcarrk33", NULL},
|
|
{"realtekrt5616c", NULL,},
|
|
{"rockchiprt5651c", "rt5651-aif1",},
|
|
{"realtekrt5670c", NULL,},
|
|
{"realtekrt5672c", NULL,},
|
|
{"realtekrt5678co", NULL,},
|
|
{"rockchipes8316c", NULL,},
|
|
{"rockchipes8323c", NULL,},
|
|
{"rockchipes8388c", NULL,},
|
|
{"rockchipes8388", NULL,},
|
|
{"rockchipes8396c", NULL,},
|
|
{"rockchipes7210", NULL,},
|
|
{"rockchipes7243", NULL,},
|
|
{"rockchiprk", NULL, },
|
|
{"rockchiprk809co", NULL,},
|
|
{"rockchiprk817co", NULL,},
|
|
{"rockchiprt5640c", NULL,},
|
|
{"rockchipes8311", NULL},
|
|
{"rockchiprt5670c", NULL,},
|
|
{"rockchiprt5672c", NULL,},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
struct dev_proc_info HDMI_IN_NAME[] =
|
|
{
|
|
{"rockchiprt5651c", "tc358749x-audio"},
|
|
{"hdmiin", NULL},
|
|
{"rockchiphdmirx", NULL},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
struct dev_proc_info BT_IN_NAME[] =
|
|
{
|
|
{"rockchipbt", NULL},
|
|
{NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
|
|
};
|
|
|
|
static int name_match(const char* dst, const char* src)
|
|
{
|
|
int score = 0;
|
|
// total equal
|
|
if (!strcmp(dst, src)) {
|
|
score = 100;
|
|
} else if (strstr(dst, src)) {
|
|
// part equal
|
|
score = 50;
|
|
}
|
|
|
|
return score;
|
|
}
|
|
|
|
static bool is_specified_out_sound_card(char *id, struct dev_proc_info *match)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!match)
|
|
return true; /* match any */
|
|
|
|
while (match[i].cid) {
|
|
if (!strcmp(id, match[i].cid)) {
|
|
return true;
|
|
}
|
|
i++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool dev_id_match(const char *info, const char *did)
|
|
{
|
|
const char *deli = "id:";
|
|
char *id;
|
|
int idx = 0;
|
|
|
|
if (!did)
|
|
return true;
|
|
if (!info)
|
|
return false;
|
|
/* find str like-> id: ff880000.i2s-rt5651-aif1 rt5651-aif1-0 */
|
|
id = strstr(info, deli);
|
|
if (!id)
|
|
return false;
|
|
id += strlen(deli);
|
|
while(id[idx] != '\0') {
|
|
if (id[idx] == '\r' ||id[idx] == '\n') {
|
|
id[idx] = '\0';
|
|
break;
|
|
}
|
|
idx ++;
|
|
}
|
|
if (strstr(id, did)) {
|
|
ALOGE("match dai!!!: %s %s", id, did);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool get_specified_out_dev(struct dev_info *devinfo,
|
|
int card,
|
|
const char *id,
|
|
struct dev_proc_info *match)
|
|
{
|
|
int i = 0;
|
|
int device;
|
|
char str_device[32];
|
|
char info[256];
|
|
size_t len;
|
|
FILE* file = NULL;
|
|
int score = 0;
|
|
int better = devinfo->score;
|
|
int index = -1;
|
|
|
|
/* parse card id */
|
|
if (!match)
|
|
return true; /* match any */
|
|
while (match[i].cid) {
|
|
score = name_match(id, match[i].cid);
|
|
if (score > better) {
|
|
better = score;
|
|
index = i;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (index < 0)
|
|
return false;
|
|
|
|
if (!match[index].cid)
|
|
return false;
|
|
|
|
if (!match[index].did) { /* no exist dai info, exit */
|
|
devinfo->card = card;
|
|
devinfo->device = 0;
|
|
devinfo->score = better;
|
|
ALOGD("%s card, got card=%d,device=%d", devinfo->id,
|
|
devinfo->card, devinfo->device);
|
|
return true;
|
|
}
|
|
|
|
/* parse device id */
|
|
for (device = 0; device < SNDRV_DEVICES; device++) {
|
|
sprintf(str_device, "proc/asound/card%d/pcm%dp/info", card, device);
|
|
if (access(str_device, 0)) {
|
|
continue;
|
|
}
|
|
file = fopen(str_device, "r");
|
|
if (!file) {
|
|
ALOGD("Could reading %s property", str_device);
|
|
continue;
|
|
}
|
|
len = fread(info, sizeof(char), sizeof(info)/sizeof(char), file);
|
|
fclose(file);
|
|
if (len == 0 || len > sizeof(info)/sizeof(char))
|
|
continue;
|
|
if (info[len - 1] == '\n') {
|
|
len--;
|
|
info[len] = '\0';
|
|
}
|
|
/* parse device dai */
|
|
if (dev_id_match(info, match[index].did)) {
|
|
devinfo->card = card;
|
|
devinfo->device = device;
|
|
devinfo->score = better;
|
|
ALOGD("%s card, got card=%d,device=%d", devinfo->id,
|
|
devinfo->card, devinfo->device);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool get_specified_in_dev(struct dev_info *devinfo,
|
|
int card,
|
|
const char *id,
|
|
struct dev_proc_info *match)
|
|
{
|
|
int i = 0;
|
|
int device;
|
|
char str_device[32];
|
|
char info[256];
|
|
size_t len;
|
|
FILE* file = NULL;
|
|
int score = 0;
|
|
int better = devinfo->score;
|
|
int index = -1;
|
|
|
|
/* parse card id */
|
|
if (!match)
|
|
return true; /* match any */
|
|
|
|
while (match[i].cid) {
|
|
score = name_match(id, match[i].cid);
|
|
if (score > better) {
|
|
better = score;
|
|
index = i;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (index < 0)
|
|
return false;
|
|
|
|
if (!match[index].cid)
|
|
return false;
|
|
|
|
if (!match[index].did) { /* no exist dai info, exit */
|
|
devinfo->card = card;
|
|
devinfo->device = 0;
|
|
devinfo->score = better;
|
|
ALOGD("%s card, got card=%d,device=%d", devinfo->id,
|
|
devinfo->card, devinfo->device);
|
|
return true;
|
|
}
|
|
|
|
/* parse device id */
|
|
for (device = 0; device < SNDRV_DEVICES; device++) {
|
|
sprintf(str_device, "proc/asound/card%d/pcm%dc/info", card, device);
|
|
if (access(str_device, 0)) {
|
|
continue;
|
|
}
|
|
file = fopen(str_device, "r");
|
|
if (!file) {
|
|
ALOGD("Could reading %s property", str_device);
|
|
continue;
|
|
}
|
|
len = fread(info, sizeof(char), sizeof(info)/sizeof(char), file);
|
|
fclose(file);
|
|
if (len == 0 || len > sizeof(info)/sizeof(char))
|
|
continue;
|
|
if (info[len - 1] == '\n') {
|
|
len--;
|
|
info[len] = '\0';
|
|
}
|
|
/* parse device dai */
|
|
if (dev_id_match(info, match[i].did)) {
|
|
devinfo->card = card;
|
|
devinfo->device = device;
|
|
devinfo->score = better;
|
|
ALOGD("%s card, got card=%d,device=%d", devinfo->id,
|
|
devinfo->card, devinfo->device);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool is_specified_in_sound_card(char *id, struct dev_proc_info *match)
|
|
{
|
|
int i = 0;
|
|
|
|
/*
|
|
* mic: diffrent product may have diffrent card name,modify codes here
|
|
* for example: 0 [rockchiprk3328 ]: rockchip-rk3328 - rockchip-rk3328
|
|
*/
|
|
if (!match)
|
|
return true;/* match any */
|
|
while (match[i].cid) {
|
|
if (!strcmp(id, match[i].cid)) {
|
|
return true;
|
|
}
|
|
i++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void set_default_dev_info( struct dev_info *info, int size, int rid)
|
|
{
|
|
for(int i =0; i < size; i++) {
|
|
if (rid) {
|
|
info[i].id = NULL;
|
|
}
|
|
info[i].card = (int)SND_OUT_SOUND_CARD_UNKNOWN;
|
|
info[i].score = 0;
|
|
}
|
|
}
|
|
|
|
static void dumpdev_info(const char *tag, struct dev_info *devinfo, int count)
|
|
{
|
|
ALOGD("dump %s device info", tag);
|
|
for(int i = 0; i < count; i++) {
|
|
if (devinfo[i].id && devinfo[i].card != SND_OUT_SOUND_CARD_UNKNOWN)
|
|
ALOGD("dev_info %s card=%d, device:%d", devinfo[i].id,
|
|
devinfo[i].card,
|
|
devinfo[i].device);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get sound card infor by parser node: /proc/asound/cards
|
|
* the sound card number is not always the same value
|
|
*/
|
|
static void read_out_sound_card(struct stream_out *out)
|
|
{
|
|
|
|
struct audio_device *device = NULL;
|
|
int card = 0;
|
|
char str[32];
|
|
char id[20];
|
|
size_t len;
|
|
FILE* file = NULL;
|
|
|
|
if((out == NULL) || (out->dev == NULL)) {
|
|
return ;
|
|
}
|
|
device = out->dev;
|
|
set_default_dev_info(device->dev_out, SND_OUT_SOUND_CARD_UNKNOWN, 0);
|
|
for (card = 0; card < SNDRV_CARDS; card++) {
|
|
sprintf(str, "proc/asound/card%d/id", card);
|
|
if (access(str, 0)) {
|
|
continue;
|
|
}
|
|
file = fopen(str, "r");
|
|
if (!file) {
|
|
ALOGD("Could reading %s property", str);
|
|
continue;
|
|
}
|
|
len = fread(id, sizeof(char), sizeof(id)/sizeof(char), file);
|
|
fclose(file);
|
|
if (len == 0 || len > sizeof(id)/sizeof(char))
|
|
continue;
|
|
if (id[len - 1] == '\n') {
|
|
len--;
|
|
id[len] = '\0';
|
|
}
|
|
ALOGD("card%d id:%s", card, id);
|
|
get_specified_out_dev(&device->dev_out[SND_OUT_SOUND_CARD_SPEAKER], card, id, SPEAKER_OUT_NAME);
|
|
get_specified_out_dev(&device->dev_out[SND_OUT_SOUND_CARD_HDMI], card, id, HDMI_OUT_NAME);
|
|
get_specified_out_dev(&device->dev_out[SND_OUT_SOUND_CARD_HDMI_1], card, id, HDMI_1_OUT_NAME);
|
|
get_specified_out_dev(&device->dev_out[SND_OUT_SOUND_CARD_SPDIF], card, id, SPDIF_OUT_NAME);
|
|
get_specified_out_dev(&device->dev_out[SND_OUT_SOUND_CARD_SPDIF_1], card, id, SPDIF_1_OUT_NAME);
|
|
get_specified_out_dev(&device->dev_out[SND_OUT_SOUND_CARD_BT], card, id, BT_OUT_NAME);
|
|
}
|
|
dumpdev_info("out", device->dev_out, SND_OUT_SOUND_CARD_MAX);
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* get sound card infor by parser node: /proc/asound/cards
|
|
* the sound card number is not always the same value
|
|
*/
|
|
static void read_in_sound_card(struct stream_in *in)
|
|
{
|
|
struct audio_device *device = NULL;
|
|
int card = 0;
|
|
char str[32];
|
|
char id[20];
|
|
size_t len;
|
|
FILE* file = NULL;
|
|
|
|
if((in == NULL) || (in->dev == NULL)){
|
|
return ;
|
|
}
|
|
device = in->dev;
|
|
set_default_dev_info(device->dev_in, SND_IN_SOUND_CARD_UNKNOWN, 0);
|
|
for (card = 0; card < SNDRV_CARDS; card++) {
|
|
sprintf(str, "proc/asound/card%d/id", card);
|
|
if(access(str, 0)) {
|
|
continue;
|
|
}
|
|
file = fopen(str, "r");
|
|
if (!file) {
|
|
ALOGD("Could reading %s property", str);
|
|
continue;
|
|
}
|
|
len = fread(id, sizeof(char), sizeof(id)/sizeof(char), file);
|
|
fclose(file);
|
|
if (len == 0 || len > sizeof(id)/sizeof(char))
|
|
continue;
|
|
if (id[len - 1] == '\n') {
|
|
len--;
|
|
id[len] = '\0';
|
|
}
|
|
get_specified_in_dev(&device->dev_in[SND_IN_SOUND_CARD_MIC], card, id, MIC_IN_NAME);
|
|
/* set HDMI audio input info if need hdmi audio input */
|
|
get_specified_in_dev(&device->dev_in[SND_IN_SOUND_CARD_HDMI], card, id, HDMI_IN_NAME);
|
|
get_specified_in_dev(&device->dev_in[SND_IN_SOUND_CARD_BT], card, id, BT_IN_NAME);
|
|
}
|
|
dumpdev_info("in", device->dev_in, SND_IN_SOUND_CARD_MAX);
|
|
return ;
|
|
}
|
|
|
|
static uint32_t channel_check(void *data, unsigned int len)
|
|
{
|
|
short *pcmLeftChannel = (short*)data;
|
|
short *pcmRightChannel = pcmLeftChannel + 1;
|
|
unsigned int index = 0;
|
|
int leftValid = 0x0;
|
|
int rightValid = 0x0;
|
|
short valuel = 0;
|
|
short valuer = 0;
|
|
uint32_t validflag = 0;
|
|
|
|
valuel = *pcmLeftChannel;
|
|
valuer = *pcmRightChannel;
|
|
for (index = 0; index < len; index += 2) {
|
|
if ((pcmLeftChannel[index] >= valuel + 50) ||
|
|
(pcmLeftChannel[index] <= valuel - 50))
|
|
leftValid++;
|
|
if ((pcmRightChannel[index] >= valuer + 50) ||
|
|
(pcmRightChannel[index] <= valuer - 50))
|
|
rightValid++;
|
|
}
|
|
if (leftValid > 20)
|
|
validflag |= CHL_VALID;
|
|
if (rightValid > 20)
|
|
validflag |= CHR_VALID;
|
|
return validflag;
|
|
}
|
|
|
|
static void channel_fixed(void *data, unsigned len, uint32_t chFlag)
|
|
{
|
|
short *ch0 ,*ch1, *pcmValid, *pcmInvalid;
|
|
|
|
if ((chFlag&(CHL_VALID | CHR_VALID)) == 0 ||
|
|
(chFlag&(CHL_VALID | CHR_VALID)) == (CHL_VALID | CHR_VALID))
|
|
return;
|
|
ch0 = (short*)data;
|
|
ch1 = ch0 + 1;
|
|
pcmValid = ch0;
|
|
pcmInvalid = ch0;
|
|
if (chFlag & CHL_VALID)
|
|
pcmInvalid = ch1;
|
|
else if (chFlag & CHR_VALID)
|
|
pcmValid = ch1;
|
|
for (unsigned index = 0; index < len; index += 2) {
|
|
pcmInvalid[index] = pcmValid[index];
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void channel_check_start(struct stream_in *in)
|
|
{
|
|
in->channel_flag = CH_CHECK;
|
|
in->start_checkcount = 0;
|
|
}
|
|
|
|
static bool is_bitstream(struct stream_out *out)
|
|
{
|
|
if (out == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (out->config.format == PCM_FORMAT_IEC958_SUBFRAME_LE)
|
|
return true;
|
|
|
|
bool bitstream = false;
|
|
if (out->output_direct) {
|
|
switch(out->output_direct_mode) {
|
|
case HBR:
|
|
case NLPCM:
|
|
bitstream = true;
|
|
break;
|
|
case LPCM:
|
|
default:
|
|
bitstream = false;
|
|
break;
|
|
}
|
|
} else {
|
|
if(out->output_direct_mode != LPCM) {
|
|
ALOGD("%s: %d: error output_direct = false, but output_direct_mode != LPCM, this is error config",__FUNCTION__,__LINE__);
|
|
}
|
|
}
|
|
|
|
return bitstream;
|
|
}
|
|
|
|
static bool is_multi_pcm(struct stream_out *out)
|
|
{
|
|
if (out == NULL) {
|
|
return false;
|
|
}
|
|
|
|
bool multi = false;
|
|
if (out->output_direct && (out->output_direct_mode == LPCM) && (out->config.channels > 2)) {
|
|
multi = true;
|
|
}
|
|
|
|
return multi;
|
|
}
|
|
|
|
/**
|
|
* @brief mixer_hdmi_set_force_bypass
|
|
* force hdmi to bypass even if hdmi not support bypass
|
|
*/
|
|
static int mixer_hdmi_set_force_bypass(struct stream_out *out, int card)
|
|
{
|
|
int ret = 0;
|
|
struct mixer *pMixer = NULL;
|
|
struct mixer_ctl *pctl;
|
|
struct audio_device *adev = out->dev;
|
|
|
|
if (is_type_in_outdevices(out, AUDIO_DEVICE_OUT_AUX_DIGITAL) && card >= 0) {
|
|
pMixer = mixer_open_legacy(card);
|
|
if (!pMixer) {
|
|
return ret;
|
|
}
|
|
|
|
pctl = mixer_get_control(pMixer, HDMI_BITSTREAM_BYPASS, 0);
|
|
if (pctl != NULL) {
|
|
// do not care edid
|
|
if (is_bitstream(out)) {
|
|
ret = mixer_ctl_set_val(pctl, 1);
|
|
} else {
|
|
ret = mixer_ctl_set_val(pctl, 0);
|
|
}
|
|
}
|
|
mixer_close_legacy(pMixer);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief mixer_mode_set
|
|
* for rk3399 audio output mixer mode set
|
|
* @param out
|
|
*
|
|
* @return
|
|
*/
|
|
static int mixer_mode_set(struct stream_out *out)
|
|
{
|
|
int ret = 0;
|
|
struct mixer *pMixer = NULL;
|
|
struct mixer_ctl *pctl;
|
|
struct audio_device *adev = out->dev;
|
|
|
|
/*
|
|
* set audio mode for hdmi
|
|
* The driver of hdmi read the audio mode to identify
|
|
* the type of audio stream according to audio mode.
|
|
* 1) LPCM: the stream is pcm format
|
|
* 2) NLPCM: the stream is bitstream format, AC3/EAC3/DTS use this format
|
|
* 3) HDR: the stream is bitstream format, TrueHD/Atoms/DTS-HD/DTS-X use this format.
|
|
*/
|
|
if (is_type_in_outdevices(out, AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
|
|
pMixer = mixer_open_legacy(adev->dev_out[SND_OUT_SOUND_CARD_HDMI].card);
|
|
if (!pMixer) {
|
|
ALOGE("mMixer is a null point %s %d,CARD = %d",__func__, __LINE__,adev->dev_out[SND_OUT_SOUND_CARD_HDMI].card);
|
|
return ret;
|
|
}
|
|
pctl = mixer_get_control(pMixer,"AUDIO MODE",0 );
|
|
if (pctl != NULL) {
|
|
ALOGD("Now set mixer audio_mode is %d for drm",out->output_direct_mode);
|
|
switch (out->output_direct_mode) {
|
|
case HBR:
|
|
ret = mixer_ctl_set_val(pctl , out->output_direct_mode);
|
|
break;
|
|
case NLPCM:
|
|
ret = mixer_ctl_set_val(pctl , out->output_direct_mode);
|
|
break;
|
|
default:
|
|
ret = mixer_ctl_set_val(pctl , out->output_direct_mode);
|
|
break;
|
|
}
|
|
|
|
if (ret != 0) {
|
|
ALOGE("set_controls() can not set ctl!");
|
|
mixer_close_legacy(pMixer);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
mixer_close_legacy(pMixer);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void open_sound_card_policy(struct stream_out *out)
|
|
{
|
|
if (out == NULL) {
|
|
return ;
|
|
}
|
|
|
|
if (is_bitstream(out) || (is_multi_pcm(out))) {
|
|
return ;
|
|
}
|
|
|
|
for (int i = 0; i < out->num_configs; ++i) {
|
|
if (audio_is_bluetooth_out_sco_device(out->devices[i]))
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This is special process
|
|
* open all sound card to output one audio stream at the same time
|
|
*/
|
|
bool support = ((out->config.rate == 44100) || (out->config.rate == 48000));
|
|
struct audio_device *adev = out->dev;
|
|
if (support) {
|
|
out->num_configs = 0;
|
|
if(adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].card != SND_OUT_SOUND_CARD_UNKNOWN) {
|
|
out->devices[out->num_configs++] = AUDIO_DEVICE_OUT_SPEAKER;
|
|
}
|
|
|
|
if(adev->dev_out[SND_OUT_SOUND_CARD_HDMI].card != SND_OUT_SOUND_CARD_UNKNOWN) {
|
|
/*
|
|
* hdmi is taken by direct/mulit pcm output
|
|
*/
|
|
if(adev->outputs[OUTPUT_HDMI_MULTI] == NULL) {
|
|
out->devices[out->num_configs++] = AUDIO_DEVICE_OUT_AUX_DIGITAL;
|
|
}
|
|
}
|
|
|
|
if(adev->dev_out[SND_OUT_SOUND_CARD_SPDIF].card != SND_OUT_SOUND_CARD_UNKNOWN){
|
|
out->devices[out->num_configs++] = AUDIO_DEVICE_OUT_SPDIF;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int open_pcm(int card, int device, int index, struct stream_out *out)
|
|
{
|
|
struct audio_device *adev = out->dev;
|
|
struct pcm_config *pcm_config = &pcm_config_ap_sco;
|
|
pcm_config->rate = adev->bt_wb_speech_enabled ? 16000 : 8000;
|
|
|
|
if (card == (int)SND_OUT_SOUND_CARD_UNKNOWN) {
|
|
ALOGE("invalid card num.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!out->pcm[index])
|
|
out->pcm[index] = pcm_open(card, device, PCM_OUT | PCM_MONOTONIC,
|
|
(index == (int)SND_OUT_SOUND_CARD_BT) ? pcm_config : &out->config);
|
|
|
|
if (out->pcm[index] && !pcm_is_ready(out->pcm[index])) {
|
|
ALOGE("%s failed: %s,card number = %d", __func__,
|
|
pcm_get_error(out->pcm[index]), card);
|
|
pcm_close(out->pcm[index]);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief start_output_stream
|
|
* must be called with hw device outputs list, output stream, and hw device mutexes locked
|
|
*
|
|
* @param out
|
|
*
|
|
* @returns
|
|
*/
|
|
static int start_output_stream(struct stream_out *out)
|
|
{
|
|
struct audio_device *adev = out->dev;
|
|
int ret = 0;
|
|
int card = (int)SND_OUT_SOUND_CARD_UNKNOWN;
|
|
int device = 0;
|
|
// set defualt value to true for compatible with mid project
|
|
|
|
|
|
//ALOGD("%s:%d out = %p,device = 0x%x,outputs[OUTPUT_HDMI_MULTI] = %p",__FUNCTION__,__LINE__,out,out->device,adev->outputs[OUTPUT_HDMI_MULTI]);
|
|
if (out == adev->outputs[OUTPUT_HDMI_MULTI]) {
|
|
force_non_hdmi_out_standby(adev);
|
|
} else if (adev->outputs[OUTPUT_HDMI_MULTI] &&
|
|
!adev->outputs[OUTPUT_HDMI_MULTI]->standby) {
|
|
out->disabled = true;
|
|
return 0;
|
|
}
|
|
|
|
out->disabled = false;
|
|
read_out_sound_card(out);
|
|
|
|
#ifndef SUPPORT_MULTIAUDIO
|
|
#ifdef BOX_HAL
|
|
if (!(out->device & (AUDIO_DEVICE_OUT_WIRED_HEADSET |
|
|
AUDIO_DEVICE_OUT_WIRED_HEADPHONE))) {
|
|
// ALOGI("rpdzkj__, %s, enter hdmi audio policy", __func__);
|
|
open_sound_card_policy(out);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
for (int i = 0; i < out->num_configs; ++i) {
|
|
ALOGD("%s: i = %d, device = 0x%x", __FUNCTION__, i, out->devices[i]);
|
|
//if (out->devices[i] == AUDIO_DEVICE_OUT_AUX_DIGITAL) {
|
|
if (1) {
|
|
audio_devices_t route_device = out->devices[i];
|
|
route_pcm_card_open(adev->dev_out[SND_OUT_SOUND_CARD_HDMI].card, getRouteFromDevice(route_device));
|
|
if (adev->owner[SOUND_CARD_HDMI] == NULL) {
|
|
card = adev->dev_out[SND_OUT_SOUND_CARD_HDMI].card;
|
|
device =adev->dev_out[SND_OUT_SOUND_CARD_HDMI].device;
|
|
#ifndef IEC958_FORAMT
|
|
#ifdef USE_DRM
|
|
// set audio mode
|
|
ret = mixer_mode_set(out);
|
|
if (ret != 0) {
|
|
ALOGE("mixer mode set error,ret=%d!",ret);
|
|
}
|
|
#endif
|
|
#endif
|
|
mixer_hdmi_set_force_bypass(out, card);
|
|
ret = open_pcm(card, device, (int)SND_OUT_SOUND_CARD_HDMI, out);
|
|
if (ret < 0) return ret;
|
|
|
|
if (is_bitstream(out) && ((out->config.format == PCM_FORMAT_S24_LE)
|
|
|| (out->config.format == PCM_FORMAT_IEC958_SUBFRAME_LE))) {
|
|
out->bistream = bitstream_init(out->config.format,
|
|
out->config.rate, out->config.channels);
|
|
}
|
|
|
|
if (is_multi_pcm(out) || is_bitstream(out)) {
|
|
adev->owner[SOUND_CARD_HDMI] = (int*)out;
|
|
}
|
|
}
|
|
} else {
|
|
ALOGD("The current HDMI is DVI mode");
|
|
out->device |= AUDIO_DEVICE_OUT_SPEAKER;
|
|
}
|
|
// if (out->device & (AUDIO_DEVICE_OUT_SPEAKER |
|
|
// AUDIO_DEVICE_OUT_WIRED_HEADSET |
|
|
// AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
|
|
// AUDIO_DEVICE_OUT_ALL_SCO)) {
|
|
if (1) { //rpdzkj
|
|
audio_devices_t route_device = out->device & (AUDIO_DEVICE_OUT_SPEAKER |
|
|
AUDIO_DEVICE_OUT_WIRED_HEADSET |
|
|
AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
|
|
AUDIO_DEVICE_OUT_ALL_SCO);
|
|
route_pcm_card_open(adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].card, getRouteFromDevice(route_device));
|
|
card = adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].card;
|
|
device = adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].device;
|
|
if(card != (int)SND_OUT_SOUND_CARD_UNKNOWN) {
|
|
out->pcm[SND_OUT_SOUND_CARD_SPEAKER] = pcm_open(card, device,
|
|
PCM_OUT | PCM_MONOTONIC, &out->config);
|
|
if (out->pcm[SND_OUT_SOUND_CARD_SPEAKER] && !pcm_is_ready(out->pcm[SND_OUT_SOUND_CARD_SPEAKER])) {
|
|
ALOGE("pcm_open(PCM_CARD) failed: %s,card number = %d",
|
|
pcm_get_error(out->pcm[SND_OUT_SOUND_CARD_SPEAKER]),card);
|
|
pcm_close(out->pcm[SND_OUT_SOUND_CARD_SPEAKER]);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
if (out->devices[i] == AUDIO_DEVICE_OUT_SPEAKER ||
|
|
out->devices[i] == AUDIO_DEVICE_OUT_WIRED_HEADSET ||
|
|
out->devices[i] == AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
|
|
out->devices[i] == AUDIO_DEVICE_OUT_BUS) {
|
|
audio_devices_t route_device = out->devices[i];
|
|
route_pcm_card_open(adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].card, getRouteFromDevice(route_device));
|
|
card = adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].card;
|
|
device = adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].device;
|
|
ret = open_pcm(card, device, (int)SND_OUT_SOUND_CARD_SPEAKER, out);
|
|
if (ret < 0) return ret;
|
|
}
|
|
|
|
if (out->devices[i] == AUDIO_DEVICE_OUT_SPDIF) {
|
|
if (adev->owner[SOUND_CARD_SPDIF] == NULL) {
|
|
card = adev->dev_out[SND_OUT_SOUND_CARD_SPDIF].card;
|
|
device = adev->dev_out[SND_OUT_SOUND_CARD_SPDIF].device;
|
|
ret = open_pcm(card, device, (int)SND_OUT_SOUND_CARD_SPDIF, out);
|
|
if (ret < 0) return ret;
|
|
|
|
if (is_multi_pcm(out) || is_bitstream(out)) {
|
|
adev->owner[SOUND_CARD_SPDIF] = (int*)out;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef SUPPORT_VX_ROCKCHIP
|
|
if (out->devices[i] == VX_ROCKCHIP_OUT_SPDIF0) {
|
|
card = adev->dev_out[SND_OUT_SOUND_CARD_SPDIF_1].card;
|
|
device = adev->dev_out[SND_OUT_SOUND_CARD_SPDIF_1].device;
|
|
ret = open_pcm(card, device, (int)SND_OUT_SOUND_CARD_SPDIF_1, out);
|
|
if (ret < 0) return ret;
|
|
}
|
|
|
|
if (out->devices[i] == VX_ROCKCHIP_OUT_HDMI0) {
|
|
card = adev->dev_out[SND_OUT_SOUND_CARD_HDMI_1].card;
|
|
device = adev->dev_out[SND_OUT_SOUND_CARD_HDMI_1].device;
|
|
ret = open_pcm(card, device, (int)SND_OUT_SOUND_CARD_HDMI_1, out);
|
|
if (ret < 0) return ret;
|
|
}
|
|
#endif
|
|
|
|
if (audio_is_bluetooth_out_sco_device(out->devices[i])) {
|
|
card = adev->dev_out[SND_OUT_SOUND_CARD_BT].card;
|
|
device = adev->dev_out[SND_OUT_SOUND_CARD_BT].device;
|
|
struct pcm_config *pcm_config = &pcm_config_ap_sco;
|
|
pcm_config->rate = adev->bt_wb_speech_enabled?16000:8000;
|
|
|
|
ALOGD("%s pcm_open bt card number = %d, device=%d, src rate: %d dest rate:%d, wbs:%d",
|
|
__func__, card, device, out->config.rate, pcm_config->rate, adev->bt_wb_speech_enabled);
|
|
ret = open_pcm(card, device, (int)SND_OUT_SOUND_CARD_BT, out);
|
|
if (ret < 0) return ret;
|
|
|
|
ret = create_resampler(out->config.rate,
|
|
pcm_config->rate,
|
|
2,
|
|
RESAMPLER_QUALITY_DEFAULT,
|
|
NULL,
|
|
&out->resampler);
|
|
|
|
if (ret != 0) ret = -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
adev->out_device |= out->device;
|
|
out_dump(out, 0);
|
|
ALOGD("%s:%d, out = %p",__FUNCTION__,__LINE__,out);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief get_next_buffer
|
|
*
|
|
* @param buffer_provider
|
|
* @param buffer
|
|
*
|
|
* @returns
|
|
*/
|
|
static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
|
|
struct resampler_buffer* buffer)
|
|
{
|
|
struct stream_in *in;
|
|
size_t i,size;
|
|
|
|
if (buffer_provider == NULL || buffer == NULL)
|
|
return -EINVAL;
|
|
|
|
in = (struct stream_in *)((char *)buffer_provider -
|
|
offsetof(struct stream_in, buf_provider));
|
|
|
|
if (in->pcm == NULL) {
|
|
buffer->raw = NULL;
|
|
buffer->frame_count = 0;
|
|
in->read_status = -ENODEV;
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (in->frames_in == 0) {
|
|
size = pcm_frames_to_bytes(in->pcm, in->config->period_size);
|
|
in->read_status = pcm_read(in->pcm,
|
|
(void*)in->buffer, size);
|
|
if (in->read_status != 0) {
|
|
ALOGE("get_next_buffer() pcm_read error %d", in->read_status);
|
|
buffer->raw = NULL;
|
|
buffer->frame_count = 0;
|
|
return in->read_status;
|
|
}
|
|
|
|
if (in->config->channels == 2 && (!(in->device == AUDIO_DEVICE_IN_HDMI))) {
|
|
if (in->channel_flag & CH_CHECK) {
|
|
if (in->start_checkcount < SAMPLECOUNT) {
|
|
in->start_checkcount += size;
|
|
} else {
|
|
in->channel_flag = channel_check((void*)in->buffer, size / 2);
|
|
in->channel_flag &= ~CH_CHECK;
|
|
}
|
|
}
|
|
channel_fixed((void*)in->buffer, size / 2, in->channel_flag & ~CH_CHECK);
|
|
}
|
|
|
|
#ifdef RK_DENOISE_ENABLE
|
|
if ((in->mDenioseState != NULL) && !(in->device == AUDIO_DEVICE_IN_HDMI) && !(in->device == AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) {
|
|
rkdenoise_process(in->mDenioseState, (void*)in->buffer, size, (void*)in->buffer);
|
|
}
|
|
#endif
|
|
//fwrite(in->buffer,pcm_frames_to_bytes(in->pcm,pcm_get_buffer_size(in->pcm)),1,in_debug);
|
|
in->frames_in = in->config->period_size;
|
|
|
|
/* Do stereo to mono conversion in place by discarding right channel */
|
|
if ((in->channel_mask == AUDIO_CHANNEL_IN_MONO)
|
|
&&(in->config->channels == 2)) {
|
|
//ALOGE("channel_mask = AUDIO_CHANNEL_IN_MONO");
|
|
for (i = 0; i < in->frames_in; i++)
|
|
in->buffer[i] = in->buffer[i * 2];
|
|
}
|
|
}
|
|
|
|
//ALOGV("pcm_frames_to_bytes(in->pcm,pcm_get_buffer_size(in->pcm)):%d",size);
|
|
buffer->frame_count = (buffer->frame_count > in->frames_in) ?
|
|
in->frames_in : buffer->frame_count;
|
|
buffer->i16 = in->buffer +
|
|
(in->config->period_size - in->frames_in) *
|
|
audio_channel_count_from_in_mask(in->channel_mask);
|
|
|
|
return in->read_status;
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief release_buffer
|
|
*
|
|
* @param buffer_provider
|
|
* @param buffer
|
|
*/
|
|
static void release_buffer(struct resampler_buffer_provider *buffer_provider,
|
|
struct resampler_buffer* buffer)
|
|
{
|
|
struct stream_in *in;
|
|
|
|
if (buffer_provider == NULL || buffer == NULL)
|
|
return;
|
|
|
|
in = (struct stream_in *)((char *)buffer_provider -
|
|
offsetof(struct stream_in, buf_provider));
|
|
|
|
in->frames_in -= buffer->frame_count;
|
|
}
|
|
|
|
static bool get_hdmiin_audio_info(struct audio_device *adev, char *prop, int *value)
|
|
{
|
|
char strfile[128];
|
|
FILE* file = NULL;
|
|
char info[20] = {0};
|
|
|
|
if (!value)
|
|
return false;
|
|
sprintf(strfile, "/sys/class/hdmirx/%s/%s", "hdmirx", prop);
|
|
if (access(strfile, 0)) {
|
|
ALOGD("No exist %s", strfile);
|
|
return false;
|
|
}
|
|
file = fopen(strfile, "r");
|
|
if (!file) {
|
|
ALOGD("Could reading %s property", strfile);
|
|
return false;
|
|
}
|
|
fread(info, sizeof(char), sizeof(info)/sizeof(char) - 1, file);
|
|
fclose(file);
|
|
*value = atoi(info);
|
|
return true;
|
|
}
|
|
|
|
#define STR_32KHZ "32KHZ"
|
|
#define STR_44_1KHZ "44.1KHZ"
|
|
#define STR_48KHZ "48KHZ"
|
|
/**
|
|
* @brief get_hdmiin_audio_rate
|
|
* @param
|
|
* @return hdmiin audio rate
|
|
*/
|
|
static int get_hdmiin_audio_rate(struct audio_device *adev)
|
|
{
|
|
int rate;
|
|
char value[PROPERTY_VALUE_MAX] = "";
|
|
|
|
if (get_hdmiin_audio_info(adev, "audio_rate", &rate)) {
|
|
return rate;
|
|
}
|
|
property_get("vendor.hdmiin.audiorate", value, STR_44_1KHZ);
|
|
if ( 0 == strncmp(value, STR_32KHZ, strlen(STR_32KHZ)) ){
|
|
rate = 32000;
|
|
} else if ( 0 == strncmp(value, STR_44_1KHZ, strlen(STR_44_1KHZ)) ){
|
|
rate = 44100;
|
|
} else if ( 0 == strncmp(value, STR_48KHZ, strlen(STR_48KHZ)) ){
|
|
rate = 48000;
|
|
} else {
|
|
rate = atoi(value);
|
|
if (rate <= 0)
|
|
rate = 44100;
|
|
}
|
|
|
|
// if hdmiin connect to codec, use 44100 sample rate
|
|
if (adev->dev_out[SND_IN_SOUND_CARD_HDMI].card
|
|
== adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].card)
|
|
rate = 44100;
|
|
|
|
return rate;
|
|
}
|
|
|
|
int create_resampler_helper(struct stream_in *in, uint32_t in_rate)
|
|
{
|
|
int ret = 0;
|
|
if (in->resampler) {
|
|
release_resampler(in->resampler);
|
|
in->resampler = NULL;
|
|
}
|
|
|
|
in->buf_provider.get_next_buffer = get_next_buffer;
|
|
in->buf_provider.release_buffer = release_buffer;
|
|
ALOGD("create resampler, channel %d, rate %d => %d",
|
|
audio_channel_count_from_in_mask(in->channel_mask),
|
|
in_rate, in->requested_rate);
|
|
ret = create_resampler(in_rate,
|
|
in->requested_rate,
|
|
audio_channel_count_from_in_mask(in->channel_mask),
|
|
RESAMPLER_QUALITY_DEFAULT,
|
|
&in->buf_provider,
|
|
&in->resampler);
|
|
if (ret != 0) {
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief start_input_stream
|
|
* must be called with input stream and hw device mutexes locked
|
|
*
|
|
* @param in
|
|
*
|
|
* @returns
|
|
*/
|
|
static int start_input_stream(struct stream_in *in)
|
|
{
|
|
struct audio_device *adev = in->dev;
|
|
int ret = 0;
|
|
int card = 0;
|
|
int device = 0;
|
|
int hdmiin_present = 0;
|
|
|
|
channel_check_start(in);
|
|
read_in_sound_card(in);
|
|
|
|
if (in->device == AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
|
|
in->config = &pcm_config_in_bt;
|
|
in->config->rate = adev->bt_wb_speech_enabled?16000:8000;
|
|
card = adev->dev_in[SND_IN_SOUND_CARD_BT].card;
|
|
device = adev->dev_in[SND_IN_SOUND_CARD_BT].device;
|
|
|
|
if (card != SND_IN_SOUND_CARD_UNKNOWN) {
|
|
in->pcm = pcm_open(card, device, PCM_IN, in->config);
|
|
} else {
|
|
ALOGE("%s: %d,the card number of bt is = %d",__FUNCTION__,__LINE__,card);
|
|
return -EINVAL;
|
|
}
|
|
} else if (in->device == AUDIO_DEVICE_IN_HDMI) {
|
|
if (get_hdmiin_audio_info(adev, "audio_present", &hdmiin_present)) {
|
|
if (!hdmiin_present) {
|
|
ALOGD("hdmiin audio is no present, don't open hdmiin sound");
|
|
return -EEXIST;
|
|
}
|
|
}
|
|
card = (int)adev->dev_in[SND_IN_SOUND_CARD_HDMI].card;
|
|
device = adev->dev_in[SND_IN_SOUND_CARD_HDMI].device;
|
|
|
|
if (card != SND_IN_SOUND_CARD_UNKNOWN) {
|
|
in->config->rate = get_hdmiin_audio_rate(adev);
|
|
in->pcm = pcm_open(card, device, PCM_IN, in->config);
|
|
ALOGD("open HDMIIN %d", card);
|
|
}
|
|
} else {
|
|
ALOGD("open build mic");
|
|
in->config = &pcm_config_in;
|
|
card = adev->dev_in[SND_IN_SOUND_CARD_MIC].card;
|
|
device = adev->dev_in[SND_IN_SOUND_CARD_MIC].device;
|
|
|
|
if (card != SND_IN_SOUND_CARD_UNKNOWN) {
|
|
route_pcm_card_open(card, getRouteFromDevice(in->device | AUDIO_DEVICE_BIT_IN));
|
|
in->pcm = pcm_open(card, device, PCM_IN, in->config);
|
|
}
|
|
|
|
#ifdef RK_DENOISE_ENABLE
|
|
{
|
|
int ch = in->config->channels;
|
|
int period = in->config->period_size;
|
|
int rate = in->config->rate;
|
|
int type = 0;
|
|
{
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("vendor.audio.anr.speex", value, "0");
|
|
type = atoi(value);
|
|
}
|
|
if (in->mDenioseState)
|
|
rkdenoise_destroy(in->mDenioseState);
|
|
in->mDenioseState = rkdenoise_create(rate, ch, period, type ? ALG_SPX : ALG_SKV);
|
|
if (in->mDenioseState == NULL) {
|
|
ALOGW("crate rkdenoise failed!!!");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
if (in->pcm == NULL || !pcm_is_ready(in->pcm)) {
|
|
if (in->pcm != NULL) {
|
|
ALOGE("pcm_open() failed: %s", pcm_get_error(in->pcm));
|
|
pcm_close(in->pcm);
|
|
}
|
|
|
|
ALOGD("%s open card = %d, device = %d fail", __func__, card, device);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (in->resampler) {
|
|
release_resampler(in->resampler);
|
|
in->resampler = NULL;
|
|
}
|
|
|
|
if (in->config->rate != in->requested_rate) {
|
|
ALOGD("create resampler in->config->rate=%d in->requested_rate=%d",in->config->rate,in->requested_rate);
|
|
ret = create_resampler_helper(in, in->config->rate);
|
|
if (ret < 0 || in->resampler == NULL) {
|
|
pcm_close(in->pcm);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* if no supported sample rate is available, use the resampler */
|
|
if (in->resampler)
|
|
in->resampler->reset(in->resampler);
|
|
|
|
in->frames_in = 0;
|
|
adev->input_source = in->input_source;
|
|
adev->in_device = in->device;
|
|
adev->in_channel_mask = in->channel_mask;
|
|
|
|
|
|
/* initialize volume ramp */
|
|
in->ramp_frames = (CAPTURE_START_RAMP_MS * in->requested_rate) / 1000;
|
|
in->ramp_step = (uint16_t)(USHRT_MAX / in->ramp_frames);
|
|
in->ramp_vol = 0;;
|
|
|
|
in_dump(in, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief get_input_buffer_size
|
|
*
|
|
* @param sample_rate
|
|
* @param format
|
|
* @param channel_count
|
|
* @param is_low_latency
|
|
*
|
|
* @returns
|
|
*/
|
|
static size_t get_input_buffer_size(unsigned int sample_rate,
|
|
audio_format_t format,
|
|
unsigned int channel_count,
|
|
bool is_low_latency)
|
|
{
|
|
const struct pcm_config *config = is_low_latency ?
|
|
&pcm_config_in_low_latency : &pcm_config_in;
|
|
size_t size;
|
|
|
|
/*
|
|
* take resampling into account and return the closest majoring
|
|
* multiple of 16 frames, as audioflinger expects audio buffers to
|
|
* be a multiple of 16 frames
|
|
*/
|
|
size = (config->period_size * sample_rate) / config->rate;
|
|
size = ((size + 15) / 16) * 16;
|
|
|
|
return size * channel_count * audio_bytes_per_sample(format);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief read_frames
|
|
* read_frames() reads frames from kernel driver, down samples to capture rate
|
|
* if necessary and output the number of frames requested to the buffer specified
|
|
*
|
|
* @param in
|
|
* @param buffer
|
|
* @param frames
|
|
*
|
|
* @returns
|
|
*/
|
|
static ssize_t read_frames(struct stream_in *in, void *buffer, ssize_t frames)
|
|
{
|
|
ssize_t frames_wr = 0;
|
|
size_t frame_size = audio_stream_in_frame_size(&in->stream);
|
|
|
|
while (frames_wr < frames) {
|
|
size_t frames_rd = frames - frames_wr;
|
|
if (in->resampler != NULL) {
|
|
in->resampler->resample_from_provider(in->resampler,
|
|
(int16_t *)((char *)buffer +
|
|
frames_wr * frame_size),
|
|
&frames_rd);
|
|
} else {
|
|
struct resampler_buffer buf = {
|
|
{ raw : NULL, },
|
|
frame_count :
|
|
frames_rd,
|
|
};
|
|
if (get_next_buffer(&in->buf_provider, &buf))
|
|
break;
|
|
if (buf.raw != NULL) {
|
|
memcpy((char *)buffer +
|
|
frames_wr * frame_size,
|
|
buf.raw,
|
|
buf.frame_count * frame_size);
|
|
frames_rd = buf.frame_count;
|
|
}
|
|
release_buffer(&in->buf_provider, &buf);
|
|
}
|
|
/* in->read_status is updated by getNextBuffer() also called by
|
|
* in->resampler->resample_from_provider() */
|
|
if (in->read_status != 0)
|
|
return in->read_status;
|
|
|
|
frames_wr += frames_rd;
|
|
}
|
|
return frames_wr;
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_sample_rate
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static uint32_t out_get_sample_rate(const struct audio_stream *stream)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("vendor.vts_test", value, NULL);
|
|
if (strcmp(value, "true") == 0) {
|
|
if (out->use_default_config) {
|
|
return 48000;
|
|
} else {
|
|
return out->aud_config.sample_rate;
|
|
}
|
|
} else {
|
|
return out->config.rate;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief out_set_sample_rate
|
|
*
|
|
* @param stream
|
|
* @param rate
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_buffer_size
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static size_t out_get_buffer_size(const struct audio_stream *stream)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
|
|
return out->config.period_size *
|
|
audio_stream_out_frame_size((const struct audio_stream_out *)stream);
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_channels
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("vendor.vts_test", value, NULL);
|
|
if (out->use_default_config) {
|
|
return AUDIO_CHANNEL_OUT_MONO;
|
|
} else {
|
|
return out->aud_config.channel_mask;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_format
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static audio_format_t out_get_format(const struct audio_stream *stream)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("vendor.vts_test", value, NULL);
|
|
if (out->use_default_config) {
|
|
return AUDIO_FORMAT_PCM_16_BIT;
|
|
} else {
|
|
return out->aud_config.format;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief out_set_format
|
|
*
|
|
* @param stream
|
|
* @param format
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_set_format(struct audio_stream *stream, audio_format_t format)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/**
|
|
* @brief output_devices
|
|
* Return the set of output devices associated with active streams
|
|
* other than out. Assumes out is non-NULL and out->dev is locked.
|
|
*
|
|
* @param out
|
|
*
|
|
* @returns
|
|
*/
|
|
static audio_devices_t output_devices(struct stream_out *out)
|
|
{
|
|
struct audio_device *dev = out->dev;
|
|
enum output_type type;
|
|
audio_devices_t devices = AUDIO_DEVICE_NONE;
|
|
|
|
for (type = 0; type < OUTPUT_TOTAL; ++type) {
|
|
struct stream_out *other = dev->outputs[type];
|
|
if (other && (other != out) && !other->standby) {
|
|
// TODO no longer accurate
|
|
/* safe to access other stream without a mutex,
|
|
* because we hold the dev lock,
|
|
* which prevents the other stream from being closed
|
|
*/
|
|
devices |= other->device;
|
|
}
|
|
}
|
|
|
|
return devices;
|
|
}
|
|
|
|
/**
|
|
* @brief do_out_standby
|
|
* must be called with hw device outputs list, all out streams, and hw device mutex locked
|
|
*
|
|
* @param out
|
|
*/
|
|
static void do_out_standby(struct stream_out *out)
|
|
{
|
|
struct audio_device *adev = out->dev;
|
|
int i;
|
|
ALOGD("%s,out = %p,device = 0x%x, standby = %d",__FUNCTION__,out,out->device,out->standby);
|
|
if (!out->standby) {
|
|
for (i = 0; i < SND_OUT_SOUND_CARD_MAX; i++) {
|
|
if (out->pcm[i]) {
|
|
pcm_close(out->pcm[i]);
|
|
out->pcm[i] = NULL;
|
|
}
|
|
}
|
|
out->standby = true;
|
|
out->nframes = 0;
|
|
if (out == adev->outputs[OUTPUT_HDMI_MULTI]) {
|
|
/* force standby on low latency output stream so that it can reuse HDMI driver if
|
|
* necessary when restarted */
|
|
force_non_hdmi_out_standby(adev);
|
|
}
|
|
#ifdef USE_DRM
|
|
mixer_mode_set(out);
|
|
#endif
|
|
/* re-calculate the set of active devices from other streams */
|
|
adev->out_device = output_devices(out);
|
|
|
|
#ifdef AUDIO_3A
|
|
if (adev->voice_api != NULL) {
|
|
adev->voice_api->flush();
|
|
}
|
|
#endif
|
|
route_pcm_close(PLAYBACK_OFF_ROUTE);
|
|
ALOGD("close device");
|
|
|
|
/* Skip resetting the mixer if no output device is active */
|
|
if (adev->out_device) {
|
|
route_pcm_open(getRouteFromDevice(adev->out_device));
|
|
ALOGD("change device");
|
|
}
|
|
if(adev->owner[SOUND_CARD_HDMI] == (int*)out){
|
|
adev->owner[SOUND_CARD_HDMI] = NULL;
|
|
}
|
|
|
|
if(adev->owner[SOUND_CARD_SPDIF] == (int*)out){
|
|
adev->owner[SOUND_CARD_SPDIF] = NULL;
|
|
}
|
|
|
|
bitstream_destory(&out->bistream);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief lock_all_outputs
|
|
* lock outputs list, all output streams, and device
|
|
*
|
|
* @param adev
|
|
*/
|
|
static void lock_all_outputs(struct audio_device *adev)
|
|
{
|
|
enum output_type type;
|
|
pthread_mutex_lock(&adev->lock_outputs);
|
|
for (type = 0; type < OUTPUT_TOTAL; ++type) {
|
|
struct stream_out *out = adev->outputs[type];
|
|
if (out)
|
|
pthread_mutex_lock(&out->lock);
|
|
}
|
|
pthread_mutex_lock(&adev->lock);
|
|
}
|
|
|
|
/**
|
|
* @brief unlock_all_outputs
|
|
* unlock device, all output streams (except specified stream), and outputs list
|
|
*
|
|
* @param adev
|
|
* @param except
|
|
*/
|
|
static void unlock_all_outputs(struct audio_device *adev, struct stream_out *except)
|
|
{
|
|
/* unlock order is irrelevant, but for cleanliness we unlock in reverse order */
|
|
pthread_mutex_unlock(&adev->lock);
|
|
enum output_type type = OUTPUT_TOTAL;
|
|
do {
|
|
struct stream_out *out = adev->outputs[--type];
|
|
if (out && out != except)
|
|
pthread_mutex_unlock(&out->lock);
|
|
} while (type != (enum output_type) 0);
|
|
pthread_mutex_unlock(&adev->lock_outputs);
|
|
}
|
|
|
|
/**
|
|
* @brief out_standby
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_standby(struct audio_stream *stream)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
struct audio_device *adev = out->dev;
|
|
|
|
lock_all_outputs(adev);
|
|
|
|
do_out_standby(out);
|
|
|
|
unlock_all_outputs(adev, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief out_dump
|
|
*
|
|
* @param stream
|
|
* @param fd
|
|
*
|
|
* @returns
|
|
*/
|
|
int out_dump(const struct audio_stream *stream, int fd)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
for (int i = 0; i < out->num_configs; ++i) {
|
|
ALOGD("out->Device[%d] : 0x%x", i, out->devices[i]);
|
|
if (out->devices[i] == AUDIO_DEVICE_OUT_ALL_SCO) {
|
|
ALOGD("out->SampleRate : %d", pcm_config_ap_sco.rate);
|
|
ALOGD("out->Channels : %d", pcm_config_ap_sco.channels);
|
|
ALOGD("out->Format : %d", pcm_config_ap_sco.format);
|
|
ALOGD("out->PreiodSize : %d", pcm_config_ap_sco.period_size);
|
|
} else {
|
|
ALOGD("out->SampleRate : %d", out->config.rate);
|
|
ALOGD("out->Channels : %d", out->config.channels);
|
|
ALOGD("out->Format : %d", out->config.format);
|
|
ALOGD("out->PreiodSize : %d", out->config.period_size);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
/**
|
|
* @brief out_set_parameters
|
|
*
|
|
* @param stream
|
|
* @param kvpairs
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
|
|
{
|
|
// The set parameters here only matters when the routing devices are changed.
|
|
// When the device version is not less than 3.0, the framework will use create
|
|
// audio patch API instead of set parameters to chanage audio routing.
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
struct audio_device *adev = out->dev;
|
|
struct str_parms *parms;
|
|
char value[32];
|
|
int ret;
|
|
int status = 0;
|
|
unsigned int val;
|
|
|
|
ALOGD("%s: kvpairs = %s", __func__, kvpairs);
|
|
|
|
parms = str_parms_create_str(kvpairs);
|
|
//set channel_mask
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_CHANNELS,
|
|
value, sizeof(value));
|
|
if (ret >= 0) {
|
|
val = atoi(value);
|
|
out->aud_config.channel_mask = val;
|
|
}
|
|
// set sample rate
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
|
|
value, sizeof(value));
|
|
if (ret >= 0) {
|
|
val = atoi(value);
|
|
out->aud_config.sample_rate = val;
|
|
}
|
|
// set format
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_FORMAT,
|
|
value, sizeof(value));
|
|
if (ret >= 0) {
|
|
val = atoi(value);
|
|
out->aud_config.format = val;
|
|
}
|
|
|
|
str_parms_destroy(parms);
|
|
ALOGV("%s: exit: status(%d)", __func__, status);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* function: get support formats
|
|
* Query supported formats. The response is a '|' separated list of strings from audio_format_t enum
|
|
* e.g: "sup_formats=AUDIO_FORMAT_PCM_16_BIT"
|
|
*/
|
|
|
|
static int stream_get_parameter_formats(const struct audio_stream *stream,
|
|
struct str_parms *query,
|
|
struct str_parms *reply)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
int avail = 1024;
|
|
char value[avail];
|
|
if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
|
|
memset(value,0,avail);
|
|
// set support pcm 16 bit default
|
|
strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
|
|
str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* function: get support channels
|
|
* Query supported channel masks. The response is a '|' separated list of strings from
|
|
* audio_channel_mask_t enum
|
|
* e.g: "sup_channels=AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_MONO"
|
|
*/
|
|
|
|
static int stream_get_parameter_channels(struct str_parms *query,
|
|
struct str_parms *reply,
|
|
audio_channel_mask_t *supported_channel_masks)
|
|
{
|
|
char value[1024];
|
|
size_t i, j;
|
|
bool first = true;
|
|
|
|
if(str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS)){
|
|
value[0] = '\0';
|
|
i = 0;
|
|
/* the last entry in supported_channel_masks[] is always 0 */
|
|
while (supported_channel_masks[i] != 0) {
|
|
for (j = 0; j < ARRAY_SIZE(channels_name_to_enum_table); j++) {
|
|
if (channels_name_to_enum_table[j].value == supported_channel_masks[i]) {
|
|
if (!first) {
|
|
strcat(value, "|");
|
|
}
|
|
|
|
strcat(value, channels_name_to_enum_table[j].name);
|
|
first = false;
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* function: get support sample_rates
|
|
* Query supported sampling rates. The response is a '|' separated list of integer values
|
|
* e.g: ""sup_sampling_rates=44100|48000"
|
|
*/
|
|
|
|
static int stream_get_parameter_rates(struct str_parms *query,
|
|
struct str_parms *reply,
|
|
uint32_t *supported_sample_rates)
|
|
{
|
|
char value[256];
|
|
int ret = -1;
|
|
|
|
if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)) {
|
|
value[0] = '\0';
|
|
int cursor = 0;
|
|
int i = 0;
|
|
while(supported_sample_rates[i]){
|
|
int avail = sizeof(value) - cursor;
|
|
ret = snprintf(value + cursor, avail, "%s%d",
|
|
cursor > 0 ? "|" : "",
|
|
supported_sample_rates[i]);
|
|
|
|
if (ret < 0 || ret > avail){
|
|
value[cursor] = '\0';
|
|
break;
|
|
}
|
|
|
|
cursor += ret;
|
|
++i;
|
|
}
|
|
str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_parameters
|
|
*
|
|
* @param stream
|
|
* @param keys
|
|
*
|
|
* @returns
|
|
*/
|
|
static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
|
|
{
|
|
ALOGD("%s: keys = %s", __func__, keys);
|
|
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
struct str_parms *query = str_parms_create_str(keys);
|
|
char *str = NULL;
|
|
struct str_parms *reply = str_parms_create();
|
|
out->use_default_config = true;
|
|
|
|
if (stream_get_parameter_formats(stream,query,reply) == 0) {
|
|
str = str_parms_to_str(reply);
|
|
} else if (stream_get_parameter_channels(query, reply, &out->supported_channel_masks[0]) == 0) {
|
|
str = str_parms_to_str(reply);
|
|
} else if (stream_get_parameter_rates(query, reply, &out->supported_sample_rates[0]) == 0) {
|
|
str = str_parms_to_str(reply);
|
|
} else {
|
|
ALOGD("%s,str_parms_get_str failed !",__func__);
|
|
str = strdup("");
|
|
}
|
|
str_parms_destroy(query);
|
|
str_parms_destroy(reply);
|
|
|
|
ALOGV("%s,exit -- str = %s",__func__,str);
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_latency
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static uint32_t out_get_latency(const struct audio_stream_out *stream)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
|
|
return (out->config.period_size * out->config.period_count * 1000) /
|
|
out->config.rate;
|
|
}
|
|
|
|
/**
|
|
* @brief out_set_volume
|
|
*
|
|
* @param stream
|
|
* @param left
|
|
* @param right
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_set_volume(struct audio_stream_out *stream, float left,
|
|
float right)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
struct audio_device *adev = out->dev;
|
|
out->volume[0] = left;
|
|
out->volume[1] = right;
|
|
|
|
/* The mutex lock is not needed, because the client
|
|
* is not allowed to close the stream concurrently with this API
|
|
* pthread_mutex_lock(&adev->lock_outputs);
|
|
*/
|
|
bool is_HDMI = out == adev->outputs[OUTPUT_HDMI_MULTI];
|
|
/* pthread_mutex_unlock(&adev->lock_outputs); */
|
|
if (is_HDMI) {
|
|
/* only take left channel into account: the API is for stereo anyway */
|
|
out->muted = (left == 0.0f);
|
|
return 0;
|
|
}
|
|
return -ENOSYS;
|
|
}
|
|
/**
|
|
* @brief dump_out_data
|
|
*
|
|
* @param buffer bytes
|
|
*/
|
|
static void dump_out_data(const void* buffer,size_t bytes)
|
|
{
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("vendor.audio.record", value, "0");
|
|
int size = atoi(value);
|
|
if (size <= 0)
|
|
return ;
|
|
|
|
ALOGD("dump pcm file.");
|
|
static FILE* fd = NULL;
|
|
static int offset = 0;
|
|
if (fd == NULL) {
|
|
fd=fopen("/data/misc/audioserver/debug.pcm","wb+");
|
|
if(fd == NULL) {
|
|
ALOGD("DEBUG open /data/debug.pcm ,errno = %s",strerror(errno));
|
|
offset = 0;
|
|
}
|
|
}
|
|
|
|
if (fd != NULL) {
|
|
fwrite(buffer,bytes,1,fd);
|
|
offset += bytes;
|
|
fflush(fd);
|
|
if(offset >= size*1024*1024) {
|
|
fclose(fd);
|
|
fd = NULL;
|
|
offset = 0;
|
|
property_set("vendor.audio.record", "0");
|
|
ALOGD("TEST playback pcmfile end");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dump_in_data(const void* buffer, size_t bytes)
|
|
{
|
|
static int offset = 0;
|
|
static FILE* fd = NULL;
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("vendor.audio.record.in", value, "0");
|
|
int size = atoi(value);
|
|
if (size > 0) {
|
|
if(fd == NULL) {
|
|
fd=fopen("/data/misc/audioserver/debug_in.pcm","wb+");
|
|
if(fd == NULL) {
|
|
ALOGD("DEBUG open /data/misc/audioserver/debug_in.pcm ,errno = %s",strerror(errno));
|
|
} else {
|
|
ALOGD("dump pcm to file /data/misc/audioserver/debug_in.pcm");
|
|
}
|
|
offset = 0;
|
|
}
|
|
}
|
|
|
|
if (fd != NULL) {
|
|
ALOGD("dump in pcm %zu bytes", bytes);
|
|
fwrite(buffer,bytes,1,fd);
|
|
offset += bytes;
|
|
fflush(fd);
|
|
if (offset >= size*1024*1024) {
|
|
fclose(fd);
|
|
fd = NULL;
|
|
offset = 0;
|
|
property_set("vendor.audio.record.in", "0");
|
|
ALOGD("TEST record pcmfile end");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void check_hdmi_reconnect(struct stream_out *out)
|
|
{
|
|
if (out == NULL) {
|
|
return ;
|
|
}
|
|
|
|
struct audio_device *adev = out->dev;
|
|
lock_all_outputs(adev);
|
|
/*
|
|
* if snd_reopen is set to true, this means we need to reopen sound card.
|
|
* There are a situation, we need to do this:
|
|
* current stream is bistream over hdmi, and hdmi is unpluged and plug later,
|
|
* the driver of hdmi may init the hdmi in pcm mode automatically, according the
|
|
* implement of driver of hdmi. If we contiune send bitstream to hdmi open in pcm mode,
|
|
* hdmi may make noies or mute.
|
|
*/
|
|
if (out->snd_reopen && !out->standby)
|
|
{
|
|
/*
|
|
* standby sound cards
|
|
* the driver of hdmi will auto init with last configurations,
|
|
* so, we don't need close and reopen sound card of hdmi here.
|
|
* If driver of hdmi not config the hdmi with last output configurations,
|
|
* please open this codes to close and reopen sound card of hdmi.
|
|
*/
|
|
// do_out_standby(out);
|
|
// reset_bitstream_buf(out);
|
|
}
|
|
unlock_all_outputs(adev,NULL);
|
|
/*
|
|
* audio hal recived the msg of hdmi plugin, and other part of sdk will reviced it too.
|
|
* Other part(maybe hwc) will config hdmi after it reviced the msg.
|
|
* Audio must wait other part(maybe hwc) codes config hdmi finish, before send bitstream datas to hdmi
|
|
*/
|
|
if (out->snd_reopen && is_bitstream(out) && is_type_in_outdevices(out, AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
|
|
#ifdef USE_DRM
|
|
const char* PATH = "/sys/class/drm/card0-HDMI-A-1/enabled";
|
|
#else
|
|
const char* PATH = "/sys/class/display/HDMI/enabled";
|
|
#endif
|
|
if (access(PATH, R_OK) != 0) {
|
|
/*
|
|
* in most test, the time is 700~800ms between received msg of hdmi plug in
|
|
* and hdmi init finish, so we sleep 1 sec here if no way to get the status of hdmi.
|
|
*/
|
|
usleep(1000000);
|
|
} else {
|
|
/*
|
|
* read this node to judge the status of hdmi is config finish?
|
|
*/
|
|
char buffer[1024];
|
|
int counter = 200;
|
|
FILE* file = NULL;
|
|
while (counter >= 0 && ((file = fopen(PATH,"r")) != NULL)) {
|
|
int size = fread(buffer,1,sizeof(buffer),file);
|
|
if(size >= 0) {
|
|
if(strstr(buffer,"enabled")) {
|
|
fclose(file);
|
|
usleep(10000);
|
|
break;
|
|
}
|
|
}
|
|
usleep(10000);
|
|
counter --;
|
|
fclose(file);
|
|
}
|
|
}
|
|
ALOGD("%s: out = %p",__FUNCTION__,out);
|
|
out->snd_reopen = false;
|
|
}
|
|
}
|
|
|
|
static void out_mute_data(struct stream_out *out,void* buffer,size_t bytes)
|
|
{
|
|
struct audio_device *adev = out->dev;
|
|
bool mute = false;
|
|
|
|
#ifdef MUTE_WHEN_SCREEN_OFF
|
|
mute = adev->screenOff;
|
|
#endif
|
|
// for some special customer
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("vendor.audio.mute", value, "false");
|
|
if (!strcasecmp(value,"true")) {
|
|
mute = true;
|
|
}
|
|
|
|
if (out->muted || mute){
|
|
memset((void *)buffer, 0, bytes);
|
|
}
|
|
}
|
|
|
|
static int bitstream_write_data(struct stream_out *out, void* buffer, size_t bytes)
|
|
{
|
|
if ((out == NULL) || (buffer == NULL) || (bytes <= 0)) {
|
|
ALOGD("%s: %d, input parameter is invalid",__FUNCTION__,__LINE__);
|
|
return -1;
|
|
}
|
|
|
|
struct audio_device *adev = out->dev;
|
|
int ret = 0;
|
|
if (is_type_in_outdevices(out, AUDIO_DEVICE_OUT_AUX_DIGITAL) && (is_multi_pcm(out) || is_bitstream(out))) {
|
|
int card = adev->dev_out[SND_OUT_SOUND_CARD_HDMI].card;
|
|
if ((card != SND_OUT_SOUND_CARD_UNKNOWN) && (out->pcm[SND_OUT_SOUND_CARD_HDMI] != NULL)) {
|
|
if(out->config.format == PCM_FORMAT_S16_LE){
|
|
out_mute_data(out,buffer,bytes);
|
|
dump_out_data(buffer, bytes);
|
|
ret = pcm_write(out->pcm[SND_OUT_SOUND_CARD_HDMI], (void *)buffer, bytes);
|
|
} else if(out->config.format == PCM_FORMAT_S24_LE ||
|
|
out->config.format == PCM_FORMAT_IEC958_SUBFRAME_LE){
|
|
char *outBuffer = NULL;
|
|
int outSize = 0;
|
|
ret = bitstream_encode(out->bistream, (char*)buffer, (int)bytes, &outBuffer, &outSize);
|
|
if (ret == 0 && outSize > 0) {
|
|
out_mute_data(out,(void*)outBuffer, outSize);
|
|
dump_out_data((void*)outBuffer, outSize);
|
|
ret = pcm_write(out->pcm[SND_OUT_SOUND_CARD_HDMI], (void *)outBuffer, outSize);
|
|
if (ret != 0) {
|
|
ALOGD("%s:%d pcm_write error, out = %p, errno = %d, %s",
|
|
__FUNCTION__, __LINE__, out, errno,
|
|
pcm_get_error(out->pcm[SND_OUT_SOUND_CARD_HDMI]));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ALOGD("%s: %d: HDMI sound card not open",__FUNCTION__,__LINE__);
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* process volume of one multi pcm frame
|
|
* The multi pcm output no using mixer,so the can't control by volume setting,
|
|
* so here we process multi pcm datas with volume value.
|
|
*
|
|
*/
|
|
static void out_multi_pcm_volume_process(struct stream_out *out, void *buffer)
|
|
{
|
|
if ((out == NULL) || (buffer == NULL)){
|
|
return ;
|
|
}
|
|
|
|
int format = out->config.format;
|
|
int channel = out->config.channels;
|
|
if((format == PCM_FORMAT_S16_LE)) {
|
|
float left = out->volume[0];
|
|
short *pcm = (short*)buffer;
|
|
float temp = 0;
|
|
for (int ch = 0; ch < channel; ch++) {
|
|
temp = (float)pcm[ch];
|
|
pcm[ch] = (short)(temp*left);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* switch LFE and FC of one multi pcm frame
|
|
* swtich Front Center's datas and Low Frequency datas
|
|
* 5.1 FL+FR+FC+LFE+BL+BR
|
|
* 5.1(side) FL+FR+FC+LFE+SL+SR
|
|
* 7.1 FL+FR+FC+LFE+SL+SR+BL+BR
|
|
* the datas needed in HDMI is:
|
|
* FL+FR+LFE+FC+SL+SR+BL+BR
|
|
*/
|
|
static void out_multi_pcm_switch_fc_lfe(struct stream_out *out, void *buffer)
|
|
{
|
|
if ((out == NULL) || (buffer == NULL)) {
|
|
return ;
|
|
}
|
|
|
|
const int CENTER = 2;
|
|
const int LFE = 3;
|
|
int channel = out->config.channels;
|
|
int format = out->config.format;
|
|
audio_channel_mask_t channel_mask = out->channel_mask;
|
|
bool hasLFE = ((channel_mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) != 0);
|
|
|
|
if (format == PCM_FORMAT_S16_LE) {
|
|
short *pcm = (short*)buffer;
|
|
short temp = 0;
|
|
if (hasLFE && ((channel == 6) || (channel == 8))) {
|
|
// Front Center's datas
|
|
temp = pcm[CENTER];
|
|
// swap FC and Low Frequency Effect's datas
|
|
pcm[CENTER] = pcm[LFE];
|
|
pcm[LFE] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void out_multi_pcm_process(struct stream_out *out, void * buffer, size_t len) {
|
|
if((out == NULL) || (buffer == NULL) || (len <= 0)){
|
|
return ;
|
|
}
|
|
|
|
int format = out->config.format;
|
|
// only process PCM16
|
|
if (format == PCM_FORMAT_S16_LE) {
|
|
short *pcm = (short*)buffer;
|
|
int channel = out->config.channels;
|
|
int frames = len/audio_stream_out_frame_size(out);
|
|
for (int frame = 0; frame < frames; frame ++){
|
|
out_multi_pcm_volume_process(out, pcm);
|
|
out_multi_pcm_switch_fc_lfe(out, pcm);
|
|
pcm += channel;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief out_write
|
|
*
|
|
* @param stream
|
|
* @param buffer
|
|
* @param bytes
|
|
*
|
|
* @returns
|
|
*/
|
|
|
|
static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
|
|
size_t bytes)
|
|
{
|
|
int ret = 0;
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
struct audio_device *adev = out->dev;
|
|
size_t newbytes = bytes * 2;
|
|
int i,card;
|
|
uint64_t startTime, endTime, cost;
|
|
|
|
/* FIXME This comment is no longer correct
|
|
* acquiring hw device mutex systematically is useful if a low
|
|
* priority thread is waiting on the output stream mutex - e.g.
|
|
* executing out_set_parameters() while holding the hw device
|
|
* mutex
|
|
*/
|
|
//check_hdmi_reconnect(out);
|
|
pthread_mutex_lock(&out->lock);
|
|
if (out->standby) {
|
|
pthread_mutex_unlock(&out->lock);
|
|
lock_all_outputs(adev);
|
|
if (!out->standby) {
|
|
unlock_all_outputs(adev, out);
|
|
goto false_alarm;
|
|
}
|
|
ret = start_output_stream(out);
|
|
if (ret < 0) {
|
|
unlock_all_outputs(adev, NULL);
|
|
goto final_exit;
|
|
}
|
|
out->standby = false;
|
|
unlock_all_outputs(adev, out);
|
|
}
|
|
|
|
false_alarm:
|
|
if (out->disabled) {
|
|
ret = -EPIPE;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
#ifdef AUDIO_3A
|
|
if (adev->voice_api != NULL) {
|
|
int ret = 0;
|
|
adev->voice_api->queuePlaybackBuffer(buffer, bytes);
|
|
ret = adev->voice_api->getPlaybackBuffer(buffer, bytes);
|
|
if (ret < 0) {
|
|
memset((char *)buffer, 0x00, bytes);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Write to all active PCMs */
|
|
if (is_type_in_outdevices(out, AUDIO_DEVICE_OUT_AUX_DIGITAL) && is_bitstream(out)) {
|
|
ret = bitstream_write_data(out, (void*)buffer, bytes);
|
|
if(ret < 0) {
|
|
goto exit;
|
|
}
|
|
} else {
|
|
if(is_multi_pcm(out)) {
|
|
if(out->device == AUDIO_DEVICE_OUT_AUX_DIGITAL) {
|
|
out_multi_pcm_process(out, buffer, bytes);
|
|
}
|
|
}
|
|
|
|
out_mute_data(out,(void*)buffer,bytes);
|
|
dump_out_data(buffer, bytes);
|
|
ret = -1;
|
|
for (i = 0; i < SND_OUT_SOUND_CARD_MAX; i++)
|
|
if (out->pcm[i]) {
|
|
if (i == SND_OUT_SOUND_CARD_BT) {
|
|
// HARD CODE FIXME 48000 stereo -> 8000 stereo
|
|
size_t inFrameCount = bytes/2/2;
|
|
|
|
int destRate = out->config.rate;
|
|
int srcRate = adev->bt_wb_speech_enabled? 16000:8000;
|
|
int coefficient = (destRate/srcRate);
|
|
size_t outFrameCount = inFrameCount/coefficient;
|
|
|
|
int16_t out_buffer[outFrameCount*2];
|
|
memset(out_buffer, 0x00, outFrameCount*2);
|
|
|
|
out->resampler->resample_from_input(out->resampler,
|
|
(const int16_t *)buffer,
|
|
&inFrameCount,
|
|
out_buffer,
|
|
&outFrameCount);
|
|
|
|
ret = pcm_write(out->pcm[i], (void *)out_buffer, outFrameCount*2*2);
|
|
if (ret != 0)
|
|
break;
|
|
} else {
|
|
/*
|
|
* do not write hdmi/spdif snd sound if they are taken by other bitstream/multi channle pcm stream
|
|
*/
|
|
if(((i == SND_OUT_SOUND_CARD_HDMI) && (adev->owner[SOUND_CARD_HDMI] != (int*)out) && (adev->owner[SOUND_CARD_HDMI] != NULL)) ||
|
|
((i == SND_OUT_SOUND_CARD_SPDIF) && (adev->owner[SOUND_CARD_SPDIF] != (int*)out) && (adev->owner[SOUND_CARD_SPDIF] != NULL))){
|
|
continue;
|
|
}
|
|
ret = pcm_write(out->pcm[i], (void *)buffer, bytes);
|
|
if (ret != 0) {
|
|
ALOGD("%s:%d pcm_write error, errno = %d, %s",
|
|
__FUNCTION__, __LINE__, errno, pcm_get_error(out->pcm[i]));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exit:
|
|
pthread_mutex_unlock(&out->lock);
|
|
final_exit:
|
|
{
|
|
// For PCM we always consume the buffer and return #bytes regardless of ret.
|
|
out->written += bytes / (out->config.channels * sizeof(short));
|
|
out->nframes = out->written;
|
|
}
|
|
if (ret != 0) {
|
|
ALOGV("AudioData write error , keep slience! ret = %d", ret);
|
|
endTime = getRelativeUs();
|
|
cost = endTime - startTime;
|
|
uint64_t time = (uint64_t)bytes * 1000000ll / audio_stream_out_frame_size(stream) /
|
|
out_get_sample_rate(&stream->common);
|
|
if (cost < time) {
|
|
usleep((int)(time - cost));
|
|
}
|
|
|
|
/*
|
|
* HDR video will set hdmi to hdr mode in kernel, this operation will
|
|
* lead hdmi card enter stop state(see sound/soc/codecs/hdmi-codec.c),
|
|
* and pcm_write will alway fail, and pcm_prepare in pcm_write can not
|
|
* recovery this state. It can only recovery by reopen sound card.
|
|
*/
|
|
if (!out->standby && !out->disabled) {
|
|
lock_all_outputs(adev);
|
|
do_out_standby(out);
|
|
unlock_all_outputs(adev, NULL);
|
|
}
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_render_position
|
|
*
|
|
* @param stream
|
|
* @param dsp_frames
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_get_render_position(const struct audio_stream_out *stream,
|
|
uint32_t *dsp_frames)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
|
|
*dsp_frames = out->nframes;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief out_add_audio_effect
|
|
*
|
|
* @param stream
|
|
* @param effect
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief out_remove_audio_effect
|
|
*
|
|
* @param stream
|
|
* @param effect
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_next_write_timestamp
|
|
*
|
|
* @param stream
|
|
* @param timestamp
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_get_next_write_timestamp(const struct audio_stream_out *stream,
|
|
int64_t *timestamp)
|
|
{
|
|
ALOGV("%s: %d Entered", __FUNCTION__, __LINE__);
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/**
|
|
* @brief out_get_presentation_position
|
|
*
|
|
* @param stream
|
|
* @param frames
|
|
* @param timestamp
|
|
*
|
|
* @returns
|
|
*/
|
|
static int out_get_presentation_position(const struct audio_stream_out *stream,
|
|
uint64_t *frames, struct timespec *timestamp)
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
int ret = -1;
|
|
|
|
pthread_mutex_lock(&out->lock);
|
|
|
|
int i;
|
|
// There is a question how to implement this correctly when there is more than one PCM stream.
|
|
// We are just interested in the frames pending for playback in the kernel buffer here,
|
|
// not the total played since start. The current behavior should be safe because the
|
|
// cases where both cards are active are marginal.
|
|
for (i = 0; i < SND_OUT_SOUND_CARD_MAX; i++)
|
|
if (out->pcm[i]) {
|
|
size_t avail;
|
|
//ALOGD("===============%s,%d==============",__FUNCTION__,__LINE__);
|
|
if (pcm_get_htimestamp(out->pcm[i], &avail, timestamp) == 0) {
|
|
size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
|
|
//ALOGD("===============%s,%d==============",__FUNCTION__,__LINE__);
|
|
// FIXME This calculation is incorrect if there is buffering after app processor
|
|
int64_t signed_frames = out->written - kernel_buffer_size + avail;
|
|
//signed_frames -= 17;
|
|
//ALOGV("============singed_frames:%lld=======",signed_frames);
|
|
//ALOGV("============timestamp:%lld==========",timestamp);
|
|
// It would be unusual for this value to be negative, but check just in case ...
|
|
if (signed_frames >= 0) {
|
|
*frames = signed_frames;
|
|
ret = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&out->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief in_get_sample_rate
|
|
* audio_stream_in implementation
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static uint32_t in_get_sample_rate(const struct audio_stream *stream)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
//ALOGV("%s:get requested_rate : %d ",__FUNCTION__,in->requested_rate);
|
|
return in->requested_rate;
|
|
}
|
|
|
|
/**
|
|
* @brief in_set_sample_rate
|
|
*
|
|
* @param stream
|
|
* @param rate
|
|
*
|
|
* @returns
|
|
*/
|
|
static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief in_get_channels
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
|
|
//ALOGV("%s:get channel_mask : %d ",__FUNCTION__,in->channel_mask);
|
|
return in->channel_mask;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief in_get_buffer_size
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static size_t in_get_buffer_size(const struct audio_stream *stream)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
|
|
return get_input_buffer_size(in->requested_rate,
|
|
AUDIO_FORMAT_PCM_16_BIT,
|
|
audio_channel_count_from_in_mask(in_get_channels(stream)),
|
|
(in->flags & AUDIO_INPUT_FLAG_FAST) != 0);
|
|
}
|
|
|
|
/**
|
|
* @brief in_get_format
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static audio_format_t in_get_format(const struct audio_stream *stream)
|
|
{
|
|
return AUDIO_FORMAT_PCM_16_BIT;
|
|
}
|
|
|
|
/**
|
|
* @brief in_set_format
|
|
*
|
|
* @param stream
|
|
* @param format
|
|
*
|
|
* @returns
|
|
*/
|
|
static int in_set_format(struct audio_stream *stream, audio_format_t format)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/**
|
|
* @brief do_in_standby
|
|
* must be called with in stream and hw device mutex locked
|
|
*
|
|
* @param in
|
|
*/
|
|
static void do_in_standby(struct stream_in *in)
|
|
{
|
|
struct audio_device *adev = in->dev;
|
|
|
|
if (!in->standby) {
|
|
pcm_close(in->pcm);
|
|
in->pcm = NULL;
|
|
|
|
if (in->device == AUDIO_DEVICE_IN_HDMI) {
|
|
route_pcm_close(HDMI_IN_CAPTURE_OFF_ROUTE);
|
|
}
|
|
|
|
in->dev->input_source = AUDIO_SOURCE_DEFAULT;
|
|
in->dev->in_device = AUDIO_DEVICE_NONE;
|
|
in->dev->in_channel_mask = 0;
|
|
in->standby = true;
|
|
route_pcm_close(CAPTURE_OFF_ROUTE);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief in_standby
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static int in_standby(struct audio_stream *stream)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
|
|
pthread_mutex_lock(&in->lock);
|
|
pthread_mutex_lock(&in->dev->lock);
|
|
|
|
do_in_standby(in);
|
|
|
|
pthread_mutex_unlock(&in->dev->lock);
|
|
pthread_mutex_unlock(&in->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief in_dump
|
|
*
|
|
* @param stream
|
|
* @param fd
|
|
*
|
|
* @returns
|
|
*/
|
|
int in_dump(const struct audio_stream *stream, int fd)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
|
|
ALOGD("in->Device : 0x%x", in->device);
|
|
ALOGD("in->SampleRate : %d", in->config->rate);
|
|
ALOGD("in->Channels : %d", in->config->channels);
|
|
ALOGD("in->Formate : %d", in->config->format);
|
|
ALOGD("in->PreiodSize : %d", in->config->period_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief in_set_parameters
|
|
*
|
|
* @param stream
|
|
* @param kvpairs
|
|
*
|
|
* @returns
|
|
*/
|
|
static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
struct audio_device *adev = in->dev;
|
|
struct str_parms *parms;
|
|
char value[32];
|
|
int ret;
|
|
int status = 0;
|
|
unsigned int val;
|
|
bool apply_now = false;
|
|
ALOGV("%s: kvpairs = %s", __func__, kvpairs);
|
|
parms = str_parms_create_str(kvpairs);
|
|
//set channel_mask
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_CHANNELS,
|
|
value, sizeof(value));
|
|
if (ret >= 0) {
|
|
val = atoi(value);
|
|
in->channel_mask = val;
|
|
}
|
|
// set sample rate
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
|
|
value, sizeof(value));
|
|
if (ret >= 0) {
|
|
val = atoi(value);
|
|
in->requested_rate = val;
|
|
}
|
|
pthread_mutex_lock(&in->lock);
|
|
pthread_mutex_lock(&adev->lock);
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
|
|
value, sizeof(value));
|
|
if (ret >= 0) {
|
|
val = atoi(value);
|
|
/* no audio source uses val == 0 */
|
|
if ((in->input_source != val) && (val != 0)) {
|
|
in->input_source = val;
|
|
apply_now = !in->standby;
|
|
}
|
|
}
|
|
if (apply_now) {
|
|
adev->input_source = in->input_source;
|
|
adev->in_device = in->device;
|
|
route_pcm_open(getRouteFromDevice(in->device | AUDIO_DEVICE_BIT_IN));
|
|
}
|
|
pthread_mutex_unlock(&adev->lock);
|
|
pthread_mutex_unlock(&in->lock);
|
|
str_parms_destroy(parms);
|
|
ALOGV("%s: exit: status(%d)", __func__, status);
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief in_get_parameters
|
|
*
|
|
* @param stream
|
|
* @param keys
|
|
*
|
|
* @returns
|
|
*/
|
|
static char * in_get_parameters(const struct audio_stream *stream,
|
|
const char *keys)
|
|
{
|
|
ALOGD("%s: keys = %s", __func__, keys);
|
|
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
struct str_parms *query = str_parms_create_str(keys);
|
|
char *str = NULL;
|
|
struct str_parms *reply = str_parms_create();
|
|
|
|
if (stream_get_parameter_formats(stream,query,reply) == 0) {
|
|
str = str_parms_to_str(reply);
|
|
} else if (stream_get_parameter_channels(query, reply, &in->supported_channel_masks[0]) == 0) {
|
|
str = str_parms_to_str(reply);
|
|
} else if (stream_get_parameter_rates(query, reply, &in->supported_sample_rates[0]) == 0) {
|
|
str = str_parms_to_str(reply);
|
|
} else {
|
|
ALOGD("%s,str_parms_get_str failed !",__func__);
|
|
str = strdup("");
|
|
}
|
|
|
|
str_parms_destroy(query);
|
|
str_parms_destroy(reply);
|
|
|
|
ALOGV("%s,exit -- str = %s",__func__,str);
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
* @brief in_set_gain
|
|
*
|
|
* @param stream
|
|
* @param gain
|
|
*
|
|
* @returns
|
|
*/
|
|
static int in_set_gain(struct audio_stream_in *stream, float gain)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief in_apply_ramp
|
|
*
|
|
* @param in
|
|
* @param buffer
|
|
* @param frames
|
|
*/
|
|
static void in_apply_ramp(struct stream_in *in, int16_t *buffer, size_t frames)
|
|
{
|
|
size_t i;
|
|
uint16_t vol = in->ramp_vol;
|
|
uint16_t step = in->ramp_step;
|
|
|
|
frames = (frames < in->ramp_frames) ? frames : in->ramp_frames;
|
|
|
|
if (in->channel_mask == AUDIO_CHANNEL_IN_MONO)
|
|
for (i = 0; i < frames; i++) {
|
|
buffer[i] = (int16_t)((buffer[i] * vol) >> 16);
|
|
vol += step;
|
|
}
|
|
else
|
|
for (i = 0; i < frames; i++) {
|
|
buffer[2*i] = (int16_t)((buffer[2*i] * vol) >> 16);
|
|
buffer[2*i + 1] = (int16_t)((buffer[2*i + 1] * vol) >> 16);
|
|
vol += step;
|
|
}
|
|
|
|
|
|
in->ramp_vol = vol;
|
|
in->ramp_frames -= frames;
|
|
}
|
|
|
|
/**
|
|
* @brief in_read
|
|
*
|
|
* @param stream
|
|
* @param buffer
|
|
* @param bytes
|
|
*
|
|
* @returns
|
|
*/
|
|
static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
|
|
size_t bytes)
|
|
{
|
|
int ret = 0;
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
struct audio_device *adev = in->dev;
|
|
size_t frames_rq = bytes / audio_stream_in_frame_size(stream);
|
|
size_t frames_rd = 0;
|
|
|
|
if (in->device == AUDIO_DEVICE_IN_HDMI) {
|
|
unsigned int rate = get_hdmiin_audio_rate(adev);
|
|
if(rate != in->config->rate){
|
|
ALOGD("HDMI-In: rate is changed: %d -> %d, restart input stream",
|
|
in->config->rate, rate);
|
|
do_in_standby(in);
|
|
}
|
|
}
|
|
/*
|
|
* acquiring hw device mutex systematically is useful if a low
|
|
* priority thread is waiting on the input stream mutex - e.g.
|
|
* executing in_set_parameters() while holding the hw device
|
|
* mutex
|
|
*/
|
|
pthread_mutex_lock(&in->lock);
|
|
if (in->standby) {
|
|
pthread_mutex_lock(&adev->lock);
|
|
ret = start_input_stream(in);
|
|
pthread_mutex_unlock(&adev->lock);
|
|
if (ret < 0)
|
|
goto exit;
|
|
in->standby = false;
|
|
#ifdef AUDIO_3A
|
|
if (adev->voice_api != NULL) {
|
|
adev->voice_api->start();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*if (in->num_preprocessors != 0)
|
|
ret = process_frames(in, buffer, frames_rq);
|
|
else */
|
|
//ALOGV("%s:frames_rq:%d",__FUNCTION__,frames_rq);
|
|
frames_rd = read_frames(in, buffer, frames_rq);
|
|
if (in->read_status) {
|
|
ret = -EPIPE;
|
|
goto exit;
|
|
} else if (frames_rd > 0) {
|
|
in->frames_read += frames_rd;
|
|
bytes = frames_rd * audio_stream_in_frame_size(stream);
|
|
}
|
|
|
|
dump_in_data(buffer, bytes);
|
|
|
|
#ifdef AUDIO_3A
|
|
do {
|
|
if (adev->voice_api != NULL) {
|
|
int ret = 0;
|
|
ret = adev->voice_api->quueCaputureBuffer(buffer, bytes);
|
|
if (ret < 0) break;
|
|
ret = adev->voice_api->getCapureBuffer(buffer, bytes);
|
|
if (ret < 0) memset(buffer, 0x00, bytes);
|
|
}
|
|
} while (0);
|
|
#endif
|
|
|
|
if (in->ramp_frames > 0)
|
|
in_apply_ramp(in, buffer, frames_rq);
|
|
|
|
/*
|
|
* Instead of writing zeroes here, we could trust the hardware
|
|
* to always provide zeroes when muted.
|
|
*/
|
|
if (ret == 0 && adev->mic_mute)
|
|
memset(buffer, 0, bytes);
|
|
#ifdef ALSA_IN_DEBUG
|
|
fwrite(buffer, bytes, 1, in_debug);
|
|
#endif
|
|
exit:
|
|
if (ret < 0) {
|
|
memset(buffer, 0, bytes);
|
|
usleep(bytes * 1000000 / audio_stream_in_frame_size(stream) /
|
|
in_get_sample_rate(&stream->common));
|
|
do_in_standby(in);
|
|
}
|
|
|
|
pthread_mutex_unlock(&in->lock);
|
|
return bytes;
|
|
}
|
|
|
|
/**
|
|
* @brief in_get_input_frames_lost
|
|
*
|
|
* @param stream
|
|
*
|
|
* @returns
|
|
*/
|
|
static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief in_add_audio_effect
|
|
*
|
|
* @param stream
|
|
* @param effect
|
|
*
|
|
* @returns
|
|
*/
|
|
static int in_add_audio_effect(const struct audio_stream *stream,
|
|
effect_handle_t effect)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
effect_descriptor_t descr;
|
|
if ((*effect)->get_descriptor(effect, &descr) == 0) {
|
|
|
|
pthread_mutex_lock(&in->lock);
|
|
pthread_mutex_lock(&in->dev->lock);
|
|
|
|
|
|
pthread_mutex_unlock(&in->dev->lock);
|
|
pthread_mutex_unlock(&in->lock);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief in_remove_audio_effect
|
|
*
|
|
* @param stream
|
|
* @param effect
|
|
*
|
|
* @returns
|
|
*/
|
|
static int in_remove_audio_effect(const struct audio_stream *stream,
|
|
effect_handle_t effect)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
effect_descriptor_t descr;
|
|
if ((*effect)->get_descriptor(effect, &descr) == 0) {
|
|
|
|
pthread_mutex_lock(&in->lock);
|
|
pthread_mutex_lock(&in->dev->lock);
|
|
|
|
|
|
pthread_mutex_unlock(&in->dev->lock);
|
|
pthread_mutex_unlock(&in->lock);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adev_get_microphones(const struct audio_hw_device *dev,
|
|
struct audio_microphone_characteristic_t *mic_array,
|
|
size_t *mic_count)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
size_t actual_mic_count = 0;
|
|
|
|
int card_no = 0;
|
|
|
|
char snd_card_node_id[100]={0};
|
|
char snd_card_node_cap[100]={0};
|
|
char address[32] = "bottom";
|
|
|
|
do{
|
|
sprintf(snd_card_node_id, "/proc/asound/card%d/id", card_no);
|
|
if (access(snd_card_node_id,F_OK) == -1) break;
|
|
|
|
sprintf(snd_card_node_cap, "/proc/asound/card%d/pcm0c/info", card_no);
|
|
if (access(snd_card_node_cap,F_OK) == -1) continue;
|
|
|
|
actual_mic_count++;
|
|
}while(++card_no);
|
|
|
|
mic_array->device = -2147483644;
|
|
strcpy(mic_array->address,address);
|
|
|
|
ALOGD("%s,get capture mic actual_mic_count =%d",__func__,actual_mic_count);
|
|
*mic_count = actual_mic_count;
|
|
return 0;
|
|
}
|
|
|
|
static int in_get_capture_position(const struct audio_stream_in *stream,
|
|
int64_t *frames, int64_t *time)
|
|
{
|
|
if (stream == NULL || frames == NULL || time == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
int ret = -ENOSYS;
|
|
|
|
pthread_mutex_lock(&in->lock);
|
|
// note: ST sessions do not close the alsa pcm driver synchronously
|
|
// on standby. Therefore, we may return an error even though the
|
|
// pcm stream is still opened.
|
|
if (in->standby) {
|
|
ALOGD("skip when standby is true.");
|
|
goto exit;
|
|
}
|
|
if (in->pcm) {
|
|
struct timespec timestamp;
|
|
size_t avail;
|
|
if (pcm_get_htimestamp(in->pcm, &avail, ×tamp) == 0) {
|
|
*frames = in->frames_read + avail;
|
|
*time = timestamp.tv_sec * 1000000000LL + timestamp.tv_nsec;
|
|
ret = 0;
|
|
ALOGD("Pos: %lld %lld", *time, *frames);
|
|
}
|
|
}
|
|
exit:
|
|
pthread_mutex_unlock(&in->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int in_get_active_microphones(const struct audio_stream_in *stream,
|
|
struct audio_microphone_characteristic_t *mic_array,
|
|
size_t *mic_count)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
struct audio_device *adev = in->dev;
|
|
pthread_mutex_lock(&in->lock);
|
|
pthread_mutex_lock(&adev->lock);
|
|
|
|
size_t actual_mic_count = 0;
|
|
int card_no = 0;
|
|
|
|
char snd_card_node_id[100]={0};
|
|
char snd_card_node_cap[100]={0};
|
|
char snd_card_info[100]={0};
|
|
char snd_card_state[255]={0};
|
|
|
|
do{
|
|
sprintf(snd_card_node_id, "/proc/asound/card%d/id", card_no);
|
|
if (access(snd_card_node_id,F_OK) == -1) break;
|
|
|
|
sprintf(snd_card_node_cap, "/proc/asound/card%d/pcm0c/info", card_no);
|
|
if (access(snd_card_node_cap,F_OK) == -1) {
|
|
continue;
|
|
} else {
|
|
sprintf(snd_card_info, "/proc/asound/card%d/pcm0c/sub0/status", card_no);
|
|
int fd;
|
|
fd = open(snd_card_info, O_RDONLY);
|
|
if (fd < 0) {
|
|
ALOGE("%s,failed to open node: %s", __func__, snd_card_info);
|
|
} else {
|
|
int length = read(fd, snd_card_state, sizeof(snd_card_state) -1);
|
|
snd_card_state[length] = 0;
|
|
if (strcmp(snd_card_state, "closed") != 0) actual_mic_count++;
|
|
}
|
|
close(fd);
|
|
}
|
|
} while(++card_no);
|
|
|
|
pthread_mutex_unlock(&adev->lock);
|
|
pthread_mutex_unlock(&in->lock);
|
|
|
|
ALOGD("%s,get active mic actual_mic_count =%d", __func__, actual_mic_count);
|
|
*mic_count = actual_mic_count;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* get support channels mask of hdmi from parsing edid of hdmi
|
|
*/
|
|
static void get_hdmi_support_channels_masks(struct stream_out *out)
|
|
{
|
|
if(out == NULL)
|
|
return ;
|
|
|
|
int channels = get_hdmi_audio_speaker_allocation(&out->hdmi_audio);
|
|
switch (channels) {
|
|
case AUDIO_CHANNEL_OUT_5POINT1:
|
|
ALOGD("%s: HDMI Support 5.1 channels pcm",__FUNCTION__);
|
|
out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1;
|
|
out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_STEREO;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_7POINT1:
|
|
ALOGD("%s: HDMI Support 7.1 channels pcm",__FUNCTION__);
|
|
out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1;
|
|
out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_STEREO:
|
|
default:
|
|
ALOGD("%s: HDMI Support 2 channels pcm",__FUNCTION__);
|
|
out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
|
|
out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_MONO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief adev_open_output_stream
|
|
*
|
|
* @param dev
|
|
* @param handle
|
|
* @param devices
|
|
* @param flags
|
|
* @param config
|
|
* @param stream_out
|
|
* @param __unused
|
|
*
|
|
* @returns
|
|
*/
|
|
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)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
struct stream_out *out;
|
|
int ret;
|
|
enum output_type type = OUTPUT_LOW_LATENCY;
|
|
bool isPcm = audio_is_linear_pcm(config->format);
|
|
bool isBitstream = false;
|
|
|
|
ALOGD("audio hal adev_open_output_stream devices = 0x%x, flags = %d, samplerate = %d,format = 0x%x",
|
|
devices, flags, config->sample_rate,config->format);
|
|
|
|
// only support PCM or IEC61937 format
|
|
if (!audio_has_proportional_frames(config->format)) {
|
|
ALOGD("%s: format = 0x%x not support", __FUNCTION__, config->format);
|
|
return -1;
|
|
}
|
|
|
|
out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
|
|
if (!out)
|
|
return -ENOMEM;
|
|
|
|
if ((flags & AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO) ||
|
|
(config->format == AUDIO_FORMAT_IEC61937)) {
|
|
isBitstream = true;
|
|
}
|
|
|
|
/*get default supported channel_mask*/
|
|
memset(out->supported_channel_masks, 0, sizeof(out->supported_channel_masks));
|
|
out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
|
|
out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_MONO;
|
|
/*get default supported sample_rate*/
|
|
memset(out->supported_sample_rates, 0, sizeof(out->supported_sample_rates));
|
|
out->supported_sample_rates[0] = 44100;
|
|
out->supported_sample_rates[1] = 48000;
|
|
|
|
if(config != NULL)
|
|
memcpy(&(out->aud_config),config,sizeof(struct audio_config));
|
|
out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
|
|
if (devices == AUDIO_DEVICE_NONE)
|
|
devices = AUDIO_DEVICE_OUT_SPEAKER;
|
|
out->device = devices;
|
|
/*
|
|
* set output_direct_mode to LPCM, means data is not multi pcm or bitstream datas.
|
|
* set output_direct to false, means data is 2 channels pcm
|
|
*/
|
|
out->output_direct_mode = LPCM;
|
|
out->output_direct = false;
|
|
out->snd_reopen = false;
|
|
out->use_default_config = false;
|
|
out->volume[0] = out->volume[1] = 1.0f;
|
|
out->bistream = NULL;
|
|
|
|
init_hdmi_audio(&out->hdmi_audio);
|
|
if(devices == AUDIO_DEVICE_OUT_AUX_DIGITAL) {
|
|
parse_hdmi_audio(&out->hdmi_audio);
|
|
get_hdmi_support_channels_masks(out);
|
|
}
|
|
|
|
if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {
|
|
if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
|
|
if (isBitstream) {
|
|
ALOGD("%s:out = %p HDMI Bitstream",__FUNCTION__,out);
|
|
out->channel_mask = config->channel_mask;
|
|
if (isValidSamplerate(config->sample_rate)) {
|
|
out->config = pcm_config_direct;
|
|
out->config.rate = config->sample_rate;
|
|
out->output_direct = true;
|
|
int channel = audio_channel_count_from_out_mask(config->channel_mask);
|
|
|
|
if (channel == 8 && config->sample_rate == 192000) {
|
|
out->output_direct_mode = HBR;
|
|
} else {
|
|
out->output_direct_mode = NLPCM;
|
|
}
|
|
|
|
if (out->config.format == PCM_FORMAT_S24_LE) {
|
|
if (config->sample_rate >= 176400) {
|
|
out->config.period_size = 1024 * 4;
|
|
} else {
|
|
out->config.period_size = 2048;
|
|
}
|
|
} else {
|
|
out->config.period_size = config->sample_rate/100; // 10ms
|
|
}
|
|
|
|
#ifdef RK3128 // only 3128 using 16bit to bitstream
|
|
out->config.format = PCM_FORMAT_S16_LE;
|
|
#endif
|
|
type = OUTPUT_HDMI_MULTI;
|
|
} else {
|
|
out->config = pcm_config;
|
|
out->config.rate = 44100;
|
|
ALOGE("hdmi bitstream samplerate %d unsupport", config->sample_rate);
|
|
}
|
|
out->config.channels = audio_channel_count_from_out_mask(config->channel_mask);
|
|
if (out->config.channels < 2)
|
|
out->config.channels = 2;
|
|
out->pcm_device = PCM_DEVICE;
|
|
out->device = AUDIO_DEVICE_OUT_AUX_DIGITAL;
|
|
} else if (isPcm){ // multi pcm
|
|
if (config->sample_rate == 0)
|
|
config->sample_rate = HDMI_MULTI_DEFAULT_SAMPLING_RATE;
|
|
if (config->channel_mask == 0)
|
|
config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
|
|
|
|
int layout = get_hdmi_audio_speaker_allocation(&out->hdmi_audio);
|
|
unsigned int mask = (layout&config->channel_mask);
|
|
ALOGD("%s:out = %p HDMI multi pcm: layout = 0x%x,mask = 0x%x",
|
|
__FUNCTION__,out,layout,mask);
|
|
// current hdmi allocation(speaker) only support MONO or STEREO
|
|
if(mask <= (int)AUDIO_CHANNEL_OUT_STEREO) {
|
|
ALOGD("%s:out = %p input stream is multi pcm,channle mask = 0x%x,but hdmi not support,mixer it to stereo output",
|
|
__FUNCTION__,out,config->channel_mask);
|
|
out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
|
|
out->config = pcm_config;
|
|
out->pcm_device = PCM_DEVICE;
|
|
type = OUTPUT_LOW_LATENCY;
|
|
out->device = AUDIO_DEVICE_OUT_AUX_DIGITAL;
|
|
out->output_direct = false;
|
|
} else {
|
|
/*
|
|
* maybe input audio stream is 7.1 channels,
|
|
* but hdmi only support 5.1, we also output 7.1 for default.
|
|
* Is better than output 2 channels after mixer?
|
|
* If customer like output 2 channles data after mixer,
|
|
* modify codes here
|
|
*/
|
|
out->channel_mask = config->channel_mask;
|
|
out->config = pcm_config_hdmi_multi;
|
|
out->config.rate = config->sample_rate;
|
|
out->config.channels = audio_channel_count_from_out_mask(config->channel_mask);
|
|
out->pcm_device = PCM_DEVICE;
|
|
type = OUTPUT_HDMI_MULTI;
|
|
out->device = AUDIO_DEVICE_OUT_AUX_DIGITAL;
|
|
out->output_direct = true;
|
|
}
|
|
} else {
|
|
ALOGD("Not any bitstream mode!");
|
|
}
|
|
} else if ((devices & AUDIO_DEVICE_OUT_SPDIF) && isBitstream) {
|
|
ALOGD("%s:out = %p Spdif Bitstream",__FUNCTION__,out);
|
|
out->channel_mask = config->channel_mask;
|
|
out->config = pcm_config_direct;
|
|
if ((config->sample_rate == 48000) ||
|
|
(config->sample_rate == 32000) ||
|
|
(config->sample_rate == 44100)) {
|
|
out->config.rate = config->sample_rate;
|
|
out->config.format = PCM_FORMAT_S16_LE;
|
|
out->config.period_size = config->sample_rate/100; // 10ms
|
|
} else {
|
|
out->config.rate = 44100;
|
|
ALOGE("spdif passthrough samplerate %d is unsupport",config->sample_rate);
|
|
}
|
|
out->config.channels = audio_channel_count_from_out_mask(config->channel_mask);
|
|
devices = AUDIO_DEVICE_OUT_SPDIF;
|
|
out->pcm_device = PCM_DEVICE;
|
|
out->output_direct = true;
|
|
type = OUTPUT_HDMI_MULTI;
|
|
out->device = AUDIO_DEVICE_OUT_SPDIF;
|
|
out->output_direct_mode = NLPCM;
|
|
} else {
|
|
out->config = pcm_config;
|
|
out->pcm_device = PCM_DEVICE;
|
|
type = OUTPUT_LOW_LATENCY;
|
|
}
|
|
} else if (flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
|
|
out->config = pcm_config_deep;
|
|
out->pcm_device = PCM_DEVICE_DEEP;
|
|
type = OUTPUT_DEEP_BUF;
|
|
} else {
|
|
out->config = pcm_config;
|
|
out->pcm_device = PCM_DEVICE;
|
|
type = OUTPUT_LOW_LATENCY;
|
|
}
|
|
|
|
ALOGD("out->config.rate = %d, out->config.channels = %d out->config.format = %d",
|
|
out->config.rate, out->config.channels, out->config.format);
|
|
|
|
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_next_write_timestamp = out_get_next_write_timestamp;
|
|
out->stream.get_presentation_position = out_get_presentation_position;
|
|
out->handle = handle;
|
|
out->dev = adev;
|
|
|
|
out->standby = true;
|
|
out->nframes = 0;
|
|
|
|
pthread_mutex_lock(&adev->lock_outputs);
|
|
if (adev->outputs[type]) {
|
|
pthread_mutex_unlock(&adev->lock_outputs);
|
|
ret = -EBUSY;
|
|
goto err_open;
|
|
}
|
|
adev->outputs[type] = out;
|
|
pthread_mutex_unlock(&adev->lock_outputs);
|
|
adev_add_stream_to_list(out->dev, &out->dev->output_stream_list, &out->list_node);
|
|
*stream_out = &out->stream;
|
|
|
|
return 0;
|
|
|
|
err_open:
|
|
if (out != NULL) {
|
|
destory_hdmi_audio(&out->hdmi_audio);
|
|
free(out);
|
|
}
|
|
*stream_out = NULL;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_close_output_stream
|
|
*
|
|
* @param dev
|
|
* @param stream
|
|
*/
|
|
static void adev_close_output_stream(struct audio_hw_device *dev,
|
|
struct audio_stream_out *stream)
|
|
{
|
|
struct audio_device *adev;
|
|
enum output_type type;
|
|
|
|
ALOGD("adev_close_output_stream!");
|
|
out_standby(&stream->common);
|
|
adev = (struct audio_device *)dev;
|
|
pthread_mutex_lock(&adev->lock_outputs);
|
|
for (type = 0; type < OUTPUT_TOTAL; ++type) {
|
|
if (adev->outputs[type] == (struct stream_out *) stream) {
|
|
adev->outputs[type] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
{
|
|
struct stream_out *out = (struct stream_out *)stream;
|
|
device_lock(out->dev);
|
|
list_remove(&out->list_node);
|
|
device_unlock(out->dev);
|
|
destory_hdmi_audio(&out->hdmi_audio);
|
|
}
|
|
|
|
pthread_mutex_unlock(&adev->lock_outputs);
|
|
free(stream);
|
|
}
|
|
|
|
/**
|
|
* @brief adev_set_parameters
|
|
*
|
|
* @param dev
|
|
* @param kvpairs
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
struct str_parms *parms = NULL;
|
|
char value[32] = "";
|
|
/*
|
|
* ret is the result of str_parms_get_str,
|
|
* if no paramter which str_parms_get_str to get, it will return result < 0 always.
|
|
* For example: kvpairs = connect=1024 is coming
|
|
* str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SCREEN_STATE,value, sizeof(value))
|
|
* will return result < 0,this means no screen_state in parms
|
|
*/
|
|
int ret = 0;
|
|
/*
|
|
* status is the result of one process,
|
|
* For example: kvpairs = screen_state=on is coming,
|
|
* str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SCREEN_STATE,value, sizeof(value))
|
|
* will return result >= 0,this means screen is on, we can do something,
|
|
* if the things we do is correct, we set status = 0, or status < 0 means fail.
|
|
*/
|
|
int status = 0;
|
|
ALOGD("%s: kvpairs = %s", __func__, kvpairs);
|
|
parms = str_parms_create_str(kvpairs);
|
|
pthread_mutex_lock(&adev->lock);
|
|
|
|
// screen state off/on
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SCREEN_STATE, // screen_state
|
|
value, sizeof(value));
|
|
if (ret >= 0) {
|
|
if(strcmp(value,"on") == 0){
|
|
adev->screenOff = false;
|
|
} else if(strcmp(value,"off") == 0){
|
|
adev->screenOff = true;
|
|
}
|
|
}
|
|
#ifdef AUDIO_BITSTREAM_REOPEN_HDMI
|
|
// hdmi reconnect
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, // hdmi reconnect
|
|
value, sizeof(value));
|
|
if (ret >= 0) {
|
|
int device = atoi(value);
|
|
if(device == (int)AUDIO_DEVICE_OUT_AUX_DIGITAL){
|
|
struct stream_out *out = adev->outputs[OUTPUT_HDMI_MULTI];
|
|
if((out != NULL) && is_bitstream(out) && (out->device == AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
|
|
ALOGD("%s: hdmi connect when audio stream is output over hdmi, do something,out = %p",__FUNCTION__,out);
|
|
out->snd_reopen = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value));
|
|
if (ret >= 0) {
|
|
adev->bt_wb_speech_enabled = !strcmp(value, AUDIO_PARAMETER_VALUE_ON);
|
|
ALOGD("%s: adev:0x%p, bt_wb_speech_enabled = %d",
|
|
__func__, adev, adev->bt_wb_speech_enabled);
|
|
}
|
|
ret = str_parms_get_str(parms, "BT_SCO", value, sizeof(value));
|
|
if (ret >= 0) {
|
|
adev->bt_sco_reroute ^= !strcmp(value, AUDIO_PARAMETER_VALUE_ON);
|
|
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) != 0){
|
|
adev->bt_wb_speech_enabled = false;
|
|
}
|
|
}
|
|
ALOGD("%s:bt_wb_speech_enabled = %d, sco reroute=%u",
|
|
__func__, adev->bt_wb_speech_enabled, adev->bt_sco_reroute);
|
|
pthread_mutex_unlock(&adev->lock);
|
|
str_parms_destroy(parms);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* get support formats for bitstream
|
|
* There is no stand interface in andorid to get the formats can be bistream,
|
|
* so we extend get parameter to report formats
|
|
*/
|
|
static int get_support_bitstream_formats(struct str_parms *query,
|
|
struct str_parms *reply)
|
|
{
|
|
int avail = 1024;
|
|
char value[avail];
|
|
|
|
struct hdmi_audio_infors hdmi_edid;
|
|
init_hdmi_audio(&hdmi_edid);
|
|
const char* AUDIO_PARAMETER_STREAM_SUP_BITSTREAM_FORMAT = "sup_bitstream_formats";
|
|
if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_BITSTREAM_FORMAT)) {
|
|
memset(value,0,avail);
|
|
|
|
// get the format can be bistream?
|
|
if(parse_hdmi_audio(&hdmi_edid) >= 0){
|
|
int cursor = 0;
|
|
for(int i = 0; i < ARRAY_SIZE(sSurroundFormat); i++){
|
|
if(is_support_format(&hdmi_edid,sSurroundFormat[i].format)){
|
|
avail -= cursor;
|
|
int length = snprintf(value + cursor, avail, "%s%s",
|
|
cursor > 0 ? "|" : "",
|
|
sSurroundFormat[i].value);
|
|
if (length < 0 || length >= avail) {
|
|
break;
|
|
}
|
|
cursor += length;
|
|
}
|
|
}
|
|
}
|
|
|
|
destory_hdmi_audio(&hdmi_edid);
|
|
str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_BITSTREAM_FORMAT, value);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_get_parameters
|
|
*
|
|
* @param dev
|
|
* @param keys
|
|
*
|
|
* @returns
|
|
*/
|
|
static char * adev_get_parameters(const struct audio_hw_device *dev,
|
|
const char *keys)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
struct str_parms *parms = str_parms_create_str(keys);
|
|
struct str_parms *reply = str_parms_create();
|
|
char *str = NULL;
|
|
ALOGD("%s: keys = %s",__FUNCTION__,keys);
|
|
if (str_parms_has_key(parms, "ec_supported")) {
|
|
str_parms_destroy(parms);
|
|
parms = str_parms_create_str("ec_supported=yes");
|
|
str = str_parms_to_str(parms);
|
|
} else if (get_support_bitstream_formats(parms,reply) == 0) {
|
|
str = str_parms_to_str(reply);
|
|
} else {
|
|
str = strdup("");
|
|
}
|
|
|
|
str_parms_destroy(parms);
|
|
str_parms_destroy(reply);
|
|
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_init_check
|
|
*
|
|
* @param dev
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_init_check(const struct audio_hw_device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_set_voice_volume
|
|
*
|
|
* @param dev
|
|
* @param volume
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
|
|
{
|
|
int ret = 0;
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
if(adev->mode == AUDIO_MODE_IN_CALL) {
|
|
if (volume < 0.0) {
|
|
volume = 0.0;
|
|
} else if (volume > 1.0) {
|
|
volume = 1.0;
|
|
}
|
|
|
|
const char *mixer_ctl_name = "Speaker Playback Volume";
|
|
ret = route_set_voice_volume(mixer_ctl_name,volume);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_set_master_volume
|
|
*
|
|
* @param dev
|
|
* @param volume
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_set_mode
|
|
*
|
|
* @param dev
|
|
* @param mode
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
|
|
ALOGD("%s: set_mode = %d", __func__, mode);
|
|
adev->mode = mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_set_mic_mute
|
|
*
|
|
* @param dev
|
|
* @param state
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
|
|
adev->mic_mute = state;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_get_mic_mute
|
|
*
|
|
* @param dev
|
|
* @param state
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
|
|
*state = adev->mic_mute;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_get_input_buffer_size
|
|
*
|
|
* @param dev
|
|
* @param config
|
|
*
|
|
* @returns
|
|
*/
|
|
static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
|
|
const struct audio_config *config)
|
|
{
|
|
|
|
return get_input_buffer_size(config->sample_rate, config->format,
|
|
audio_channel_count_from_in_mask(config->channel_mask),
|
|
false /* is_low_latency: since we don't know, be conservative */);
|
|
}
|
|
|
|
/**
|
|
* @brief adev_open_input_stream
|
|
*
|
|
* @param dev
|
|
* @param handle
|
|
* @param devices
|
|
* @param config
|
|
* @param stream_in
|
|
* @param flags
|
|
* @param __unused
|
|
* @param __unused
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_open_input_stream(struct audio_hw_device *dev,
|
|
audio_io_handle_t handle,
|
|
audio_devices_t devices,
|
|
struct audio_config *config,
|
|
struct audio_stream_in **stream_in,
|
|
audio_input_flags_t flags,
|
|
const char *address __unused,
|
|
audio_source_t source __unused)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
struct stream_in *in;
|
|
int ret;
|
|
|
|
ALOGD("audio hal adev_open_input_stream devices = 0x%x, flags = %d, config->samplerate = %d,config->channel_mask = %x",
|
|
devices, flags, config->sample_rate,config->channel_mask);
|
|
|
|
*stream_in = NULL;
|
|
#ifdef ALSA_IN_DEBUG
|
|
in_debug = fopen("/data/debug.pcm","wb");//please touch /data/debug.pcm first
|
|
#endif
|
|
/* Respond with a request for mono if a different format is given. */
|
|
//ALOGV("%s:config->channel_mask %d",__FUNCTION__,config->channel_mask);
|
|
if (/*config->channel_mask != AUDIO_CHANNEL_IN_MONO &&
|
|
config->channel_mask != AUDIO_CHANNEL_IN_FRONT_BACK*/
|
|
config->channel_mask != AUDIO_CHANNEL_IN_STEREO) {
|
|
config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
|
|
ALOGE("%s:channel is not support",__FUNCTION__);
|
|
return -EINVAL;
|
|
}
|
|
if (config->sample_rate == 0 ) {
|
|
config->sample_rate = 44100;
|
|
ALOGW("%s: rate is not support",__FUNCTION__);
|
|
}
|
|
|
|
in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
|
|
if (!in)
|
|
return -ENOMEM;
|
|
|
|
/*get default supported channel_mask*/
|
|
memset(in->supported_channel_masks, 0, sizeof(in->supported_channel_masks));
|
|
in->supported_channel_masks[0] = AUDIO_CHANNEL_IN_STEREO;
|
|
in->supported_channel_masks[1] = AUDIO_CHANNEL_IN_MONO;
|
|
/*get default supported sample_rate*/
|
|
memset(in->supported_sample_rates, 0, sizeof(in->supported_sample_rates));
|
|
in->supported_sample_rates[0] = 44100;
|
|
in->supported_sample_rates[1] = 48000;
|
|
|
|
in->stream.common.get_sample_rate = in_get_sample_rate;
|
|
in->stream.common.set_sample_rate = in_set_sample_rate;
|
|
in->stream.common.get_buffer_size = in_get_buffer_size;
|
|
in->stream.common.get_channels = in_get_channels;
|
|
in->stream.common.get_format = in_get_format;
|
|
in->stream.common.set_format = in_set_format;
|
|
in->stream.common.standby = in_standby;
|
|
in->stream.common.dump = in_dump;
|
|
in->stream.common.set_parameters = in_set_parameters;
|
|
in->stream.common.get_parameters = in_get_parameters;
|
|
in->stream.common.add_audio_effect = in_add_audio_effect;
|
|
in->stream.common.remove_audio_effect = in_remove_audio_effect;
|
|
in->stream.set_gain = in_set_gain;
|
|
in->stream.read = in_read;
|
|
in->stream.get_input_frames_lost = in_get_input_frames_lost;
|
|
in->stream.get_active_microphones = in_get_active_microphones;
|
|
in->stream.get_capture_position = in_get_capture_position;
|
|
|
|
#ifdef RK_DENOISE_ENABLE
|
|
in->mDenioseState = NULL;
|
|
#endif
|
|
in->dev = adev;
|
|
in->standby = true;
|
|
in->requested_rate = config->sample_rate;
|
|
in->input_source = AUDIO_SOURCE_DEFAULT;
|
|
in->device = devices;
|
|
in->handle = handle;
|
|
in->io_handle = handle;
|
|
in->channel_mask = config->channel_mask;
|
|
in->resampler = NULL;
|
|
|
|
if (in->device == AUDIO_DEVICE_IN_HDMI) {
|
|
ALOGD("HDMI-In: use low latency");
|
|
flags |= AUDIO_INPUT_FLAG_FAST;
|
|
}
|
|
in->flags = flags;
|
|
struct pcm_config *pcm_config = flags & AUDIO_INPUT_FLAG_FAST ?
|
|
&pcm_config_in_low_latency : &pcm_config_in;
|
|
|
|
if (in->device == AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
|
|
pcm_config = &pcm_config_in_bt;
|
|
pcm_config->rate = adev->bt_wb_speech_enabled? 16000:8000;
|
|
}
|
|
|
|
in->config = pcm_config;
|
|
|
|
in->buffer = malloc(pcm_config->period_size * pcm_config->channels
|
|
* audio_stream_in_frame_size(&in->stream));
|
|
if (!in->buffer) {
|
|
ret = -ENOMEM;
|
|
goto err_malloc;
|
|
}
|
|
|
|
if (in->device == AUDIO_DEVICE_IN_HDMI) {
|
|
goto out;
|
|
}
|
|
|
|
#ifdef AUDIO_3A
|
|
ALOGD("voice process has opened, try to create voice process!");
|
|
adev->voice_api = rk_voiceprocess_create(DEFAULT_PLAYBACK_SAMPLERATE,
|
|
DEFAULT_PLAYBACK_CHANNELS,
|
|
in->requested_rate,
|
|
audio_channel_count_from_in_mask(in->channel_mask));
|
|
if (adev->voice_api == NULL) {
|
|
ALOGE("crate voice process failed!");
|
|
}
|
|
#endif
|
|
|
|
out:
|
|
adev_add_stream_to_list(in->dev, &in->dev->input_stream_list, &in->list_node);
|
|
*stream_in = &in->stream;
|
|
return 0;
|
|
err_resampler:
|
|
free(in->buffer);
|
|
err_malloc:
|
|
free(in);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_close_input_stream
|
|
*
|
|
* @param dev
|
|
* @param stream
|
|
*/
|
|
static void adev_close_input_stream(struct audio_hw_device *dev,
|
|
struct audio_stream_in *stream)
|
|
{
|
|
struct stream_in *in = (struct stream_in *)stream;
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
|
|
ALOGD("%s",__FUNCTION__);
|
|
|
|
device_lock(in->dev);
|
|
list_remove(&in->list_node);
|
|
device_unlock(in->dev);
|
|
|
|
in_standby(&stream->common);
|
|
if (in->resampler) {
|
|
release_resampler(in->resampler);
|
|
in->resampler = NULL;
|
|
}
|
|
|
|
#ifdef ALSA_IN_DEBUG
|
|
fclose(in_debug);
|
|
#endif
|
|
#ifdef AUDIO_3A
|
|
if (adev->voice_api != NULL) {
|
|
rk_voiceprocess_destory();
|
|
adev->voice_api = NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef RK_DENOISE_ENABLE
|
|
if (in->mDenioseState)
|
|
rkdenoise_destroy(in->mDenioseState);
|
|
in->mDenioseState = NULL;
|
|
#endif
|
|
free(in->buffer);
|
|
free(stream);
|
|
}
|
|
|
|
/**
|
|
* @brief adev_dump
|
|
*
|
|
* @param device
|
|
* @param fd
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_dump(const audio_hw_device_t *device, int fd)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief adev_close
|
|
*
|
|
* @param device
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_close(hw_device_t *device)
|
|
{
|
|
struct audio_device *adev = (struct audio_device *)device;
|
|
|
|
//audio_route_free(adev->ar);
|
|
|
|
|
|
route_uninit();
|
|
|
|
free(device);
|
|
return 0;
|
|
}
|
|
|
|
static int adev_create_audio_patch(struct audio_hw_device *dev,
|
|
unsigned int num_sources,
|
|
const struct audio_port_config *sources,
|
|
unsigned int num_sinks,
|
|
const struct audio_port_config *sinks,
|
|
audio_patch_handle_t *handle)
|
|
{
|
|
if (num_sources != 1 || num_sinks == 0 || num_sinks > AUDIO_PATCH_PORTS_MAX) {
|
|
ALOGE("%s unsupport num sources:%d or sinks:%d", __func__, num_sources, num_sinks);
|
|
return -EINVAL;
|
|
}
|
|
ALOGD("%s num_sources:%d, num_sinks:%d, %s(%x)->%s(%x), handle:%p"
|
|
, __func__, num_sources, num_sinks
|
|
, (sources[0].type == AUDIO_PORT_TYPE_MIX) ? "mix" : "device"
|
|
, (sources[0].type == AUDIO_PORT_TYPE_MIX) ?
|
|
sources[0].ext.mix.handle : sources[0].ext.device.type
|
|
, (sinks[0].type == AUDIO_PORT_TYPE_MIX) ? "mix" : "device"
|
|
, (sinks[0].type == AUDIO_PORT_TYPE_MIX) ?
|
|
sinks[0].ext.mix.handle : sinks[0].ext.device.type
|
|
, handle);
|
|
struct audio_device *adev = (struct audio_device *)dev;
|
|
const struct audio_port_config *src_port_config = sources;
|
|
const struct audio_port_config *sink_port_config = sinks;
|
|
bool generatedPatchHandle = false;
|
|
if (*handle == AUDIO_PATCH_HANDLE_NONE) {
|
|
*handle = ++adev->next_patch_handle;
|
|
generatedPatchHandle = true;
|
|
}
|
|
|
|
struct stream_in *in = NULL;
|
|
struct stream_out *out = NULL;
|
|
audio_io_handle_t io_handle = 0;
|
|
audio_patch_handle_t *patch_handle = NULL;
|
|
bool wasStandby = true;
|
|
int ret = 0;
|
|
/**
|
|
* 1. device --> mix (recording)
|
|
* 2. mix --> device (playback)
|
|
*/
|
|
if (sources[0].type == AUDIO_PORT_TYPE_DEVICE
|
|
&& sinks[0].type == AUDIO_PORT_TYPE_MIX) {
|
|
if (num_sinks != 1) {
|
|
return -EINVAL;
|
|
}
|
|
ALOGD("%s recording", __func__);
|
|
device_lock(adev);
|
|
in = adev_get_stream_in_by_io_handle_l(adev, sinks[0].ext.mix.handle);
|
|
if (in == NULL) {
|
|
ALOGE("%s()can not find stream with handle(%d)", __func__, sinks[0].ext.mix.handle);
|
|
device_unlock(adev);
|
|
return -EINVAL;
|
|
}
|
|
io_handle = in->handle;
|
|
patch_handle = &in->patch_handle;
|
|
wasStandby = in->standby;
|
|
if (!generatedPatchHandle && *patch_handle != *handle) {
|
|
ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream "
|
|
"with handle(%d) when creating audio patch",
|
|
__func__, *handle, *patch_handle, io_handle);
|
|
device_unlock(adev);
|
|
return -EINVAL;
|
|
}
|
|
in->device = sources[0].ext.device.type;
|
|
device_unlock(adev);
|
|
pthread_mutex_lock(&in->lock);
|
|
device_lock(adev);
|
|
do_in_standby(in);
|
|
device_unlock(adev);
|
|
*patch_handle = *handle;
|
|
if (!wasStandby) {
|
|
device_lock(adev);
|
|
if (in != NULL) {
|
|
ret = start_input_stream(in);
|
|
if (ret < 0) {
|
|
ALOGE("%s() start_input_stream fail", __func__);
|
|
device_unlock(adev);
|
|
pthread_mutex_unlock(&in->lock);
|
|
return -EINVAL;
|
|
}
|
|
in->standby = false;
|
|
}
|
|
device_unlock(adev);
|
|
}
|
|
pthread_mutex_unlock(&in->lock);
|
|
} else if (sources[0].type == AUDIO_PORT_TYPE_MIX
|
|
&& sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
|
|
for (unsigned int i = 0; i < num_sinks; i++) {
|
|
if (sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
|
|
ALOGE("%s() invalid sink type %#x for mix source", __func__, sinks[i].type);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
ALOGD("%s playback address:%s", __func__, sinks[0].ext.device.address);
|
|
device_lock(adev);
|
|
out = adev_get_stream_out_by_io_handle_l(adev, sources[0].ext.mix.handle);
|
|
if (out == NULL) {
|
|
ALOGE("%s()can not find stream with handle(%d)", __func__, sinks[0].ext.mix.handle);
|
|
device_unlock(adev);
|
|
return -EINVAL;
|
|
}
|
|
io_handle = out->handle;
|
|
patch_handle = &out->patch_handle;
|
|
wasStandby = out->standby;
|
|
if (!generatedPatchHandle && *patch_handle != *handle) {
|
|
ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream "
|
|
"with handle(%d) when creating audio patch",
|
|
__func__, *handle, *patch_handle, io_handle);
|
|
device_unlock(adev);
|
|
return -EINVAL;
|
|
}
|
|
out->num_configs = num_sinks;
|
|
for (unsigned int i = 0; i < num_sinks; ++i) {
|
|
out->devices[i] = sinks[i].ext.device.type;
|
|
}
|
|
device_unlock(adev);
|
|
|
|
lock_all_outputs(adev);
|
|
do_out_standby(out);
|
|
unlock_all_outputs(adev, NULL);
|
|
*patch_handle = *handle;
|
|
if (!wasStandby) {
|
|
lock_all_outputs(adev);
|
|
if (in != NULL) {
|
|
ret = start_output_stream(out);
|
|
if (ret < 0) {
|
|
ALOGE("%s() start_output_stream fail", __func__);
|
|
unlock_all_outputs(adev, NULL);
|
|
return -EINVAL;
|
|
}
|
|
out->standby = false;
|
|
}
|
|
unlock_all_outputs(adev, NULL);
|
|
}
|
|
} else {
|
|
// All other cases are invalid.
|
|
ALOGE("%s() invalid case", __func__);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int adev_release_audio_patch(struct audio_hw_device *dev,
|
|
audio_patch_handle_t handle)
|
|
{
|
|
struct audio_device* adev = (struct audio_device*) dev;
|
|
ALOGD("%s in", __func__);
|
|
device_lock(adev);
|
|
struct stream_out *out = adev_get_stream_out_by_patch_handle_l(adev, handle);
|
|
device_unlock(adev);
|
|
if (out != NULL) {
|
|
lock_all_outputs(adev);
|
|
do_out_standby(out);
|
|
out->patch_handle = AUDIO_PATCH_HANDLE_NONE;
|
|
unlock_all_outputs(adev, NULL);
|
|
return 0;
|
|
}
|
|
device_lock(adev);
|
|
struct stream_in *in = adev_get_stream_in_by_patch_handle_l(adev, handle);
|
|
device_unlock(adev);
|
|
if (in != NULL) {
|
|
pthread_mutex_lock(&in->lock);
|
|
lock_all_outputs(adev);
|
|
do_in_standby(in);
|
|
unlock_all_outputs(adev, NULL);
|
|
in->patch_handle = AUDIO_PATCH_HANDLE_NONE;
|
|
pthread_mutex_unlock(&in->lock);
|
|
return 0;
|
|
}
|
|
ALOGE("%s cannot find stream with patch handle as %d", __func__, handle);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int adev_get_audio_port(struct audio_hw_device *dev,
|
|
struct audio_port *port)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int adev_set_audio_port_config(struct audio_hw_device *dev,
|
|
const struct audio_port_config *config)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void adev_open_init(struct audio_device *adev)
|
|
{
|
|
ALOGD("%s",__func__);
|
|
int i = 0;
|
|
adev->mic_mute = false;
|
|
adev->screenOff = false;
|
|
|
|
#ifdef AUDIO_3A
|
|
adev->voice_api = NULL;
|
|
#endif
|
|
|
|
adev->input_source = AUDIO_SOURCE_DEFAULT;
|
|
|
|
for(i =0; i < OUTPUT_TOTAL; i++){
|
|
adev->outputs[i] = NULL;
|
|
}
|
|
set_default_dev_info(adev->dev_out, SND_OUT_SOUND_CARD_MAX, 1);
|
|
set_default_dev_info(adev->dev_in, SND_IN_SOUND_CARD_MAX, 1);
|
|
adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].id = "SPEAKER";
|
|
adev->dev_out[SND_OUT_SOUND_CARD_HDMI].id = "HDMI";
|
|
adev->dev_out[SND_OUT_SOUND_CARD_HDMI_1].id = "HDMI_1";
|
|
adev->dev_out[SND_OUT_SOUND_CARD_SPDIF].id = "SPDIF";
|
|
adev->dev_out[SND_OUT_SOUND_CARD_SPDIF_1].id = "SPDIF_1";
|
|
adev->dev_out[SND_OUT_SOUND_CARD_BT].id = "BT";
|
|
adev->dev_in[SND_IN_SOUND_CARD_MIC].id = "MIC";
|
|
adev->dev_in[SND_IN_SOUND_CARD_BT].id = "BT";
|
|
adev->owner[0] = NULL;
|
|
adev->owner[1] = NULL;
|
|
|
|
char value[PROPERTY_VALUE_MAX];
|
|
if (property_get("vendor.audio.period_size", value, NULL) > 0) {
|
|
pcm_config.period_size = atoi(value);
|
|
pcm_config_in.period_size = pcm_config.period_size;
|
|
}
|
|
if (property_get("vendor.audio.in_period_size", value, NULL) > 0)
|
|
pcm_config_in.period_size = atoi(value);
|
|
}
|
|
|
|
/**
|
|
* @brief adev_open
|
|
*
|
|
* @param module
|
|
* @param name
|
|
* @param device
|
|
*
|
|
* @returns
|
|
*/
|
|
static int adev_open(const hw_module_t* module, const char* name,
|
|
hw_device_t** device)
|
|
{
|
|
struct audio_device *adev;
|
|
int ret;
|
|
|
|
ALOGD(AUDIO_HAL_VERSION);
|
|
|
|
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
|
|
return -EINVAL;
|
|
|
|
adev = calloc(1, sizeof(struct audio_device));
|
|
if (!adev)
|
|
return -ENOMEM;
|
|
|
|
list_init(&adev->output_stream_list);
|
|
list_init(&adev->input_stream_list);
|
|
|
|
adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
|
|
adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_3_0;
|
|
adev->hw_device.common.module = (struct hw_module_t *) module;
|
|
adev->hw_device.common.close = adev_close;
|
|
|
|
adev->hw_device.init_check = adev_init_check;
|
|
adev->hw_device.set_voice_volume = adev_set_voice_volume;
|
|
adev->hw_device.set_master_volume = adev_set_master_volume;
|
|
adev->hw_device.set_mode = adev_set_mode;
|
|
adev->hw_device.set_mic_mute = adev_set_mic_mute;
|
|
adev->hw_device.get_mic_mute = adev_get_mic_mute;
|
|
adev->hw_device.set_parameters = adev_set_parameters;
|
|
adev->hw_device.get_parameters = adev_get_parameters;
|
|
adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size;
|
|
adev->hw_device.open_output_stream = adev_open_output_stream;
|
|
adev->hw_device.close_output_stream = adev_close_output_stream;
|
|
adev->hw_device.open_input_stream = adev_open_input_stream;
|
|
adev->hw_device.close_input_stream = adev_close_input_stream;
|
|
adev->hw_device.dump = adev_dump;
|
|
adev->hw_device.get_microphones = adev_get_microphones;
|
|
//add for version 3.0
|
|
adev->hw_device.create_audio_patch = adev_create_audio_patch;
|
|
adev->hw_device.release_audio_patch = adev_release_audio_patch;
|
|
adev->hw_device.get_audio_port = adev_get_audio_port;
|
|
adev->hw_device.set_audio_port_config = adev_set_audio_port_config;
|
|
//adev->ar = audio_route_init(MIXER_CARD, NULL);
|
|
//route_init();
|
|
/* adev->cur_route_id initial value is 0 and such that first device
|
|
* selection is always applied by select_devices() */
|
|
*device = &adev->hw_device.common;
|
|
|
|
adev->bt_wb_speech_enabled = false;
|
|
adev->bt_sco_reroute = 0;
|
|
|
|
adev_open_init(adev);
|
|
return 0;
|
|
}
|
|
|
|
static struct hw_module_methods_t hal_module_methods = {
|
|
.open = adev_open,
|
|
};
|
|
|
|
struct audio_module HAL_MODULE_INFO_SYM = {
|
|
.common = {
|
|
.tag = HARDWARE_MODULE_TAG,
|
|
.module_api_version = AUDIO_MODULE_API_VERSION_0_1,
|
|
.hal_api_version = HARDWARE_HAL_API_VERSION,
|
|
.id = AUDIO_HARDWARE_MODULE_ID,
|
|
.name = "Manta audio HW HAL",
|
|
.author = "The Android Open Source Project",
|
|
.methods = &hal_module_methods,
|
|
},
|
|
};
|