|
@@ -1,4 +1,3 @@
|
|
|
-#include "precompile.h"
|
|
|
#include "audiomicspklinux.h"
|
|
|
#include "audiocontext.h"
|
|
|
#include "audiolog.h"
|
|
@@ -7,8 +6,11 @@
|
|
|
#include <portaudio.h>
|
|
|
#include <assert.h>
|
|
|
#include <speex/speex_resampler.h>
|
|
|
+#include <time.h>
|
|
|
+
|
|
|
|
|
|
#define MAX_DELAY 60
|
|
|
+#define CLOCK_PERIOD 20
|
|
|
|
|
|
static int get_device_index(int indev, const char* key)
|
|
|
{
|
|
@@ -39,20 +41,21 @@ static int outStreamCallback(const void* input,
|
|
|
apr_status_t status;
|
|
|
|
|
|
if (output) {
|
|
|
- unsigned nsamples_req = frameCount;
|
|
|
- if (micspk->ply_buf_cnt == 0 && nsamples_req == micspk->capture_frame_samples) {
|
|
|
- SHORT data[160];
|
|
|
- spx_uint32_t in_len = micspk->frame_samples;
|
|
|
- spx_uint32_t out_len = micspk->capture_frame_samples;
|
|
|
- delay_buf_get((delay_buf*)micspk->ply_dbuf, data);
|
|
|
- if (micspk->user_data)
|
|
|
- {
|
|
|
- micspk->on_rx_audio((char*)data, micspk->user_data);
|
|
|
- }
|
|
|
- //speex_resampler_process_int((SpeexResamplerState*)micspk->output_resample_state, 0,
|
|
|
- // data, &in_len, (spx_int16_t*)output, &out_len);
|
|
|
- memcpy(output, data, in_len*sizeof(float));
|
|
|
- }
|
|
|
+ //unsigned nsamples_req = frameCount;
|
|
|
+ //if (micspk->ply_buf_cnt == 0 && nsamples_req == micspk->capture_frame_samples) {
|
|
|
+ // SHORT data[160];
|
|
|
+ // spx_uint32_t in_len = micspk->frame_samples;
|
|
|
+ // spx_uint32_t out_len = micspk->capture_frame_samples;
|
|
|
+ // delay_buf_get((delay_buf*)micspk->ply_dbuf, data);
|
|
|
+ // if (micspk->user_data)
|
|
|
+ // {
|
|
|
+ // micspk->on_rx_audio((char*)data, micspk->user_data);
|
|
|
+ // }
|
|
|
+ // //speex_resampler_process_int((SpeexResamplerState*)micspk->output_resample_state, 0,
|
|
|
+ // // data, &in_len, (spx_int16_t*)output, &out_len);
|
|
|
+ // memcpy(output, data, in_len*sizeof(float));
|
|
|
+ //}
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "audio speaker outStreamCallback.");
|
|
|
}
|
|
|
return paContinue;
|
|
|
}
|
|
@@ -74,19 +77,20 @@ static int inStreamCallback(const void* input,
|
|
|
}
|
|
|
else {
|
|
|
unsigned long nsamples = frameCount + micspk->rec_buf_cnt;
|
|
|
- while (nsamples >= micspk->frame_samples) {
|
|
|
- unsigned chunk_count = micspk->frame_samples - micspk->rec_buf_cnt;
|
|
|
- memcpy(micspk->rec_buf + micspk->rec_buf_cnt, input, chunk_count << 1);
|
|
|
- input = (const short*)input + chunk_count;
|
|
|
- delay_buf_put(micspk->rec_dbuf, micspk->rec_buf);
|
|
|
- micspk->rec_buf_cnt = 0;
|
|
|
- nsamples -= micspk->frame_samples;
|
|
|
- }
|
|
|
- if (nsamples > 0) {
|
|
|
- memcpy(micspk->rec_buf + micspk->rec_buf_cnt, input, nsamples << 1);
|
|
|
- micspk->rec_buf_cnt += nsamples;
|
|
|
- }
|
|
|
+ //while (nsamples >= micspk->frame_samples) {
|
|
|
+ // unsigned chunk_count = micspk->frame_samples - micspk->rec_buf_cnt;
|
|
|
+ // memcpy(micspk->rec_buf + micspk->rec_buf_cnt, input, chunk_count << 1);
|
|
|
+ // input = (const short*)input + chunk_count;
|
|
|
+ // delay_buf_put(micspk->rec_dbuf, micspk->rec_buf);
|
|
|
+ // micspk->rec_buf_cnt = 0;
|
|
|
+ // nsamples -= micspk->frame_samples;
|
|
|
+ //}
|
|
|
+ //if (nsamples > 0) {
|
|
|
+ // memcpy(micspk->rec_buf + micspk->rec_buf_cnt, input, nsamples << 1);
|
|
|
+ // micspk->rec_buf_cnt += nsamples;
|
|
|
+ //}
|
|
|
}
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "audio micro inStreamCallback.");
|
|
|
}
|
|
|
|
|
|
return paContinue;
|
|
@@ -125,6 +129,8 @@ static audiostream_vtbl_t g_stream_vtbl = {
|
|
|
|
|
|
void audiomicspklinux_destroy(audiomicspklinux_t* micspk)
|
|
|
{
|
|
|
+ sem_post(micspk->audio_device_started_sem);
|
|
|
+
|
|
|
if (micspk->ply_stream) {
|
|
|
Pa_AbortStream(micspk->ply_stream);
|
|
|
Pa_CloseStream(micspk->ply_stream);
|
|
@@ -142,6 +148,7 @@ void audiomicspklinux_destroy(audiomicspklinux_t* micspk)
|
|
|
if (micspk->opt & AMS_OPT_AS_ENGINE) {
|
|
|
//DeleteCriticalSection(&micspk->engine_lock);
|
|
|
}
|
|
|
+ sem_destroy(micspk->audio_device_started_sem);
|
|
|
}
|
|
|
|
|
|
static int initialize_speaker(audiomicspklinux_t* micspk)
|
|
@@ -170,7 +177,7 @@ static int initialize_speaker(audiomicspklinux_t* micspk)
|
|
|
}
|
|
|
audio_log_v(AUDIO_LOG_LEVEL_INFO, "audio micspk create success! audio output device defaultSampleRate is %f", info->defaultSampleRate);
|
|
|
|
|
|
- UINT32 capture_frame_samples = info->defaultSampleRate * FRAME_TIME / 1000;
|
|
|
+ uint32_t capture_frame_samples = info->defaultSampleRate * FRAME_TIME / 1000;
|
|
|
|
|
|
paError = Pa_OpenStream(&micspk->ply_stream, NULL, &outParam, info->defaultSampleRate, capture_frame_samples, paClipOff, &outStreamCallback, micspk);
|
|
|
if (paError != 0) {
|
|
@@ -196,8 +203,8 @@ static int initialize_speaker(audiomicspklinux_t* micspk)
|
|
|
static int initialize_micro(audiomicspklinux_t* micspk)
|
|
|
{
|
|
|
const PaDeviceInfo* info;
|
|
|
- PaStreamParameters inParam;
|
|
|
- PaError paError;
|
|
|
+ PaStreamParameters inParam = {0};
|
|
|
+
|
|
|
int micro_dev_id = micspk->rec_dev_id;
|
|
|
|
|
|
if (micro_dev_id == -1) {
|
|
@@ -207,30 +214,33 @@ static int initialize_micro(audiomicspklinux_t* micspk)
|
|
|
return APR_EGENERAL;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
info = Pa_GetDeviceInfo(micro_dev_id);
|
|
|
inParam.device = micro_dev_id;
|
|
|
inParam.channelCount = 1;
|
|
|
inParam.sampleFormat = paFloat32;
|
|
|
- inParam.suggestedLatency = info->defaultLowOutputLatency;
|
|
|
+ inParam.suggestedLatency = info->defaultLowInputLatency;
|
|
|
inParam.hostApiSpecificStreamInfo = NULL;
|
|
|
- if (Pa_IsFormatSupported(NULL, &inParam, info->defaultSampleRate) != paNoError) {
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "audio input device name is %s.", info->name);
|
|
|
+ PaError paError = Pa_IsFormatSupported(&inParam, NULL, info->defaultSampleRate);
|
|
|
+ if (paNoError != paError) {
|
|
|
audio_log_v(AUDIO_LOG_LEVEL_ERROR, "audio micro create error, cannot open audio input device.");
|
|
|
return APR_EGENERAL;
|
|
|
}
|
|
|
audio_log_v(AUDIO_LOG_LEVEL_INFO, "audio micspk create success, open audio input device success! and defaultSampleRate is %f.", info->defaultSampleRate);
|
|
|
|
|
|
- UINT32 capture_frame_samples = info->defaultSampleRate * FRAME_TIME / 1000;
|
|
|
+ uint32_t capture_frame_samples = info->defaultSampleRate * FRAME_TIME / 1000;
|
|
|
|
|
|
- paError = Pa_OpenStream(&micspk->rec_stream, NULL, &inParam, info->defaultSampleRate, capture_frame_samples, paClipOff, &inStreamCallback, micspk);
|
|
|
- if (paError != 0) {
|
|
|
- audio_log_v(AUDIO_LOG_LEVEL_ERROR, "audio micspk create error, Pa_OpenStream function failed in dir! ");
|
|
|
+ paError = Pa_OpenStream(&micspk->rec_stream, &inParam, NULL, info->defaultSampleRate, capture_frame_samples, paClipOff|paDitherOff, &inStreamCallback, micspk);
|
|
|
+ if (paNoError != paError) {
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_ERROR, "audio input Pa_OpenStream function failed for %s.", Pa_GetErrorText(paError));
|
|
|
return APR_EGENERAL;
|
|
|
}
|
|
|
audio_log_v(AUDIO_LOG_LEVEL_INFO, "audio micspk create success, audio input device Pa_OpenStream success!");
|
|
|
|
|
|
paError = Pa_StartStream(micspk->rec_stream);
|
|
|
if (paError != 0) {
|
|
|
- audio_log_v(AUDIO_LOG_LEVEL_ERROR, "Pa_StartStream function failed in dir! ");
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_ERROR, "Pa_StartStream function failed!");
|
|
|
Pa_CloseStream(micspk->rec_stream);
|
|
|
micspk->rec_stream = NULL;
|
|
|
return APR_EGENERAL;
|
|
@@ -271,12 +281,11 @@ static void uninitialize_micro(audiomicspklinux_t* micspk)
|
|
|
audio_log_v(AUDIO_LOG_LEVEL_INFO, "uninitialize_micro success!");
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-void* audiowork_proc(void* param)
|
|
|
+void* APR_THREAD_FUNC* audiowork_proc(apr_thread_t* threadhandle, void* param)
|
|
|
{
|
|
|
audiomicspklinux_t* micspk = (audiomicspklinux_t*)param;
|
|
|
int rc;
|
|
|
-
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d micspk addr is 0x%0x, current sem addr is 0x%0x. started flag is %s.", __FUNCTION__, __LINE__, param, micspk->audio_device_started_sem, micspk->baudio_device_started_flag ? "true" : "false");
|
|
|
//
|
|
|
// record need play because of AEC, so
|
|
|
// record <---> record and play
|
|
@@ -313,8 +322,36 @@ void* audiowork_proc(void* param)
|
|
|
audio_log_v(AUDIO_LOG_LEVEL_INFO, "play mode initialize speak louder param success!");
|
|
|
}
|
|
|
}
|
|
|
-on_error:
|
|
|
|
|
|
+ micspk->baudio_device_started_flag = true;
|
|
|
+
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d micspk addr is 0x%0x, current sem addr is 0x%0x.", __FUNCTION__, __LINE__, micspk, micspk->audio_device_started_sem);
|
|
|
+
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ struct timespec ts;
|
|
|
+ //int ivalue = -1;
|
|
|
+ clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
+ ts.tv_nsec += 1000 * 1000 * CLOCK_PERIOD;
|
|
|
+
|
|
|
+ //sem_getvalue(micspk->audio_device_started_sem, &ivalue);
|
|
|
+ //audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d current sem value is %d.", __FUNCTION__, __LINE__, ivalue);
|
|
|
+ if (-1 == sem_timedwait(micspk->audio_device_started_sem, &ts)) {
|
|
|
+ if (ETIMEDOUT == errno) {
|
|
|
+ if (micspk->opt & AMS_OPT_RECORD) {
|
|
|
+ //on_clock_rec(micspk);
|
|
|
+ }
|
|
|
+ else if (micspk->opt & AMS_OPT_PLAY) {
|
|
|
+ //on_clock_spk(micspk);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+on_error:
|
|
|
if (micspk->opt & AMS_OPT_RECORD) {
|
|
|
uninitialize_micro(micspk);
|
|
|
uninitialize_speaker(micspk);
|
|
@@ -352,6 +389,7 @@ apr_status_t audiomicspklinux_create(apr_pool_t* pool,
|
|
|
|
|
|
micspk = apr_palloc(pool, sizeof(audiomicspklinux_t));
|
|
|
memset(micspk, 0, sizeof(audiomicspklinux_t));
|
|
|
+ micspk->audio_device_started_sem = (sem_t*)apr_palloc(pool, sizeof(sem_t));
|
|
|
|
|
|
frame_samples = FRAME_TIME * clock / 1000;
|
|
|
capture_frame_samples = FRAME_TIME * clock / 1000;
|
|
@@ -368,23 +406,56 @@ apr_status_t audiomicspklinux_create(apr_pool_t* pool,
|
|
|
|
|
|
if (opt & AMS_OPT_PLAY) {
|
|
|
micspk->base.direction |= STREAM_DIR_WRITE;
|
|
|
- delay_buf_create(clock, frame_samples, 1, MAX_DELAY, 0, (delay_buf * *)& micspk->ply_dbuf);
|
|
|
+ delay_buf_create(clock, frame_samples, 1, MAX_DELAY, 0, (delay_buf**)& micspk->ply_dbuf);
|
|
|
micspk->ply_buf = (short*)apr_palloc(pool, frame_samples << 1);
|
|
|
micspk->ply_buf_cnt = 0;
|
|
|
//micspk->output_resample_state = speex_resampler_init(1, AUDIO_CLOCK, CAPTURE_AUDIO_CLOCK, RESAMPLE_QUALITY, NULL);
|
|
|
}
|
|
|
if (opt & AMS_OPT_RECORD) {
|
|
|
micspk->base.direction |= STREAM_DIR_READ;
|
|
|
- delay_buf_create(clock, frame_samples, 1, MAX_DELAY, 0, (delay_buf * *)& micspk->rec_dbuf);
|
|
|
+ delay_buf_create(clock, frame_samples, 1, MAX_DELAY, 0, (delay_buf**)& micspk->rec_dbuf);
|
|
|
//micspk->input_resample_state = speex_resampler_init(1, CAPTURE_AUDIO_CLOCK, AUDIO_CLOCK, RESAMPLE_QUALITY, NULL);
|
|
|
}
|
|
|
-
|
|
|
- int err = pthread_create(&micspk->audio_work_threadid, NULL, audiowork_proc, micspk);
|
|
|
- if (0 == err) {
|
|
|
+ micspk->baudio_device_started_flag = false;
|
|
|
+ sem_init(micspk->audio_device_started_sem, 0, 0);
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d micspk addr is 0x%0x, current sem addr is 0x%0x. started flag is %s.", __FUNCTION__, __LINE__, micspk, micspk->audio_device_started_sem, micspk->baudio_device_started_flag ? "true" : "false");
|
|
|
+ apr_status_t err = apr_thread_create(&micspk->audio_work_threadid, NULL, audiowork_proc, micspk, pool);
|
|
|
+ if (APR_SUCCESS == err) {
|
|
|
Dbg("create audiomicspk work thread success, %lu.", micspk->audio_work_threadid);
|
|
|
+ bool baudio_work_thread_exit = false;
|
|
|
+
|
|
|
+ do {
|
|
|
+ struct timespec ts;
|
|
|
+ int ivalue = -1;
|
|
|
+ clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
+ ts.tv_nsec += 1000 * 1000 * 10;
|
|
|
+ sem_getvalue(micspk->audio_device_started_sem, &ivalue);
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d current sem value is %d.", __FUNCTION__, __LINE__, ivalue);
|
|
|
+ if(-1 == sem_timedwait(micspk->audio_device_started_sem, &ts)) {
|
|
|
+ if (ETIMEDOUT == errno){
|
|
|
+ if (micspk->baudio_device_started_flag) {
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d audio device is already started.", __FUNCTION__, __LINE__);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d audio device work thread has exit.", __FUNCTION__, __LINE__);
|
|
|
+ baudio_work_thread_exit = true;
|
|
|
+ }
|
|
|
+ } while (!baudio_work_thread_exit);
|
|
|
+
|
|
|
+ if (baudio_work_thread_exit) {
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d", __FUNCTION__, __LINE__);
|
|
|
+ audiomicspklinux_destroy(micspk);
|
|
|
+ audio_log_v(AUDIO_LOG_LEVEL_INFO, "%s:%d", __FUNCTION__, __LINE__);
|
|
|
+ return APR_EGENERAL;
|
|
|
+ }
|
|
|
}
|
|
|
else {
|
|
|
Dbg("create audiomicspk work thread failed.");
|
|
|
+ audiomicspklinux_destroy(micspk);
|
|
|
+ return APR_EGENERAL;
|
|
|
}
|
|
|
|
|
|
*p_micspk = micspk;
|