#include "precompile.h" #include "audiowavfile.h" #include "audiocontext.h" #include "audiolog.h" #include static int write_wav_header(HANDLE hFile, int clock, int codec_pt) { WORD wFormat; WORD wBitsPerSample; DWORD dw; DWORD dwByteWritten; WAVEFORMATEX fmt; DWORD dwFmtSize = sizeof(fmt); if (codec_pt == AUDIO_FILE_CODEC_PCM16) { wBitsPerSample = 16; wFormat = WAVE_FORMAT_PCM; dwFmtSize -= 2; } else if (codec_pt == AUDIO_FILE_CODEC_ALAW) { wBitsPerSample = 8; wFormat = WAVE_FORMAT_ALAW; } else if (codec_pt == AUDIO_FILE_CODEC_MULAW) { wBitsPerSample = 8; wFormat = WAVE_FORMAT_MULAW; } else if (codec_pt == AUDIO_FILE_CODEC_PCM8) { wBitsPerSample = 8; wFormat = WAVE_FORMAT_PCM; } else { return -1; } dw = 'FFIR'; // riff chunk WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); dw = 0x7fffffff; // riff chunk size WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); dw = 'EVAW'; // format WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); dw = ' tmf'; // fmt subchunk WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); dw = dwFmtSize; WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); fmt.wFormatTag = wFormat; fmt.nChannels = 1; fmt.nSamplesPerSec = (DWORD)clock; fmt.nBlockAlign = 2; fmt.wBitsPerSample = wBitsPerSample; fmt.cbSize = 0; fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * wBitsPerSample / 8; WriteFile(hFile, &fmt, dwFmtSize, &dwByteWritten, NULL); dw = 'atad'; // data chunk WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); dw = 0x7fffffff; // data chunk size WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); return 0; } static int write_wav_header_end(HANDLE hFile, int pt) { DWORD dwByteWritten; DWORD dw; DWORD dwFileLen; DWORD dwDataChunkOffset; if (hFile == INVALID_HANDLE_VALUE) return -1; dwDataChunkOffset = (pt == AUDIO_FILE_CODEC_PCM16 ? 44 : 46); SetFilePointer(hFile, 0, NULL, FILE_END); dwFileLen = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); dw = dwFileLen - 8; // riff chunk size SetFilePointer(hFile, 4, NULL, FILE_BEGIN); WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); dw = dwFileLen - dwDataChunkOffset; // data chunk size SetFilePointer(hFile, dwDataChunkOffset-4, NULL, FILE_BEGIN); WriteFile(hFile, &dw, sizeof(dw), &dwByteWritten, NULL); SetFilePointer(hFile, 0, NULL, FILE_END); return 0; } static int write_wav_frame(HANDLE hFile, const char *buf, DWORD size) { DWORD dwByteWritten; BOOL bRet = WriteFile(hFile, buf, size, &dwByteWritten, NULL); return bRet ? 0 : -1; } static int read_wav_header(HANDLE hFile, int *clock, int *pt) { DWORD dw; DWORD dwByteRead; DWORD dwFileLen; DWORD dwFormatLen; WAVEFORMATEX fmt; SetFilePointer(hFile, 0, NULL, FILE_END); dwFileLen = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); SetFilePointer(hFile, 0, NULL, FILE_BEGIN); ReadFile(hFile, &dw, sizeof(dw), &dwByteRead, NULL); if (dw != 'FFIR') return -1; ReadFile(hFile, &dw, sizeof(dw), &dwByteRead, NULL); if (dw != dwFileLen-8) return -1; ReadFile(hFile, &dw, sizeof(dw), &dwByteRead, NULL); if (dw != 'EVAW') return -1; ReadFile(hFile, &dw, sizeof(dw), &dwByteRead, NULL); if (dw != ' tmf') return -1; ReadFile(hFile, &dwFormatLen, sizeof(dwFormatLen), &dwByteRead, NULL); if (dwFormatLen < 16) return -1; ReadFile(hFile, &fmt, dwFormatLen, &dwByteRead, NULL); if (fmt.wFormatTag == WAVE_FORMAT_PCM) { if (fmt.wBitsPerSample == 16) *pt = AUDIO_FILE_CODEC_PCM16; else if (fmt.wBitsPerSample == 8) *pt = AUDIO_FILE_CODEC_PCM8; else return -1; // not supported } else if (fmt.wFormatTag == WAVE_FORMAT_ALAW) { *pt = AUDIO_FILE_CODEC_ALAW; } else if (fmt.wFormatTag == WAVE_FORMAT_MULAW) { *pt = AUDIO_FILE_CODEC_MULAW; } else { return -1; } if (fmt.nChannels != 1) return -1; if (fmt.nSamplesPerSec == 8000) *clock = 8000; else if (fmt.nSamplesPerSec == 16000) *clock = 16000; else return -1; // not supported ReadFile(hFile, &dw, sizeof(dw), &dwByteRead, NULL); while (dw != 'atad') { // is not data chunk DWORD tmp; ReadFile(hFile, &tmp, sizeof(tmp), &dwByteRead, NULL); SetFilePointer(hFile, tmp, NULL, FILE_CURRENT); ReadFile(hFile, &dw, sizeof(dw), &dwByteRead, NULL); } ReadFile(hFile, &dw, sizeof(dw), &dwByteRead, NULL); return 0; } static int read_wav_frame(HANDLE hFile, DWORD dwFrameSize, unsigned char *buf) { DWORD dwByteRead; BOOL bRet; bRet = ReadFile(hFile, buf, dwFrameSize, &dwByteRead, NULL); if (!bRet || dwByteRead == 0) { return -1; } if (dwByteRead < dwFrameSize) { memset(buf+dwByteRead, 0, dwFrameSize-dwByteRead); } return 0; } static apr_status_t read_frame(void *self, audioframe_t *frame) { audiofile_t *audiofile = CONTAINING_RECORD(self, audiofile_t, base); audiowavfile_t *audiowavfile = CONTAINING_RECORD(audiofile, audiowavfile_t, base); if (audiofile->opt & AUDIO_FILE_OPT_PLAY) { int pcnt = FRAME_TIME * audiowavfile->clock / 1000 * (audiowavfile->samplebit / 8); if (read_wav_frame(audiofile->file_handle, pcnt, frame->buffer) != 0) { if (GetLastError() == 0 && (audiofile->opt&AUDIO_FILE_OPT_LOOP)) { SetFilePointer(audiofile->file_handle, 0, NULL, FILE_BEGIN); audiostream_raise_event(&audiofile->base, STREAM_EVT_FILE_REPLAY, 0, 0); if (read_wav_frame(audiofile->file_handle, pcnt, frame->buffer) != 0) { audiostream_raise_event(&audiofile->base, STREAM_EVT_FILE_IOERROR, GetLastError(), 0); memset(frame->buffer, 0, pcnt); } } else { audiostream_raise_event(&audiofile->base, STREAM_EVT_FILE_IOERROR, GetLastError(), 0); memset(frame->buffer, 0, pcnt);; } } if (audiowavfile->codec_pt == AUDIO_FILE_CODEC_PCM8) { unsigned char *src = frame->buffer; unsigned char *end_src = frame->buffer + pcnt; short *dst = (short*)frame->buffer+pcnt-1; while (src != end_src) { *dst = (short)(*src << 8) - 32768; ++src; --dst; } frame->size = 2 * pcnt; } else { frame->size = pcnt; } frame->dtmf = 0; } else { return APR_ENOTIMPL; } return APR_SUCCESS; } static apr_status_t write_frame(void *self, const audioframe_t *frame) { audiofile_t *audiofile = CONTAINING_RECORD(self, audiofile_t, base); audiowavfile_t *audiowavfile = CONTAINING_RECORD(audiofile, audiowavfile_t, base); if (audiofile->opt & AUDIO_FILE_OPT_RECORD) { if (audiowavfile->codec_pt == AUDIO_FILE_CODEC_PCM8) { unsigned char *buf = _alloca(frame->size/2); const short *src = (const short *)frame->buffer; int i; for (i = 0; i < (int)frame->size/2; ++i) buf[i] = (src[i] >> 8) + 128; if (write_wav_frame(audiofile->file_handle, buf, frame->size/2) != 0) { audiostream_raise_event(&audiofile->base, STREAM_EVT_FILE_IOERROR, GetLastError(), 0); } } else { if (write_wav_frame(audiofile->file_handle, frame->buffer, frame->size) != 0) { audiostream_raise_event(&audiofile->base, STREAM_EVT_FILE_IOERROR, GetLastError(), 0); } } } else { return APR_ENOTIMPL; } return APR_SUCCESS; } static audiostream_vtbl_t g_stream_vtbl = { &read_frame, &write_frame, }; apr_status_t audiowavfile_create(apr_pool_t *pool, audioengine_t *engine, const char *file, int opt, int clock, int codec_pt, // one of AUDIO_FILE_CODEC_xxx audiowavfile_t **p_file) { apr_status_t status; audiowavfile_t *audiofile; audiofile = apr_palloc(pool, sizeof(audiowavfile_t)); status = audiofile_init(pool, engine, file, opt, &audiofile->base); if (status != APR_SUCCESS) { return status; } audiofile->base.base.vtbl = &g_stream_vtbl; audiofile->clock = clock; audiofile->codec_pt = codec_pt; audiofile->samplebit = (codec_pt == AUDIO_FILE_CODEC_PCM16) ? 16 : 8; if (opt & AUDIO_FILE_OPT_RECORD) { if (write_wav_header(audiofile->base.file_handle, clock, codec_pt) != 0) { CloseHandle(audiofile->base.file_handle); apr_pool_destroy(pool); return APR_EGENERAL; } } else if (opt & AUDIO_FILE_OPT_PLAY) { if (read_wav_header(audiofile->base.file_handle, &clock, &audiofile->codec_pt) != 0) { CloseHandle(audiofile->base.file_handle); apr_pool_destroy(pool); return APR_EGENERAL; } } *p_file = audiofile; return APR_SUCCESS; } void audiowavfile_destroy(audiowavfile_t *audiofile) { if (audiofile->base.opt & AUDIO_FILE_OPT_RECORD) { write_wav_header_end(audiofile->base.file_handle, audiofile->codec_pt); } audiofile_term(&audiofile->base); }