?? hspalsadevice.cpp
字號:
period_time, period_time_out);#endif period_time = period_time_out; } } /* Apply parameters */ err = snd_pcm_hw_params(m_pAlsaPCMHandle, hwparams); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } /* read buffer & period sizes */ snd_pcm_uframes_t buffer_size = 0; snd_pcm_uframes_t period_size = 0; if (err == 0) { err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_get_buffer_size: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } else { HX_ASSERT (buffer_size > 0); } } if (err == 0) { err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_get_period_size: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } /* Get hardware pause */ if (err == 0) { int can_pause = 0; int can_resume = 0; can_pause = snd_pcm_hw_params_can_pause(hwparams); can_resume = snd_pcm_hw_params_can_resume(hwparams); // could we really have one without the other? m_bHasHardwarePauseAndResume = (can_pause && can_resume); m_Player->print2stderr("########## can_pause %d can_resume %d\n", can_pause, can_resume); } /* Software parameters */ if (err == 0) { err = snd_pcm_sw_params_current(m_pAlsaPCMHandle, swparams); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_current: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } snd_pcm_uframes_t start_threshold = ((buffer_size - 1) / period_size) * period_size; if (err == 0) { err = snd_pcm_sw_params_set_start_threshold(m_pAlsaPCMHandle, swparams, start_threshold); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_start_threshold: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } if (err == 0) { err = snd_pcm_sw_params_set_avail_min(m_pAlsaPCMHandle, swparams, period_size); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_avail_min: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } if (err == 0) { err = snd_pcm_sw_params_set_xfer_align(m_pAlsaPCMHandle, swparams, 1); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_xfer_align: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } if (err == 0) { err = snd_pcm_sw_params_set_tstamp_mode(m_pAlsaPCMHandle, swparams, SND_PCM_TSTAMP_MMAP); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_xfer_align: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } if (err == 0) { err = snd_pcm_sw_params_set_stop_threshold(m_pAlsaPCMHandle, swparams, ~0U); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_stop_threshold: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } if (err == 0) { err = snd_pcm_sw_params(m_pAlsaPCMHandle, swparams); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } /* If all the calls to this point have succeeded, move to the PREPARE state. We will enter the RUNNING state when we've buffered enough for our start theshold. */ if (err == 0) { err = snd_pcm_prepare (m_pAlsaPCMHandle); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_prepare: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_NOTENABLED; } } /* Sanity check: See if we're now in the PREPARE state */ if (err == 0) { snd_pcm_state_t state; state = snd_pcm_state (m_pAlsaPCMHandle); if (state != SND_PCM_STATE_PREPARED) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "Expected to be in PREPARE state, actually in state %d", (int) state);#endif m_wLastError = RA_AOE_NOTENABLED; } } /* Use avail to get the alsa buffer size, which is distinct from the hardware buffer size. This will match what GetRoomOnDevice uses. */ int alsa_buffer_size = 0; err = snd_pcm_avail_update(m_pAlsaPCMHandle); if(err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_avail_update: %s", snd_strerror(err));#endif } else { alsa_buffer_size = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, err); err = 0; } if (err == 0) { m_wLastError = RA_AOE_NOERR; m_unSampleRate = sample_rate; m_unNumChannels = channels; m_wBlockSize = m_ulBytesPerGran; m_ulDeviceBufferSize = alsa_buffer_size; m_uSampFrameSize = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, 1) / channels; #ifdef HX_LOG_SUBSYSTEM HXLOGL2 ( HXLOG_ADEV, "Device Configured:\n"); HXLOGL2 ( HXLOG_ADEV, " Sample Rate: %d", m_unSampleRate); HXLOGL2 ( HXLOG_ADEV, " Sample Width: %d", m_uSampFrameSize); HXLOGL2 ( HXLOG_ADEV, " Num channels: %d", m_unNumChannels); HXLOGL2 ( HXLOG_ADEV, " Block size: %d", m_wBlockSize); HXLOGL2 ( HXLOG_ADEV, " Device buffer size: %lu", m_ulDeviceBufferSize); HXLOGL2 ( HXLOG_ADEV, " Supports HW Pause: %d", m_bHasHardwarePauseAndResume); HXLOGL2 ( HXLOG_ADEV, " Start threshold: %d", start_threshold);#endif } else { m_unSampleRate = 0; m_unNumChannels = 0; if (m_pAlsaPCMHandle) { _CloseAudio(); } } return m_wLastError;}//Device specific method to write bytes out to the audiodevice and return a//count of bytes written.HX_RESULT HSPAudioDevice::WriteBytes( UCHAR* buffer, ULONG32 ulBuffLength, LONG32& lCount ){ int err = 0, count = 0; unsigned int frames_written = 0; snd_pcm_sframes_t num_frames = 0; ULONG32 ulBytesToWrite = ulBuffLength; ULONG32 ulBytesWrote = 0; lCount = 0; HX_ASSERT(m_pAlsaPCMHandle); if (!m_pAlsaPCMHandle) { m_wLastError = RA_AOE_DEVNOTOPEN; return m_wLastError; } m_wLastError = RA_AOE_NOERR; if (ulBuffLength == 0) { lCount = ulBuffLength; return m_wLastError; } do { pthread_mutex_lock(&m_m); if (!m_closed) { if (!m_SWPause) { num_frames = snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, ulBytesToWrite); err = snd_pcm_writei( m_pAlsaPCMHandle, buffer, num_frames ); } else err = -EAGAIN; } else { pthread_mutex_unlock(&m_m); return 0; } pthread_mutex_unlock(&m_m); count++; if (err >= 0) { frames_written = err; pthread_mutex_lock(&m_m); if (!m_closed) ulBytesWrote = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, frames_written); pthread_mutex_unlock(&m_m); buffer += ulBytesWrote; ulBytesToWrite -= ulBytesWrote; lCount += ulBytesWrote; m_ulTotalWritten += ulBytesWrote; } else { switch (err) { case -EAGAIN: usleep(10000); break; case -EPIPE: HandleXRun(); lCount = (LONG32) ulBuffLength; break; case -ESTRPIPE: HandleSuspend(); lCount = (LONG32) ulBuffLength; break; default: m_Player->print2stderr("########### snd_pcm_writei: %s num_frames=%ld\n", snd_strerror(err), num_frames);#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_writei: %s", snd_strerror(err));#endif m_wLastError = RA_AOE_DEVBUSY; } } } while (err == -EAGAIN || (err>0 && ulBytesToWrite>0)); //m_Player->print2stderr("############## count = %d\n", count); return m_wLastError;}/* Subtract the `struct timeval' values X and Y, storing the result in RESULT. Return 1 if the difference is negative, otherwise 0. */inttimeval_subtract (struct timeval *result, const struct timeval *x, const struct timeval *y_orig){ struct timeval y = *y_orig; /* Perform the carry for the later subtraction by updating Y. */ if (x->tv_usec < y.tv_usec) { int nsec = (y.tv_usec - x->tv_usec) / 1000000 + 1; y.tv_usec -= 1000000 * nsec; y.tv_sec += nsec; } if ((x->tv_usec - y.tv_usec) > 1000000) { int nsec = (x->tv_usec - y.tv_usec) / 1000000; y.tv_usec += 1000000 * nsec; y.tv_sec -= nsec; } /* Compute the time remaining to wait. `tv_usec' is certainly positive. */ result->tv_sec = x->tv_sec - y.tv_sec; result->tv_usec = x->tv_usec - y.tv_usec; /* Return 1 if result is negative. */ return x->tv_sec < y.tv_sec;}HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingTStamps(UINT64 &nBytesPlayed) const{ HX_RESULT retVal = HXR_FAIL; int err = 0; snd_timestamp_t trigger_tstamp, now_tstamp, diff_tstamp; snd_pcm_status_t* status; snd_pcm_status_alloca(&status); err = snd_pcm_status(m_pAlsaPCMHandle, status); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_status: %s", snd_strerror(err));#endif } if (err == 0) { snd_pcm_status_get_tstamp(status, &now_tstamp); snd_pcm_status_get_trigger_tstamp(status, &trigger_tstamp); if(!m_bGotInitialTrigger && now_tstamp.tv_sec == 0 && now_tstamp.tv_usec == 0) { /* Our first "now" timestamp appears to be invalid (or the user is very unlucky, and happened to start playback as the timestamp rolls over). Fall back to using snd_pcm_delay. XXXRGG: Is there a better way to figure out if the driver supports mmap'd timestamps? */ m_bUseMMAPTStamps = FALSE; } else { /* Timestamp seems to be valid */ if(!m_bGotInitialTrigger) { m_bGotInitialTrigger = TRUE; memcpy(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger)); } else { if(memcmp(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger)) != 0) { /* There's been a trigger since last time -- restart the timestamp counter XXXRGG: What if there's been multiple triggers? */ m_nBytesPlayedBeforeLastTrigger = m_nLastBytesPlayed; memcpy(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger));#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "Retriggered...");#endif } } timeval_subtract (&diff_tstamp, &now_tstamp, &m_tstampLastTrigger); double fTimePlayed = (double) diff_tstamp.tv_sec + ((double) diff_tstamp.tv_usec / 1e6); nBytesPlayed = (UINT64) ((fTimePlayed * (double) m_unSampleRate * m_uSampFrameSize * m_unNumChannels) + m_nBytesPlayedBeforeLastTrigger); retVal = HXR_OK; } } return retVal;}HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingDelay (UINT64 &nBytesPlayed) const{ HX_RESULT retVal = HXR_FAIL; int err = 0; snd_pcm_sframes_t frame_delay = 0; err = snd_pcm_delay (m_pAlsaPCMHandle, &frame_delay); if (err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_status: %s", snd_strerror(err)); #endif } else { int bytes_delay; bytes_delay = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, frame_delay); nBytesPlayed = m_ulTotalWritten - bytes_delay; retVal = HXR_OK; }#ifdef HX_LOG_SUBSYSTEM// HXLOGL4 ( HXLOG_ADEV, "nBytesPlayed: %llu, m_ulTotalWritten: %llu\n", nBytesPlayed, m_ulTotalWritten);#endif return retVal;}HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingAvail(UINT64 &nBytesPlayed) const{ /* Try this the hwsync way. This method seems to crash & burn with dmix, as avail seems to come from the device, and varies depending on what other dmix clients are writing to the slave device. Currently not used for that reason. */ HX_RESULT retVal = HXR_FAIL; int err = 0; err = snd_pcm_hwsync(m_pAlsaPCMHandle); if(err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hwsync: %s", snd_strerror(err)); #endif } err = snd_pcm_avail_update(m_pAlsaPCMHandle); if(err < 0) {#ifdef HX_LOG_SUBSYSTEM HXLOGL1 ( HXLOG_ADEV, "snd_pcm_avail_update: %s", snd_strerror(err)); #endif } else { snd_pcm_sframes_t avail = err; int bytes_avail; bytes_avail = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, avail); nBytesPlayed = m_ulTotalWritten - (m_ulDeviceBufferSize - bytes_avail); retVal = HXR_OK; } return retVal;}HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingTimer(UINT64 &/*nBytesPlayed*/) const{ /* Look at the alsa timer api, and how we can lock onto it as a timer source. */ return HXR_FAIL;}UINT64 HSPAudioDevice::GetBytesActualyPlayed(void) const{ HX_ASSERT(m_pAlsaPCMHandle); if (!m_pAlsaPCMHandle) { return 0; } HX_RESULT retVal = HXR_OK; UINT64 nBytesPlayed = 0; snd_pcm_state_t state; for(;;) { state = snd_pcm_state(m_pAlsaPCMHandle); switch(state) { case SND_PCM_STATE_OPEN: case SND_PCM_STATE_SETUP: case SND_PCM_STATE_PREPARED: /* If we're in one of these states, written and played should match. */ m_nLastBytesPlayed = m_ulTotalWritten; return m_nLastBytesPlayed; case SND_PCM_STATE_XRUN: HandleXRun(); continue; case SND_PCM_STATE_RUNNING: break; case SND_PCM_STATE_PAUSED: // return m_nLastBytesPlayed; break; case SND_PCM_STATE_DRAINING: case SND_PCM_STATE_SUSPENDED:
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -