Пример #1
0
bool
PCMMixerDataSource::AddSource(PCMDataSource &source)
{
  assert(source.GetSampleRate() == sample_rate);

  const std::lock_guard<Mutex> protect(lock);

#ifndef NDEBUG
  for (unsigned i = 0; i < MAX_MIXER_SOURCES_COUNT; ++i) {
    assert(sources[i] != &source);
  }
#endif

  for (unsigned i = 0; i < MAX_MIXER_SOURCES_COUNT; ++i) {
    if (nullptr == sources[i]) {
      sources[i] = &source;
      return true;
    }
  }

  return false;
}
Пример #2
0
bool
ALSAPCMPlayer::Start(PCMDataSource &_source)
{
  const unsigned new_sample_rate = _source.GetSampleRate();

  if ((nullptr != source) &&
      alsa_handle &&
      (source->GetSampleRate() == new_sample_rate)) {
    /* just change the source / resume playback */
    bool success = false;
    DispatchWait(io_service, [this, &_source, &success]() {
      bool recovered_from_underrun = false;

      switch (snd_pcm_state(alsa_handle.get())) {
      case SND_PCM_STATE_XRUN:
        if (0 != snd_pcm_prepare(alsa_handle.get()))
          return;
        else {
          recovered_from_underrun = true;
          success = true;
        }
        break;

      case SND_PCM_STATE_RUNNING:
        success = true;
        break;

      default:
        return;
      }

      if (success) {
        source = &_source;

        if (recovered_from_underrun) {
          const size_t n = buffer_size / channels;
          const size_t n_read = FillPCMBuffer(buffer.get(), n);
          if (!WriteFrames(n_read)) {
            success = false;
            return;
          }
        }

        if (success)
          StartEventHandling();
      }
    });
    if (success)
      return true;
  }

  Stop();

  assert(!alsa_handle);

  AlsaHandleUniquePtr new_alsa_handle = MakeAlsaHandleUniquePtr();
  {
    const char *alsa_device = ALSAEnv::GetALSADeviceName();

    snd_pcm_t *raw_alsa_handle;
    int alsa_error = snd_pcm_open(&raw_alsa_handle, alsa_device,
                                  SND_PCM_STREAM_PLAYBACK, 0);
    if (alsa_error < 0) {
      LogFormat("snd_pcm_open(0x%p, \"%s\", SND_PCM_STREAM_PLAYBACK, 0) "
                    "failed: %s",
                &alsa_handle, alsa_device, snd_strerror(alsa_error));
      return false;
    }
    new_alsa_handle = MakeAlsaHandleUniquePtr(raw_alsa_handle);
    assert(new_alsa_handle);
  }

  unsigned latency = ALSAEnv::GetALSALatency();

  channels = 1;
  bool big_endian_source = _source.IsBigEndian();
  if (!SetParameters(*new_alsa_handle, new_sample_rate, big_endian_source,
                     latency, channels))
    return false;

  snd_pcm_sframes_t n_available = snd_pcm_avail(new_alsa_handle.get());
  if (n_available <= 0) {
    LogFormat("snd_pcm_avail(0x%p) failed: %ld - %s",
              new_alsa_handle.get(),
              static_cast<long>(n_available),
              snd_strerror(static_cast<int>(n_available)));
    return false;
  }

  buffer_size = static_cast<snd_pcm_uframes_t>(n_available * channels);
  buffer = std::unique_ptr<int16_t[]>(new int16_t[buffer_size]);

  /* Why does Boost.Asio make it so hard to register a set of of standard
     poll() descriptors (struct pollfd)? */
  int poll_fds_count = snd_pcm_poll_descriptors_count(new_alsa_handle.get());
  if (poll_fds_count < 1) {
    LogFormat("snd_pcm_poll_descriptors_count(0x%p) returned %d",
              new_alsa_handle.get(),
              poll_fds_count);
    return false;
  }

  std::unique_ptr<struct pollfd[]> poll_fds(
      new struct pollfd[poll_fds_count]);
  BOOST_VERIFY(
      poll_fds_count ==
          snd_pcm_poll_descriptors(new_alsa_handle.get(),
                                   poll_fds.get(),
                                   static_cast<unsigned>(poll_fds_count)));

  for (int i = 0; i < poll_fds_count; ++i) {
    if ((poll_fds[i].events & POLLIN) || (poll_fds[i].events & POLLPRI)) {
      read_poll_descs.emplace_back(
          boost::asio::posix::stream_descriptor(io_service, poll_fds[i].fd));
    }
    if (poll_fds[i].events & POLLOUT) {
      write_poll_descs.emplace_back(
          boost::asio::posix::stream_descriptor(io_service, poll_fds[i].fd));
    }
  }

  source = &_source;
  size_t n_read = FillPCMBuffer(buffer.get(), static_cast<size_t>(n_available));

  if (0 == n_read) {
    LogFormat("ALSA PCMPlayer started with data source which "
                  "does not deliver any data");
    return false;
  }

  if (!WriteFrames(*new_alsa_handle, buffer.get(),
                   static_cast<size_t>(n_available), false))
    return false;

  alsa_handle = std::move(new_alsa_handle);

  StartEventHandling();

  return true;
}