blob: fffc5c51aa721962d4f3ab7452377c1fe9ee2f97 [file] [log] [blame]
/*
* Copyright (C) 2017 Amlogic Corporation.
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include "audio_if.h"
#include <IpcBuffer/IpcBuffer_c.h>
#include "hal_bt_capture.h"
#define NSEC_PER_SEC 1000000000L
#define CAP_SAMPLE_RATE 48000
#define CAP_SAMPLE_SIZE 4
#define CAP_DEFAULT_SIZE (10240*4)
#define CAP_DEFAULT_THRESHOLD (CAP_DEFAULT_SIZE / 4)
#define CAP_BUFFER_NAME "blu_capture_test"
typedef enum {
STATE_INIT = 0,
STATE_STARTED = 1,
STATE_STOPPED = 2
} cap_state_e;
struct AML_AudioCapture_Struct {
/* states & settings */
cap_state_e next_state;
cap_state_e cur_state;
AML_AudioCapture_Settings next_settings;
AML_AudioCapture_Settings cur_settings;
AML_AudioCapture_Status status;
/* working thread */
pthread_t thread;
/* protect current state and capture buffer */
pthread_mutex_t mutex;
pthread_cond_t condition;
bool exit;
/* temp working buffer */
unsigned char *data;
/* capture buffer interface */
struct audio_hw_device *dev;
unsigned char *cap_buf;
uint64_t rd_ptr;
uint64_t wr_ptr;
unsigned buffer_size;
};
typedef struct AML_AudioCapture_Struct *AML_AudioCaptureHandle;
/* protect singleton AML_AudioCapture_Struct for AML APIs */
static pthread_mutex_t rmf_lock = PTHREAD_MUTEX_INITIALIZER;
static struct AML_AudioCapture_Struct *control;
AML_AudioCapture_Settings settings; //operate setting
static inline uint64_t bytes_to_ns(unsigned bytes)
{
return (uint64_t)bytes * NSEC_PER_SEC / CAP_SAMPLE_SIZE / CAP_SAMPLE_RATE;
}
static inline unsigned int _buffer_level (
AML_AudioCaptureHandle handle
) {
return (handle->wr_ptr - handle->rd_ptr) % handle->buffer_size;
}
static inline bool _buffer_overflow (
AML_AudioCaptureHandle handle
) {
return (handle->wr_ptr - handle->rd_ptr) > handle->buffer_size;
}
static inline void _buffer_normalize (
AML_AudioCaptureHandle handle
) {
handle->rd_ptr = handle->wr_ptr - _buffer_level(handle);
}
static void* _AML_AudioCapture_Thread (
void *arg
) {
AML_AudioCaptureHandle handle = (AML_AudioCaptureHandle)arg;
struct timespec ts;
while (1) {
uint64_t n;
bool update_status = false;
pthread_mutex_lock(&control->mutex);
clock_gettime(CLOCK_MONOTONIC, &ts);
n = ts.tv_nsec + bytes_to_ns(control->cur_settings.threshold);
ts.tv_nsec = n % NSEC_PER_SEC;
ts.tv_sec += n / NSEC_PER_SEC;
pthread_cond_timedwait(&control->condition,&control->mutex, &ts);
/* state switching */
if (handle->cur_state != handle->next_state) {
char cmd[32] = {0};
if (handle->next_state == STATE_STARTED) {
handle->cur_settings = handle->next_settings;
handle->rd_ptr = 0;
handle->wr_ptr = 0;
handle->buffer_size = (handle->cur_settings.fifoSize == 0) ? CAP_DEFAULT_SIZE : handle->cur_settings.fifoSize;
handle->data = (unsigned char *)malloc(handle->buffer_size);
memset(handle->data,0,handle->buffer_size);
handle->status.started = 0;
handle->status.format = Format_e16BitStereo;
handle->status.samplingFreq = Freq_e48000;
snprintf(cmd, sizeof(cmd), "cap_buffer=" CAP_BUFFER_NAME ",%d", control->buffer_size);
handle->dev->set_parameters(handle->dev, cmd);
handle->cap_buf = IpcBuffer_get_ptr(CAP_BUFFER_NAME);
handle->status.started = 1;
update_status = true;
}
else if (handle->next_state == STATE_STOPPED) {
snprintf(cmd, sizeof(cmd), "cap_buffer=" CAP_BUFFER_NAME ",0");
control->dev->set_parameters(handle->dev, cmd);
handle->cap_buf = IpcBuffer_get_ptr(CAP_BUFFER_NAME);
printf("%s,%d:handle->cap_buf=%p\n",handle->cap_buf);
handle->status.started = 0;
if (handle->data) {
free(handle->data);
handle->data = NULL;
}
update_status = true;
}
handle->cur_state = handle->next_state;
}
pthread_mutex_unlock(&handle->mutex);
if (handle->exit) {
break;
}
if (handle->status.started ) {
handle->wr_ptr = IpcBuffer_get_wr_pos(CAP_BUFFER_NAME);
_buffer_normalize(handle);
if (handle->cap_buf && handle->data) {
long unsigned int len = _buffer_level(handle);//write point-read point % buf_size=can read
long unsigned int offset;
offset = handle->rd_ptr % handle->buffer_size;
if (len <= (handle->buffer_size - offset)) {
//memcpy(handle->data, handle->cap_buf + offset, len);
audio_dump_audio_bitstreams("/data/audio/capture.pcm",handle->cap_buf + offset,len);
} else {
unsigned copied = handle->buffer_size - offset;
audio_dump_audio_bitstreams("/data/audio/capture.pcm",handle->cap_buf + offset,copied);
audio_dump_audio_bitstreams("/data/audio/capture.pcm",handle->cap_buf,len - copied);
}
handle->rd_ptr += len;
}
}
}
return NULL;
}
int _AML_AudioCapture_Control_Init (
AML_AudioCaptureHandle handle
) {
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&control->condition, &attr);
pthread_mutex_init(&control->mutex, NULL);
if ( audio_hw_load_interface(&handle->dev) ) {
return fail;
}
if (handle->dev->init_check(handle->dev)) {
printf("device not inited, quit\n");
audio_hw_unload_interface(handle->dev);
return fail;
}
handle->cur_settings.threshold = CAP_DEFAULT_THRESHOLD;
handle->cur_state = STATE_INIT;
handle->next_state = STATE_INIT;
if (pthread_create(&handle->thread, NULL, _AML_AudioCapture_Thread, handle)) {
printf("pthread_create failed\n");
struct audio_hw_device *dev = handle->dev;
dev->common.close(&dev->common);
audio_hw_unload_interface(dev);
return fail;
}
return success;
}
struct AML_AudioCapture_Struct *
AML_AudioCapture_Open () {
pthread_mutex_lock(&rmf_lock);
if (control) {
pthread_mutex_unlock(&rmf_lock);
return NULL;
}
control = (struct AML_AudioCapture_Struct *)malloc(sizeof(struct AML_AudioCapture_Struct));
if (!control) {
pthread_mutex_unlock(&rmf_lock);
return NULL;
}
memset(control, 0, sizeof(struct AML_AudioCapture_Struct));
if (!_AML_AudioCapture_Control_Init(control)) {
pthread_mutex_unlock(&rmf_lock);
return NULL;
}
pthread_mutex_unlock(&rmf_lock);
return control;
}
int
AML_AudioCapture_GetDefaultSettings (
AML_AudioCapture_Settings* settings
) {
settings->fifoSize = CAP_DEFAULT_SIZE;
settings->threshold = CAP_DEFAULT_THRESHOLD;
settings->format = Format_e16BitStereo;
settings->samplingFreq = Freq_e48000;
return success;
}
int
AML_AudioCapture_Start (
AML_AudioCaptureHandle handle,
AML_AudioCapture_Settings* settings
) {
pthread_mutex_lock(&rmf_lock);
pthread_mutex_lock(&handle->mutex);
if (handle->cur_state == STATE_STARTED) {
pthread_mutex_unlock(&handle->mutex);
pthread_mutex_unlock(&rmf_lock);
return fail;
}
handle->next_settings = *settings;
handle->next_state = STATE_STARTED;
pthread_cond_signal(&handle->condition);
pthread_mutex_unlock(&handle->mutex);
pthread_mutex_unlock(&rmf_lock);
return success;
}
int
AML_AudioCapture_Stop_Locked (
AML_AudioCaptureHandle handle
) {
pthread_mutex_lock(&handle->mutex);
if (handle->cur_state != STATE_STARTED) {
pthread_mutex_unlock(&handle->mutex);
pthread_mutex_unlock(&rmf_lock);
return fail;
}
handle->next_state = STATE_STOPPED;
pthread_cond_signal(&handle->condition);
pthread_mutex_unlock(&handle->mutex);
return success;
}
int
AML_AudioCapture_Stop (
AML_AudioCaptureHandle handle
) {
int ret;
pthread_mutex_lock(&rmf_lock);
ret = AML_AudioCapture_Stop_Locked(handle);
pthread_mutex_unlock(&rmf_lock);
return ret;
}
int
AML_AudioCapture_Close (
AML_AudioCaptureHandle handle
) {
pthread_mutex_lock(&rmf_lock);
if (handle != control) {
pthread_mutex_unlock(&rmf_lock);
return fail;
}
AML_AudioCapture_Stop_Locked(handle);
pthread_mutex_lock(&handle->mutex);
handle->exit = true;
pthread_cond_signal(&handle->condition);
pthread_mutex_unlock(&handle->mutex);
/* avoid dead lock from RMF_AudioCapture_GetStatus triggered by status change call back */
pthread_mutex_unlock(&rmf_lock);
pthread_join(handle->thread, NULL);
pthread_mutex_lock(&rmf_lock);
if (handle->data) {
free(handle->data);
}
free(handle);
control = NULL;
handle->dev->common.close(&handle->dev->common);
audio_hw_unload_interface(handle->dev);
pthread_mutex_unlock(&rmf_lock);
return success;
}
int audio_dump_audio_bitstreams(const char *path, const void *buf, size_t bytes)
{
if (!path) {
return fail;
}
int flen;
FILE *fp = fopen(path, "ab+");
if (fp) {
flen = fwrite((char *)buf, 1, bytes, fp);
fclose(fp);
return flen;
}
printf("fail to open path=%s, errno=%d/%s", path, errno, strerror(errno));
return -1;
}
int main(int argc ,void **argv)
{
int len;
//get operation interface
AML_AudioCaptureHandle handle=NULL;
handle=(AML_AudioCaptureHandle) AML_AudioCapture_Open();
if (handle == NULL) {
printf("open error\n");
return fail;
}
//get_default_setting
if (!AML_AudioCapture_GetDefaultSettings(&settings)) {
printf("get default_setting error\n");
return fail;
}
//audio capture start
if (!AML_AudioCapture_Start(handle,&settings)) {
printf("AudioCapture Start error\n");
return fail;
}
printf("Audio Capturing .....\n");
while ( 1 );
//Sleep(300);
//AML_AudioCapture_Stop(handle);
//AML_AudioCapture_Close(handle);
return success;
}