void AudioRenderer::SetClock(IReferenceClock* pClock) { CAutoLock objectLock(this); m_graphClock = pClock; if (m_graphClock && !IsEqualObject(m_graphClock, m_myClock.GetOwner())) { if (!m_externalClock) ClearDevice(); m_externalClock = true; } else { if (m_externalClock) ClearDevice(); m_externalClock = false; } }
void AudioRenderer::CreateDevice() { CAutoLock objectLock(this); assert(!m_device); assert(m_inputFormat); m_deviceSettingsSerial = m_settings->GetSerial(); m_defaultDeviceSerial = m_deviceManager.GetDefaultDeviceSerial(); m_device = m_deviceManager.CreateDevice(m_inputFormat, m_live || m_externalClock, m_settings); if (m_device) { m_sampleCorrection.NewDeviceBuffer(); InitializeProcessors(); m_startClockOffset = m_sampleCorrection.GetLastFrameEnd(); if (m_state == State_Running) StartDevice(); } }
void AudioRenderer::InitializeProcessors() { CAutoLock objectLock(this); assert(m_inputFormat); assert(m_device); if (m_device->IsBitstream()) return; const auto inRate = m_inputFormat->nSamplesPerSec; const auto inChannels = m_inputFormat->nChannels; const auto inMask = DspMatrix::GetChannelMask(*m_inputFormat); const auto outRate = m_device->GetWaveFormat()->nSamplesPerSec; const auto outChannels = m_device->GetWaveFormat()->nChannels; const auto outMask = DspMatrix::GetChannelMask(*m_device->GetWaveFormat()); m_dspMatrix.Initialize(inChannels, inMask, outChannels, outMask); m_dspRate.Initialize(m_live || m_externalClock, inRate, outRate, outChannels); m_dspRealtimeRate.Initialize(m_live || m_externalClock, inRate, outRate, outChannels); m_dspTempo.Initialize(m_rate, outRate, outChannels); m_dspCrossfeed.Initialize(m_settings, outRate, outChannels, outMask); m_dspLimiter.Initialize(outRate, outChannels, m_device->IsExclusive()); m_dspDither.Initialize(m_device->GetDspFormat()); }
void detachThread(Thread *thread) { Object *group, *excep; //ExecEnv *ee = thread->ee; //Object *jThread = ee->thread; Object *jThread = thread->thread; Object *vmthread = (Object*)INST_DATA(jThread)[vmthread_offset]; /* Get the thread's group */ group = (Object *)INST_DATA(jThread)[group_offset]; /* If there's an uncaught exception, call uncaughtException on the thread's exception handler, or the thread's group if this is unset */ if((excep = exceptionOccurred())) { FieldBlock *fb = findField(thread_class, SYMBOL(exceptionHandler), SYMBOL(sig_java_lang_Thread_UncaughtExceptionHandler)); Object *thread_handler = fb == NULL ? NULL : (Object *)INST_DATA(jThread)[fb->offset]; Object *handler = thread_handler == NULL ? group : thread_handler; MethodBlock *uncaught_exp = lookupMethod(handler->classobj, SYMBOL(uncaughtException), SYMBOL(_java_lang_Thread_java_lang_Throwable__V)); if(uncaught_exp) { clearException(); DummyFrame dummy; executeMethod(&dummy, handler, uncaught_exp, jThread, excep); } else printException(); } /* remove thread from thread group */ DummyFrame dummy; executeMethod(&dummy, group, (CLASS_CB(group->classobj))->method_table[rmveThrd_mtbl_idx], jThread); /* set VMThread ref in Thread object to null - operations after this point will result in an IllegalThreadStateException */ INST_DATA(jThread)[vmthread_offset] = 0; /* Remove thread from the ID map hash table */ deleteThreadFromHash(thread); /* Disable suspend to protect lock operation */ disableSuspend(thread); /* Grab global lock, and update thread structures protected by it (thread list, thread ID and number of daemon threads) */ pthread_mutex_lock(&lock); /* remove from thread list... */ if((thread->prev->next = thread->next)) thread->next->prev = thread->prev; /* One less live thread */ threads_count--; /* Recycle the thread's thread ID */ freeThreadID(thread->id); /* Handle daemon thread status */ if(!INST_DATA(jThread)[daemon_offset]) non_daemon_thrds--; pthread_mutex_unlock(&lock); /* notify any threads waiting on VMThread object - these are joining this thread */ objectLock(vmthread); objectNotifyAll(vmthread); objectUnlock(vmthread); /* It is safe to free the thread's ExecEnv and stack now as these are only used within the thread. It is _not_ safe to free the native thread structure as another thread may be concurrently accessing it. However, they must have a reference to the VMThread -- therefore, it is safe to free during GC when the VMThread is determined to be no longer reachable. */ // sysFree(ee->stack); //sysFree(ee); /* If no more daemon threads notify the main thread (which may be waiting to exit VM). Note, this is not protected by lock, but main thread checks again */ if(non_daemon_thrds == 0) { /* No need to bother with disabling suspension * around lock, as we're no longer on thread list */ pthread_mutex_lock(&exit_lock); pthread_cond_signal(&exit_cv); pthread_mutex_unlock(&exit_lock); } TRACE("Thread 0x%x id: %d detached from VM\n", thread, thread->id); }
bool AudioRenderer::IsLive() { CAutoLock objectLock(this); return m_live; }
bool AudioRenderer::OnExternalClock() { CAutoLock objectLock(this); return m_externalClock; }
void AudioRenderer::ApplyRateCorrection(DspChunk& chunk) { CAutoLock objectLock(this); assert(m_device); assert(!m_device->IsBitstream()); assert(m_state == State_Running); if (chunk.IsEmpty()) return; const REFERENCE_TIME latency = llMulDiv(chunk.GetFrameCount(), OneSecond, chunk.GetRate(), 0) + m_device->GetStreamLatency() + OneMillisecond * 10; const REFERENCE_TIME remaining = m_device->GetEnd() - m_device->GetPosition(); REFERENCE_TIME deltaTime = 0; if (m_live) { // Rate matching. if (remaining > latency) { size_t dropFrames = (size_t)llMulDiv(m_device->GetWaveFormat()->nSamplesPerSec, remaining - latency, OneSecond, 0); dropFrames = std::min(dropFrames, chunk.GetFrameCount()); chunk.ShrinkHead(chunk.GetFrameCount() - dropFrames); DebugOut("AudioRenderer drop", dropFrames, "frames for rate matching"); } } else { // Clock matching. assert(m_externalClock); REFERENCE_TIME graphTime, myTime, myStartTime; if (SUCCEEDED(m_myClock.GetAudioClockStartTime(&myStartTime)) && SUCCEEDED(m_myClock.GetAudioClockTime(&myTime, nullptr)) && SUCCEEDED(m_graphClock->GetTime(&graphTime)) && myTime > myStartTime) { myTime -= m_device->GetSilence(); if (myTime > graphTime) { // Pad and adjust backwards. REFERENCE_TIME padTime = myTime - graphTime; assert(padTime >= 0); size_t padFrames = (size_t)llMulDiv(m_device->GetWaveFormat()->nSamplesPerSec, padTime, OneSecond, 0); if (padFrames > m_device->GetWaveFormat()->nSamplesPerSec / 33) // ~30ms threshold { DspChunk tempChunk(chunk.GetFormat(), chunk.GetChannelCount(), chunk.GetFrameCount() + padFrames, chunk.GetRate()); size_t padBytes = tempChunk.GetFrameSize() * padFrames; ZeroMemory(tempChunk.GetData(), padBytes); memcpy(tempChunk.GetData() + padBytes, chunk.GetData(), chunk.GetSize()); chunk = std::move(tempChunk); REFERENCE_TIME paddedTime = llMulDiv(padFrames, OneSecond, m_device->GetWaveFormat()->nSamplesPerSec, 0); m_myClock.OffsetSlavedClock(-paddedTime); padTime -= paddedTime; assert(padTime >= 0); DebugOut("AudioRenderer pad", paddedTime / 10000., "ms for clock matching at", m_sampleCorrection.GetLastFrameEnd() / 10000., "frame position"); } // Correct the rest with variable rate. m_dspRealtimeRate.Adjust(padTime); m_myClock.OffsetSlavedClock(-padTime); } else if (remaining > latency) { // Crop and adjust forwards. assert(myTime <= graphTime); REFERENCE_TIME dropTime = std::min(graphTime - myTime, remaining - latency); assert(dropTime >= 0); size_t dropFrames = (size_t)llMulDiv(m_device->GetWaveFormat()->nSamplesPerSec, dropTime, OneSecond, 0); dropFrames = std::min(dropFrames, chunk.GetFrameCount()); if (dropFrames > m_device->GetWaveFormat()->nSamplesPerSec / 33) // ~30ms threshold { chunk.ShrinkHead(chunk.GetFrameCount() - dropFrames); REFERENCE_TIME droppedTime = llMulDiv(dropFrames, OneSecond, m_device->GetWaveFormat()->nSamplesPerSec, 0); m_myClock.OffsetSlavedClock(droppedTime); dropTime -= droppedTime; assert(dropTime >= 0); DebugOut("AudioRenderer drop", droppedTime / 10000., "ms for clock matching at", m_sampleCorrection.GetLastFrameEnd() / 10000., "frame position"); } // Correct the rest with variable rate. m_dspRealtimeRate.Adjust(-dropTime); m_myClock.OffsetSlavedClock(dropTime); } } } }
SharedWaveFormat AudioRenderer::GetInputFormat() { CAutoLock objectLock(this); return m_inputFormat; }
bool AudioRenderer::Finish(bool blockUntilEnd, CAMEvent* pFilledEvent) { DspChunk chunk; { CAutoLock objectLock(this); assert(m_state != State_Stopped); // No device - nothing to block on. if (!m_device) blockUntilEnd = false; try { // Apply dsp chain. if (m_device && !m_device->IsBitstream()) { auto f = [&](DspBase* pDsp) { pDsp->Finish(chunk); }; EnumerateProcessors(f); DspChunk::ToFormat(m_device->GetDspFormat(), chunk); } } catch (std::bad_alloc&) { chunk = DspChunk(); assert(chunk.IsEmpty()); } } auto doBlock = [&] { // Increase system timer resolution. TimePeriodHelper timePeriodHelper(1); for (;;) { REFERENCE_TIME remaining = 0; { CAutoLock objectLock(this); if (m_device) { try { remaining = m_device->Finish(pFilledEvent); } catch (HRESULT) { ClearDevice(); } } } // The end of stream is reached. if (remaining <= 0) return true; // Sleep until predicted end of stream. if (m_flush.Wait(std::max(1, (int32_t)(remaining / OneMillisecond)))) return false; } }; // Send processed sample to the device, and block until the end of stream (if requested). return PushToDevice(chunk, pFilledEvent) && (!blockUntilEnd || doBlock()); }