/** * @file record.c * * * @brief 認識した入力音声をファイルに保存する. * * 入力された音声データを1つずつファイルに保存する. * ファイル名は録音時のタイムスタンプから "YYYY.MMDD.HHMMSS.wav" となる. * ファイル形式は Microsoft WAVE format, 16bit, PCM (無圧縮) である. * * 録音はいったんメモリに確保されずに、入力と平行してファイルに直接 * 書き込まれる。最初は一時ファイルに記録され、録音終了後(=第1パス * 終了後)に上記の形式のファイル名に変更される。 * * * * @brief Record speech inputs into successive files. * * These functions record each input data to the corresponding file with * file name of their time stamp in a format of "YYYY.MMDD.HHMMSS.wav". * The audio format is Microsoft WAVE, 16bit, PCM (no compression). * * The recording will not be stored in memory, instead it will be directly * recorded to a temporary file on the fly. After an end-of-sentence found * and the first pass ends, the temporary file will be moved to the * final filename descrived above. * * * @author Akinobu Lee * @date Tue Sep 06 14:13:54 2005 * * $Revision: 1.6 $ * */ /* * Copyright (c) 1991-2012 Kawahara Lab., Kyoto University * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology * Copyright (c) 2005-2012 Julius project team, Nagoya Institute of Technology * All rights reserved */ #include "app.h" #include static char *record_dirname = NULL; static char rectmpfilename[MAXLINELEN]; static char recordfilename[MAXLINELEN]; static int recordlen; static FILE *recfile_fp = NULL; static boolean open_error = FALSE; /** * * システム時間からベースファイル名を作成する. * * @param t [out] 結果を格納する文字列バッファ * @param maxlen [in] @a t の最大長 * * * Make base filename string from current system time. * * @param t [out] string buffer to hold the result string. * @param maxlen [in] the length of @a t. * */ static void timestring(char *t, int maxlen) { time_t timep; struct tm *lmtm; time(&timep); lmtm = localtime(&timep); snprintf ( t, maxlen,"%04d.%02d%02d.%02d%02d%02d", 1900+lmtm->tm_year, 1+lmtm->tm_mon, lmtm->tm_mday, lmtm->tm_hour, lmtm->tm_min, lmtm->tm_sec); } /** * * ベースファイル名から実際のパス名を作成する. ディレクトリは大域変数 * record_dirname であらかじめ指定されている. * * @param buf [out] 結果のパス名を格納するバッファへのポインタ * @param buflen [in] @a buf の最大長 * @param filename [in] ベースファイル名 * * * Make actual file path name from base file name. The recording directory * should be specified by the global variable "record_dirname". * * @param buf [out] buffer to hold the result string of this function * @param buflen [in] maximum length of @a buf. * @param filename [in] base filename. * */ static void make_record_filename(char *buf, int buflen, char *basename, char *dirname) { if (dirname == NULL) { fprintf(stderr, "no record directory specified??\n"); return; } snprintf(buf, buflen, #if defined(_WIN32) && !defined(__CYGWIN32__) "%s\\%s.wav" #else "%s/%s.wav" #endif , dirname, basename); } /** * * 一時ファイル名を作成する. * * @param buf [out] 結果のファイル名を格納するポインタ * @param buflen [in] @a buf の最大長 * * * Make temporary filename to store the incoming data while recording. * * @param buf [out] pointer of buffer to hold the resulting file name. * @param buflen [in] maximum length of @a buf. * */ static void make_tmp_filename(char *buf, int buflen, char *dirname) { #if defined(_WIN32) && !defined(__CYGWIN32__) snprintf(buf, buflen, "%s\\tmprecord.000", dirname); #else snprintf(buf, buflen, "%s/tmprecord.%d", dirname, getpid()); #endif } /** * * 録音のために一時ファイルをオープンする. * * * * Open temporary file for starting recording. * * */ static void record_sample_open(Recog *recog, void *dummy) { if (recfile_fp != NULL) { fprintf(stderr, "Error: record_sample_open: re-opened before closed!\n"); return; } make_tmp_filename(rectmpfilename, MAXLINELEN, record_dirname); if ((recfile_fp = wrwav_open(rectmpfilename, recog->jconf->input.sfreq)) == NULL) { perror("Error: record_sample_open"); fprintf(stderr, "failed to open \"%s\" (temporary record file)\n", rectmpfilename); open_error = TRUE; return; } recordlen = 0; } /** * * 入力音声断片をファイルに追加記録する. * * @param speech [in] 音声データのバッファ * @param samplenum [in] 音声データの長さ(サンプル数) * * * Append speech segment to file previously opened by record_sample_open(). * * @param speech [in] speech buffer * @param samplenum [in] length of above in samples * */ static void record_sample_write(Recog *recog, SP16 *speech, int samplenum, void *dummy) { static char tstr[20]; if (recfile_fp == NULL) { if (! open_error) record_sample_open(recog, dummy); } if (wrwav_data(recfile_fp, speech, samplenum) == FALSE) { perror("Error: record_sample_write"); fprintf(stderr, "failed to write samples to \"%s\"\n", rectmpfilename); return; } /* make timestamp of system time when an input begins */ /* the current temporal recording file will be renamed to this time-stamp filename */ if (recordlen == 0) { /* beginning of recording */ timestring(tstr, 18); } make_record_filename(recordfilename, MAXLINELEN, tstr, record_dirname); recordlen += samplenum; } /** * * 録音を終了する. 録音用の一時ファイルをクローズし、本来の名前にrenameする。 * * * * End recording. Close the current temporary recording file, and * rename the temporary file to the final time-stamp file name. * * */ static void record_sample_close(Recog *recog, void *dummy) { open_error = FALSE; if (recfile_fp == NULL) { fprintf(stderr, "Warning: record_sample_close; file not opened yet!?\n"); return; } if (wrwav_close(recfile_fp) == FALSE) { perror("Error: record_sample_close"); } recfile_fp = NULL; if (recordlen == 0) { unlink(rectmpfilename); if (verbose_flag) { fprintf(stderr, "No input, not recorded\n"); } return; } /* now rename the temporary file to time-stamp filename */ if (rename(rectmpfilename, recordfilename) < 0) { perror("Error: record_sample_close"); fprintf(stderr, "failed to move %s to %s\n", rectmpfilename, recordfilename); return; } if (verbose_flag) { fprintf(stderr, "recorded to \"%s\" (%d bytes, %.2f sec.)\n", recordfilename, recordlen * sizeof(SP16), (float)recordlen / (float) recog->jconf->input.sfreq); } } /************************************************************************/ static boolean opt_record(Jconf *jconf, char *arg[], int argnum) { record_dirname = strdup(arg[0]); #if !defined(_WIN32) || defined(__CYGWIN32__) if (access(record_dirname, R_OK | W_OK | X_OK) == -1) { perror("checkdir"); fprintf(stderr, "Error: cannot write to dir %s\n", record_dirname); return FALSE; } #endif return TRUE; } void record_add_option() { j_add_option("-record", 1, 1, "record input waveform to file in dir", opt_record); } /************************************************************************/ void record_setup(Recog *recog, void *data) { if (record_dirname) { /* regist callbacks */ callback_add_adin(recog, CALLBACK_ADIN_TRIGGERED, record_sample_write, data); callback_add(recog, CALLBACK_EVENT_SPEECH_STOP, record_sample_close, data); printf("Input speech data will be stored to = %s/\n", record_dirname); } }