Пример #1
0
EC_VOID AudioRender::DispatchMsg(EC_VOID* pMsg)
{
    if (pMsg)
        HandleControlMsg(pMsg);
    else
        RenderAudio();
}
    /**
     * Entry point for the thread.
     */
    int AudioOutputDeviceAlsa::Main() {
        while (true) {

            // let all connected engines render 'FragmentSize' sample points
            RenderAudio(FragmentSize);

            // convert from DSP value range (-1.0..+1.0) to 16 bit integer value
            // range (-32768..+32767), check clipping  and copy to Alsa output buffer
            // (note: we use interleaved output method to Alsa)
            for (int c = 0; c < uiAlsaChannels; c++) {
                float* in  = Channels[c]->Buffer();
                for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) {
                    float sample_point = in[i] * 32768.0f;
                    if (sample_point < -32768.0) sample_point = -32768.0;
                    if (sample_point >  32767.0) sample_point =  32767.0;
                    pAlsaOutputBuffer[o] = (int16_t) sample_point;
                }
            }

            // output sound
            int res = Output();
            if (res < 0) {
                fprintf(stderr, "Alsa: Audio output error, exiting.\n");
                exit(EXIT_FAILURE);
            }
        }
        // just to suppress compiler warning
        return EXIT_FAILURE;
    }
 /**
  * This method should not be called directly! It will be called by
  * libjack to demand transmission of further sample points.
  */
 int AudioOutputDeviceJack::Process(uint Samples) {
     if (csIsPlaying.Pop()) {
         // let all connected engines render 'Samples' sample points
         return RenderAudio(Samples);
     }
     else {
         // playback stop by zeroing output buffer(s) and not calling connected sampler engines to render audio
         return RenderSilence(Samples);
     }
 }
DWORD CWASAPIRenderFilter::ThreadProc()
{
  Log("CWASAPIRenderFilter::Render thread - starting up - thread ID: %d", m_ThreadId);
  
  SetThreadName(0, "WASAPI-renderer");

  // Polling delay
  LARGE_INTEGER liDueTime; 
  liDueTime.QuadPart = -1LL;

  AudioSinkCommand command;
  
  LONGLONG writeSilence = 0;
  BYTE* sampleData = NULL;

  bool flush = false;
  bool sampleProcessed = false;

  REFERENCE_TIME dueTime = 0;
  REFERENCE_TIME maxSampleWaitTime = Latency() / 20000;

  HRESULT hr = S_FALSE;

  m_csResources.Lock();

  if (m_pSettings->m_bReleaseDeviceOnStop && !m_pAudioClient && m_pInputFormat)
  {
    hr = CreateAudioClient(true);
    if (FAILED(hr))
    {
      Log("CWASAPIRenderFilter::Render thread Error, audio client not available: (0x%08x)", hr);
      StopRenderThread();
      m_csResources.Unlock();
      return 0;
    }
  }

  if (m_pAudioClient)
  {
    hr = StartAudioClient();
    if (FAILED(hr))
    {
      Log("CWASAPIRenderFilter::Render thread Error, starting audio client failed: (0x%08x)", hr);
      StopRenderThread();
      m_csResources.Unlock();
      return 0;
    }
  }

  if (!m_bDeviceInitialized)
  {
    Log("CWASAPIRenderFilter::Render thread Error, device not initialized");
    StopRenderThread();
    m_csResources.Unlock();
    return 0;
  }

  EnableMMCSS();
  m_state = StateRunning;

  while (true)
  {
    if (flush)
    {
      Log("CWASAPIRenderFilter::Render thread flushing buffers");
      HandleFlush();
      flush = false;
    }
    
    m_csResources.Unlock();
    hr = WaitForEvents(INFINITE, &m_hDataEvents, &m_dwDataWaitObjects);
    m_csResources.Lock();

    if (hr == MPAR_S_THREAD_STOPPING || !m_pAudioClient)
    {
      StopRenderThread();
      return 0;
    }
    else if (hr == MPAR_S_NEED_DATA)
    {
      UpdateAudioClock();

      UINT32 bytesFilled = 0;
      UINT32 bufferSize = 0;
      UINT32 currentPadding = 0;
      UINT32 bufferSizeInBytes = 0;
      BYTE* data = NULL;
      DWORD flags = 0;

      static BYTE* prevData = NULL;
       
      hr = GetWASAPIBuffer(bufferSize, currentPadding, bufferSizeInBytes, &data);
      if (SUCCEEDED(hr))
      {
        do
        {
          fetchSample:

          bool OOBCommandOnly = m_nDataLeftInSample > 0;

          if (m_nDataLeftInSample == 0 || OOBCommandOnly)
          {
            m_csResources.Unlock();
            HRESULT result = GetNextSampleOrCommand(&command, &m_pCurrentSample.p, maxSampleWaitTime, &m_hSampleEvents,
                                                    &m_dwSampleWaitObjects, OOBCommandOnly);
            m_csResources.Lock();

            if (result == MPAR_S_THREAD_STOPPING || !m_pAudioClient)
            {
              if (m_pAudioClient)
              {
                hr = m_pRenderClient->ReleaseBuffer(bufferSize - currentPadding, flags);
                if (FAILED(hr) && hr != AUDCLNT_E_OUT_OF_ORDER)
                  Log("CWASAPIRenderFilter::Render thread: ReleaseBuffer failed (0x%08x)", hr);
              }

              StopRenderThread();
              return 0;
            }

            if (!m_pCurrentSample)
              m_nDataLeftInSample = 0;
              
            if (command == ASC_PutSample && m_pCurrentSample)
            {
              sampleProcessed = false;
              m_nSampleOffset = 0;
              m_nDataLeftInSample = m_pCurrentSample->GetActualDataLength();
            }
            else if (command == ASC_Flush)
            {
              m_pCurrentSample.Release();

              flush = true;
              sampleData = NULL;
              m_nSampleOffset = 0;
              m_nDataLeftInSample = 0;

              break;
            }
            else if (command == ASC_Pause)
            {
              m_pCurrentSample.Release();
              m_state = StatePaused;
            }
            else if (command == ASC_Resume)
            {
              sampleProcessed = false;
              writeSilence = 0;
              m_state = StateRunning;
              if (!m_pCurrentSample)
              {
                m_nDataLeftInSample = 0;
                goto fetchSample;
              }
            }
          }

          if (m_state != StateRunning)
            writeSilence = bufferSizeInBytes - bytesFilled;
          else if (m_nSampleOffset == 0 && !OOBCommandOnly)
          {
            // TODO error checking
            if (CheckSample(m_pCurrentSample, bufferSize - currentPadding) == S_FALSE)
            {
              GetWASAPIBuffer(bufferSize, currentPadding, bufferSizeInBytes, &data);
              bytesFilled = 0;
            }
          }

          if (writeSilence == 0 && (m_nSampleOffset == 0 || m_nSampleNum == 0) && !sampleProcessed)
          {
            HRESULT schedulingHR = CheckStreamTimeline(m_pCurrentSample, &dueTime, m_nSampleOffset);
            sampleProcessed = true;
              
            // m_pCurrentSample must exist if CheckStreamTimeline returns either of these
            if (schedulingHR == MPAR_S_DROP_SAMPLE)
            {
              m_pCurrentSample.Release();
              m_nDataLeftInSample = 0;
              goto fetchSample;
            }
            else if (schedulingHR == MPAR_S_WAIT_RENDER_TIME)
              CalculateSilence(&dueTime, &writeSilence);
          }

          if (writeSilence == 0 && m_pCurrentSample)
            RenderAudio(data, bufferSizeInBytes, m_nDataLeftInSample, m_nSampleOffset, m_pCurrentSample, bytesFilled);
          else
          {
            if (bufferSizeInBytes == writeSilence)
              flags = AUDCLNT_BUFFERFLAGS_SILENT;

            if (!m_pCurrentSample)
              writeSilence = bufferSizeInBytes;

            RenderSilence(data, bufferSizeInBytes, writeSilence, bytesFilled);
          }
        } while (bytesFilled < bufferSizeInBytes);

        hr = m_pRenderClient->ReleaseBuffer(bufferSize - currentPadding, flags);

        if (FAILED(hr) && hr != AUDCLNT_E_OUT_OF_ORDER)
          Log("CWASAPIRenderFilter::Render thread: ReleaseBuffer failed (0x%08x)", hr);
      }

      if (!m_pSettings->m_bWASAPIUseEventMode)
      {
        if (m_pAudioClient)
          hr = m_pAudioClient->GetCurrentPadding(&currentPadding);
        else
          hr = S_FALSE;

        if (SUCCEEDED(hr) && bufferSize > 0)
        {
          liDueTime.QuadPart = (double)currentPadding / (double)bufferSize * (double)m_pSettings->m_hnsPeriod * -0.9;
          // Log(" currentPadding: %d QuadPart: %lld", currentPadding, liDueTime.QuadPart);
        }
        else
        {
          liDueTime.QuadPart = (double)m_pSettings->m_hnsPeriod * -0.9;
          if (hr != AUDCLNT_E_NOT_INITIALIZED)
            Log("CWASAPIRenderFilter::Render thread: GetCurrentPadding failed (0x%08x)", hr);  
        }
        SetWaitableTimer(m_hDataEvent, &liDueTime, 0, NULL, NULL, 0);
      }
    }
  }
  
  m_csResources.Unlock();
  return 0;
}