📄 recorder.c
字号:
/*File Name: Recorder.c*/
#include <fcntl.h>
#include <linux/soundcard.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include "Ldb.h"
#include "Recorder.h"
#include "Adpcm.h"
#include "Utility.h"
#include "LocoSystem.h"
#include "SoundDesc.h"
#include "ComMonitor.h"
#include "LocoLog.h"
#define ADPCM_COMPRESSION 1
/********************************************/
#define SAMPLE_BIT 16
#define SAMPLE_CHANNEL 1
int SAMPLE_RATE;
int SAMPLE_PER_BLOCK;
int BLOCK_ALIGN;
int SAMPLE_BUFFER_SIZE;
int CODE_BUFFER_SIZE;
/********************************************
读写的文件及buffer
********************************************/
/*设备fd*/
int f_audiofd;
/*当前正在录音的文件*/
FILE *f_pCurrFile;
char f_prevFileName[SYS_PATH_MAX_LEN];
char f_currFileName[SYS_PATH_MAX_LEN];
/*当前录音文件得长度*/
int f_rcdSize;
/*读写文件buffer*/
unsigned char* f_bufSample;
unsigned char* f_bufCode;
/********************************************
录音线程
********************************************/
/*录音线程*/
pthread_t f_rcdThrd;
/*录音停止标志*/
volatile char f_rcdStop;
pthread_mutex_t f_pauseMutex = PTHREAD_MUTEX_INITIALIZER;
#define pause_Lock {pthread_mutex_lock(&f_pauseMutex);}
#define pause_Unlock {pthread_mutex_unlock(&f_pauseMutex);}
/*录音暂停标志*/
volatile char f_rcdPause;
volatile char f_forceStop;
/*录音线程函数*/
void *loco_recorder(void *arg);
void loco_rcdPlayFile(FILE *pfile);
void loco_rcdStop();
int loco_rcdStart();
/*******************************************
读写方式打开声音设备
mode: O_WRONLY, O_RDONLY, O_RDWR
*******************************************/
int loco_openAudioDev(int mode)
{
int status, arg;
f_audiofd = open(LOCO_SOUND_DEV, mode);
if(f_audiofd < 0)
{
LDB_ERR_MSGF("open audio device failed!");
return -1;
}
arg = AFMT_U16_BE;
status= ioctl(f_audiofd, SNDCTL_DSP_SETFMT, &arg);
if(status == -1)
{
LDB_ERR_MSGF("AFMT_U16_BE error!");
return -1;
}
else if(arg != AFMT_U16_BE)
{
LDB_ERR_MSGF("Failed to set sample format %d! arg = %d", AFMT_U16_BE, arg);
return -1;
}
else
{
//printf("Set sample bits sucessfully\r\n");
}
arg = SAMPLE_BIT;
status= ioctl(f_audiofd, SNDCTL_DSP_SAMPLESIZE, &arg);
if(status == -1)
{
LDB_ERR_MSGF("SNDCTL_DSP_SAMPLESIZE error!");
return -1;
}
else if(arg != SAMPLE_BIT)
{
LDB_ERR_MSGF("Failed to set sample bits!arg = %d", arg);
return -1;
}
else
{
//printf("Set sample bits sucessfully\r\n");
}
arg = SAMPLE_CHANNEL;
status= ioctl(f_audiofd, SNDCTL_DSP_CHANNELS, &arg);
if(status == -1)
{
LDB_ERR_MSGF("SNDCTL_DSP_CHANNELS error!");
return -1;
}
else if(arg != SAMPLE_CHANNEL)
{
LDB_ERR_MSGF("unable to set numble of channels!arg = %d",arg);
return -1;
}
else
{
//printf("Set channels sucessfully\r\n");
}
arg = SAMPLE_RATE;
status= ioctl(f_audiofd, SNDCTL_DSP_SPEED, &arg);
if(status == -1)
{
LDB_ERR_MSGF("SNDCTL_DSP_SPEED error!");
return -1;
}
else if(arg != SAMPLE_RATE)
{
LDB_ERR_MSGF("unable to set rate:%d! arg = %d",SAMPLE_RATE,arg);
return -1;
}
else
{
//printf("Set sample rate sucessfully\r\n");
}
return f_audiofd;
}
int loco_dspRead(unsigned char *buffer, int size)
{
int nread;
if (f_audiofd == -1)
{
return -1;
}
nread = read(f_audiofd, buffer, size);
return nread;
}
int loco_dspWrite(unsigned char *buffer, int size)
{
int nwrite;
if (f_audiofd == -1)
{
return -1;
}
nwrite = write(f_audiofd, buffer, size);
return nwrite;
}
void loco_dspClose()
{
if (f_audiofd != -1)
{
close(f_audiofd);
f_audiofd = -1;
}
printf("dsp closed \n");
}
/*******************************************/
int loco_rcdInit()
{
if (g_sysSampleRate == 8)
{
SAMPLE_RATE = 8000;
SAMPLE_PER_BLOCK = 505;
BLOCK_ALIGN = 256;
SAMPLE_BUFFER_SIZE = 2020;
CODE_BUFFER_SIZE = 512;
}
else
{
SAMPLE_RATE = 16000;
SAMPLE_PER_BLOCK = 1017;
BLOCK_ALIGN = 512;
SAMPLE_BUFFER_SIZE = 4068;
CODE_BUFFER_SIZE = 1024;
}
f_forceStop = 0;
/*分配工作buffer*/
f_bufSample = (unsigned char*)malloc(SAMPLE_BUFFER_SIZE);
f_bufCode = (unsigned char*)malloc(CODE_BUFFER_SIZE);
if (NULL == f_bufSample || NULL == f_bufCode)
return -1;
return loco_rcdStart();
}
void loco_rcdFree()
{
loco_rcdStop();
free(f_bufSample);
f_bufSample = NULL;
free(f_bufCode);
f_bufCode = NULL;
f_forceStop = 0;
}
int loco_rcdStart()
{
f_pCurrFile = NULL;
if (g_rcdAuto == 1)
f_rcdPause = 0;
else
f_rcdPause = 1;
f_audiofd = -1;
/*创建语音保存文件夹*/
if (ldb_fileCreateDir(SYS_SOUND_SAVE_DIR) == -1)
{
LDB_ERR_MSGF("can not create recorder directory");
return -1;
}
/*打开声音设备*/
if (-1 == loco_openAudioDev(O_RDONLY))//O_RDWR
{
LDB_ERR_MSGF("unable to open audio device: %s!", LOCO_SOUND_DEV);
loco_rcdFree();
return -1;
}
/*创建录音工作线程*/
f_rcdStop = 0;
if( pthread_create(&f_rcdThrd, NULL, loco_recorder, NULL) != 0)
{
LDB_ERR_MSGF("can not create recorder thread");
return -1;
}
return 0;
}
//在开始播放的时候将录音设备关闭,然后以Writer only方式打开
void loco_rcdStop()
{
/*停止线程*/
f_rcdStop = 1;
f_rcdPause = 0;
if (f_rcdThrd != -1)
{
if ( pthread_join(f_rcdThrd, NULL) != 0)
{
LDB_DEBUG_MSGF("recorder thread exits abnormally");
}
f_rcdThrd = -1;
}
/*关闭设备*/
loco_dspClose();
/*关闭录音文件*/
if (NULL != f_pCurrFile)
fclose(f_pCurrFile);
f_pCurrFile = NULL;
}
void loco_rcdPause()
{
pause_Lock
LDB_DEBUG_MSGF("Pause recording...");
f_rcdPause++;
pause_Unlock
}
void loco_rcdResume()
{
//如果已经停止,启动之
if(f_rcdStop == 1)
{
loco_rcdFree();
loco_rcdInit();
}
if (f_rcdPause == 0)
return;
loco_adpcmResetEncoder();
pause_Lock
f_rcdPause--;
LDB_DEBUG_MSGF("Resume recording...");
pause_Unlock
}
int loco_rcdEnsureSoundDir(S_SndDesc *pdesc, char *pdir)
{
char dir[SYS_PATH_MAX_LEN];
char date[9], train[6];
memset(date, 0, sizeof(date));
strncpy(date, pdesc->date_start, 8);
memset(train, 0, sizeof(train));
snd_descGetTrain(pdesc->train_id, train, sizeof(train));
memset(dir, 0, SYS_PATH_MAX_LEN);
sprintf(dir, "%s/%s.%05d.%s", SYS_SOUND_SAVE_DIR, train, pdesc->driver, date);
if ( strcmp(dir, pdir) == 0)
return 0;
memset(pdir, 0, SYS_PATH_MAX_LEN);
strcpy(pdir, dir);
if (-1 == ldb_fileCreateDir(pdir) )
return -1;
memset(dir, 0, SYS_PATH_MAX_LEN);
sprintf(dir, "save sound file to %s", pdir);
log_info(dir);
return 0;
}
//最小检查个数
#define CHECK_NUM_MIN 1024
int loco_rcdGetValue(int checknum)
{
int i, count, value;
short sample;
value = 0;
count = checknum/2;
for (i=0; i<count; )
{
sample = f_bufSample[i++] & 0xff;
sample |= (f_bufSample[i++] << 8) & 0xff00;
value += abs(sample);
}
value = value*2 / count;
return value;
}
void *loco_recorder(void *arg)
{
char b_pause, silence_count;
char dir[SYS_PATH_MAX_LEN];
char log[SYS_PATH_MAX_LEN];
int test_counter;
int nread, nblock, check_value;
S_SndDesc *pdesc;
int sample_size, sample_min_size;
sample_min_size = g_voiceMinLen*SAMPLE_RATE*2;
while(-1 != f_audiofd && f_rcdStop == 0)
{
loco_adpcmResetEncoder();
f_pCurrFile = NULL;
b_pause = 1;
test_counter = 0;
//语音检测暂停
while(b_pause == 1)
{
if (f_rcdStop == 1)
{
goto RECORDER_END;
}
nread = loco_dspRead(f_bufSample, SAMPLE_BUFFER_SIZE);
if (nread <= 0)
goto RECORDER_END;
if (nread < CHECK_NUM_MIN)
continue;
check_value = loco_rcdGetValue(nread);
#if 0
//#if _TEST
if (test_counter++ == 5)
{
printf("Before Recording: v=%d\n", check_value);
test_counter = 0;
}
//#endif
#endif
if (check_value > g_voiceMin)
{
memset(log, 0, sizeof(log));
sprintf(log, "Recording start for voice %d", check_value);
log_info(log);
printf(log);
printf("\n");
b_pause = 0;
}
}
sample_size = nread;
//通知串口单元开始录音了
loco_commReply(COMM_MSG_RESUME);
//得到当前的列车信息
pdesc = snd_descGet();
//确保录音目录存在
if (-1 == loco_rcdEnsureSoundDir(pdesc, dir) )
{
g_stopRequest = 1;
goto RECORDER_END;
}
//确保磁盘空间足够
if ( 0 != loco_ensureDiskSpace(dir))
{
g_stopRequest = 1;
goto RECORDER_END;
}
//创建一个新文件
memset(f_currFileName, 0, SYS_PATH_MAX_LEN);
f_pCurrFile = ldb_fileCreate(dir, pdesc->date_start, f_currFileName);
if (NULL == f_pCurrFile)
continue;
LDB_DEBUG_MSGF("Record a new file %s", f_currFileName);
//在文件头写入描述信息
fwrite(pdesc, 1, sizeof(S_SndDesc), f_pCurrFile);
//压缩
nblock = loco_adpcmEncode(f_bufSample, nread, SAMPLE_PER_BLOCK, BLOCK_ALIGN, f_bufCode, CODE_BUFFER_SIZE);
//写入文件
if (nblock > 0)
ldb_fileWrite(f_pCurrFile, f_bufCode, nblock*BLOCK_ALIGN);
//开始录音
silence_count = 0;
while(b_pause == 0 && f_rcdStop == 0)// && f_rcdPause == 0
{
memset(f_bufSample, 0, SAMPLE_BUFFER_SIZE);
memset(f_bufCode, 0, CODE_BUFFER_SIZE);
//读数据
nread = loco_dspRead(f_bufSample, SAMPLE_BUFFER_SIZE);
if (nread <= 0)
goto RECORDER_END;
#if 0
//#if _TEST
check_value = loco_rcdGetValue(nread);
memset(log, 0, sizeof(log));
sprintf(log, "In Recording: v=%d", check_value);
log_info(log);
printf(log);
printf("\n");
//#endif
#endif
//压缩
nblock = loco_adpcmEncode(f_bufSample, nread, SAMPLE_PER_BLOCK, BLOCK_ALIGN, f_bufCode, CODE_BUFFER_SIZE);
//写入文件
if (0 != ldb_fileWrite(f_pCurrFile, f_bufCode, nblock*BLOCK_ALIGN))
break;
sample_size += nread;
if (nread < CHECK_NUM_MIN)
continue;
//检查是否应该暂停
if (sample_size > sample_min_size || silence_count > 0)
{
check_value = loco_rcdGetValue(CHECK_NUM_MIN);
if (check_value <= g_voiceMin )
silence_count++;
else
{
sample_size = 0;
silence_count = 0;
}
if (silence_count > 5)
{
memset(log, 0, sizeof(log));
sprintf(log, "Recording stop for voice %d", check_value);
log_info(log);
printf(log);
printf("\n");
b_pause = 1;
}
}
}
//通知串口单元暂停录音了
loco_commReply(COMM_MSG_PAUSE);
fclose(f_pCurrFile);
f_pCurrFile = NULL;
memset(f_prevFileName, 0, SYS_PATH_MAX_LEN);
strcpy(f_prevFileName, f_currFileName);
LDB_DEBUG_MSGF("Close file %s", f_currFileName);
}
RECORDER_END:
LDB_DEBUG_MSGF("Exiting recorder thread...");
/*清理工作*/
loco_dspClose();
if (NULL != f_pCurrFile)
{
fclose(f_pCurrFile);
f_pCurrFile = NULL;
}
memset(f_prevFileName, 0, SYS_PATH_MAX_LEN);
strcpy(f_prevFileName, f_currFileName);
f_rcdStop = 1;
f_rcdThrd = -1;
return NULL;
}
int loco_rcdIsStop()
{
return f_rcdStop;
}
int loco_rcdIsForceStop()
{
return f_forceStop;
}
void loco_rcdSetForceStop()
{
f_forceStop = 1;
}
void loco_rcdClearForceStop()
{
f_forceStop = 0;
}
/*************************************************/
/*************************************************/
void loco_rcdPlayFile(FILE *pfile)
{
int nblock, ncode;
loco_adpcmResetDecoder();
/*打开声音设备*/
if (-1 == loco_openAudioDev(O_WRONLY) )
{
log_error("failed to open audio device!");
return;
}
do
{
memset(f_bufSample, 0, SAMPLE_BUFFER_SIZE);
memset(f_bufCode, 0, CODE_BUFFER_SIZE);
//读数据
ncode = fread(f_bufCode, 1, CODE_BUFFER_SIZE, pfile);
if (ncode == EOF || ncode <= 0)
break;
#if ADPCM_COMPRESSION
//解压缩
nblock = loco_adpcmDecode(f_bufCode, ncode, BLOCK_ALIGN, f_bufSample, SAMPLE_BUFFER_SIZE, SAMPLE_PER_BLOCK);
//写到设备
if (0 > loco_dspWrite(f_bufSample, nblock*2*SAMPLE_PER_BLOCK))
break;
#else
if (0 > write(f_audiofd, f_bufCode, ncode))
break;
#endif
}while(1);
loco_dspClose();
}
void loco_audioPrompt(const char *apid)
{
FILE *pfile;
char path[SYS_PATH_MAX_LEN];
memset(path, 0, sizeof(path));
if (f_forceStop == 1)
{
return;
}
loco_rcdSetForceStop();
//播放的时候,暂停录音
loco_rcdStop();
sleep(1);
if (g_sysSampleRate == 16)
{
sprintf(path, "%s%d", apid, g_sysSampleRate);
if (ldb_fileExists(path) == 0)
goto AUDIO_PROMPT_END;
pfile = fopen(path, "rb");
}
else
{
if (ldb_fileExists(apid) == 0)
goto AUDIO_PROMPT_END;
pfile = fopen(apid, "rb");
}
if (pfile != NULL)
{
loco_rcdPlayFile(pfile);
fclose(pfile);
pfile = NULL;
}
AUDIO_PROMPT_END:
loco_rcdStart();
loco_rcdClearForceStop();
}
int loco_testerStart()
{
FILE *pfile;
S_SndDesc tmpDesc;
if (f_forceStop == 1)
{
return 0;
}
loco_rcdSetForceStop();
//停止录音
loco_rcdStop();
sleep(1);
if (ldb_fileExists(f_prevFileName) == 0)
{
//继续录音
loco_rcdStart();
loco_rcdClearForceStop();
return -1;
}
pfile = fopen(f_prevFileName, "rb");
if (pfile == NULL)
{
//继续录音
loco_rcdStart();
loco_rcdClearForceStop();
return -1;
}
fread(&tmpDesc, 1, sizeof(tmpDesc), pfile);
loco_rcdPlayFile(pfile);
fclose(pfile);
pfile = NULL;
sleep(1);
loco_rcdStart();
loco_rcdClearForceStop();
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -