/*************************************************************************************** * * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. * * By downloading, copying, installing or using the software you agree to this license. * If you do not agree to this license, do not download, install, * copy or use the software. * * Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved. * * Redistribution and use in binary forms, with or without modification, are permitted. * * 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 "sys_inc.h" #include "audio_play_win.h" /**************************************************************************************/ #pragma comment(lib, "dsound.lib") /**************************************************************************************/ CWAudioPlay::CWAudioPlay() : CAudioPlay() , m_pDSound8(NULL) , m_pDSoundBuffer(NULL) , m_pMutex(NULL) , m_pAudioBuff(NULL) , m_nAudioBuffLen(0) , m_nLastChunk(0) , m_nBufferNums(8) , m_nSampleNums(1024) , m_nSpecSize(0) { m_pMutex = sys_os_create_mutex(); } CWAudioPlay::~CWAudioPlay(void) { stopPlay(); sys_os_destroy_sig_mutex(m_pMutex); m_pMutex = NULL; } BOOL CWAudioPlay::startPlay(int samplerate, int channels) { HRESULT ret = DirectSoundCreate8(NULL, &m_pDSound8, NULL); if (FAILED(ret)) { log_print(HT_LOG_ERR, "%s, DirectSoundCreate8 failed\r\n", __FUNCTION__); return FALSE; } ret = m_pDSound8->SetCooperativeLevel(GetDesktopWindow(), DSSCL_NORMAL); if (FAILED(ret)) { stopPlay(); log_print(HT_LOG_ERR, "%s, SetCooperativeLevel failed\r\n", __FUNCTION__); return FALSE; } WAVEFORMATEX format; memset(&format, 0, sizeof(WAVEFORMATEX)); format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = channels; format.wBitsPerSample = 16; format.nSamplesPerSec = samplerate; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec; format.cbSize = 0; DSBUFFERDESC buf_desc; memset(&buf_desc, 0, sizeof(DSBUFFERDESC)); m_nSpecSize = m_nSampleNums * channels * 2; buf_desc.dwSize = sizeof(buf_desc); buf_desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME; buf_desc.dwBufferBytes = m_nBufferNums * m_nSpecSize; buf_desc.dwReserved = 0; buf_desc.lpwfxFormat = &format; ret = m_pDSound8->CreateSoundBuffer(&buf_desc, &m_pDSoundBuffer, NULL); if (FAILED(ret)) { stopPlay(); log_print(HT_LOG_ERR, "%s, CreateSoundBuffer failed\r\n", __FUNCTION__); return FALSE; } m_pDSoundBuffer->SetFormat(&format); void * buf1 = NULL; DWORD len1 = 0; void * buf2 = NULL; DWORD len2 = 0; /* Silence the initial audio buffer */ ret = m_pDSoundBuffer->Lock(0, buf_desc.dwBufferBytes, &buf1, &len1, &buf2, &len2, DSBLOCK_ENTIREBUFFER); if (ret == DS_OK) { memset(buf1, 0, len1); m_pDSoundBuffer->Unlock(buf1, len1, buf2, len2); } m_pAudioBuff = (uint8 *) malloc(m_nSpecSize); if (NULL == m_pAudioBuff) { stopPlay(); log_print(HT_LOG_ERR, "%s, memory malloc failed\r\n", __FUNCTION__); return FALSE; } m_nSamplerate = samplerate; m_nChannels = channels; m_bInited = TRUE; return TRUE; } void CWAudioPlay::stopPlay() { m_bInited = FALSE; sys_os_mutex_enter(m_pMutex); if (m_pDSoundBuffer) { m_pDSoundBuffer->Stop(); m_pDSoundBuffer->Release(); m_pDSoundBuffer = NULL; } if (m_pDSound8) { m_pDSound8->Release(); m_pDSound8 = NULL; } if (m_pAudioBuff) { free(m_pAudioBuff); m_pAudioBuff = NULL; } sys_os_mutex_leave(m_pMutex); m_nAudioBuffLen = 0; } BOOL CWAudioPlay::setVolume(int volume) { if (m_pDSoundBuffer) { double db = (double) volume / (HTVOLUME_MAX - HTVOLUME_MIN); if (db < 0) { db = -db; } int nv = (DSBVOLUME_MAX - DSBVOLUME_MIN) * db + DSBVOLUME_MIN; HRESULT hr = m_pDSoundBuffer->SetVolume(nv); if (DS_OK == hr) { return TRUE; } } return FALSE; } int CWAudioPlay::getVolume() { if (m_pDSoundBuffer) { long volume = 0; HRESULT ret = m_pDSoundBuffer->GetVolume(&volume); if (SUCCEEDED(ret)) { double db = (double) volume / (DSBVOLUME_MAX - DSBVOLUME_MIN); if (db < 0) { db = -db; } int nv = (HTVOLUME_MAX - HTVOLUME_MIN) * db + HTVOLUME_MIN; return nv; } } return HTVOLUME_MIN; } void CWAudioPlay::playAudio1(uint8 * data, int size) { DWORD cursor = 0; DWORD junk = 0; HRESULT result = DS_OK; DWORD rawlen = 0; void * bufptr = NULL; /* Figure out which blocks to fill next */ result = m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor); if (result == DSERR_BUFFERLOST) { m_pDSoundBuffer->Restore(); result = m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor); } if (result != DS_OK) { return; } cursor /= m_nSpecSize; m_nLastChunk = cursor; cursor = (cursor + 1) % m_nBufferNums; cursor *= m_nSpecSize; /* Lock the audio buffer */ result = m_pDSoundBuffer->Lock(cursor, m_nSpecSize, &bufptr, &rawlen, NULL, &junk, 0); if (result == DSERR_BUFFERLOST) { m_pDSoundBuffer->Restore(); result = m_pDSoundBuffer->Lock(cursor, m_nSpecSize, &bufptr, &rawlen, NULL, &junk, 0); } if (result != DS_OK) { return; } if (bufptr && rawlen > 0) { memcpy(bufptr, data, rawlen); } m_pDSoundBuffer->Unlock(bufptr, rawlen, NULL, 0); waitDevice(); } void CWAudioPlay::playAudio(uint8 * data, int size) { if (!m_bInited) { return; } sys_os_mutex_enter(m_pMutex); if (NULL == m_pDSoundBuffer) { sys_os_mutex_leave(m_pMutex); return; } while (m_nAudioBuffLen + size >= (uint32)m_nSpecSize) { memcpy(m_pAudioBuff + m_nAudioBuffLen, data, m_nSpecSize - m_nAudioBuffLen); playAudio1(m_pAudioBuff, m_nSpecSize); size -= m_nSpecSize - m_nAudioBuffLen; data += m_nSpecSize - m_nAudioBuffLen; m_nAudioBuffLen = 0; } if (size > 0) { memcpy(m_pAudioBuff + m_nAudioBuffLen, data, size); m_nAudioBuffLen += size; } sys_os_mutex_leave(m_pMutex); } void CWAudioPlay::waitDevice() { DWORD status = 0; DWORD cursor = 0; DWORD junk = 0; HRESULT result = DS_OK; result = m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor); if (result != DS_OK) { if (result == DSERR_BUFFERLOST) { m_pDSoundBuffer->Restore(); } return; } while ((cursor / m_nSpecSize) == m_nLastChunk) { usleep(1000); /* Try to restore a lost sound buffer */ m_pDSoundBuffer->GetStatus(&status); if (status & DSBSTATUS_BUFFERLOST) { m_pDSoundBuffer->Restore(); m_pDSoundBuffer->GetStatus(&status); if ((status & DSBSTATUS_BUFFERLOST)) { break; } } if (!(status & DSBSTATUS_PLAYING)) { result = m_pDSoundBuffer->Play(0, 0, DSBPLAY_LOOPING); if (result == DS_OK) { continue; } return; } /* Find out where we are playing */ result = m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor); if (result != DS_OK) { return; } } }