Пример #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;
}
Пример #3
0
size_t
PCMMixerDataSource::GetData(int16_t *buffer, size_t n)
{
  size_t copied_count = 0;

  PCMDataSource *sources_to_remove[MAX_MIXER_SOURCES_COUNT];
  unsigned sources_to_remove_count = 0;

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

  for (unsigned i = 0; i < MAX_MIXER_SOURCES_COUNT; ++i) {
    PCMDataSource *source = sources[i];
    if (nullptr != source) {
      bool do_byte_swap = ::IsBigEndian() != source->IsBigEndian();
      if (0 == copied_count) {
        copied_count = source->GetData(buffer, n);
        if (0 == copied_count) {
          sources_to_remove[sources_to_remove_count++] = source;
        } else {
          if (do_byte_swap)
            ByteSwapAndLowerVolume(buffer, copied_count, vol_percent);
          else
            LowerVolume(buffer, copied_count, vol_percent);
        }
      } else {
        int16_t temp_buffer[1024];
        size_t current_src_copied_count = 0;
        bool current_src_end = false;
        while (!current_src_end && (current_src_copied_count < copied_count)) {
          size_t bytes_to_read = std::min(copied_count - current_src_copied_count,
                                          ARRAY_SIZE(temp_buffer));
          size_t iteration_read_count =
              source->GetData(temp_buffer, bytes_to_read);
          if (do_byte_swap)
            ByteSwapAndMixPCM(buffer + current_src_copied_count, temp_buffer,
                              iteration_read_count, vol_percent);
          else
            MixPCM(buffer + current_src_copied_count, temp_buffer,
                   iteration_read_count, vol_percent);
          current_src_copied_count += iteration_read_count;
          current_src_end = (iteration_read_count < bytes_to_read);
        }
        if (!current_src_end && (copied_count < n)) {
          size_t bytes_to_read = n - copied_count;
          size_t rest_read_count =
              source->GetData(buffer + copied_count, bytes_to_read);
          if (do_byte_swap)
            ByteSwapAndLowerVolume(buffer + copied_count, rest_read_count,
                                   vol_percent);
          else
            LowerVolume(buffer + copied_count, rest_read_count,
                        vol_percent);
          copied_count += rest_read_count;
          current_src_end = (rest_read_count < bytes_to_read);
        }
        if (current_src_end) {
          sources_to_remove[sources_to_remove_count++] = source;
        }
      }
    }
  }

  if (sources_to_remove_count > 0) {
    for (unsigned i = 0; i < sources_to_remove_count; ++i) {
      for (unsigned j = 0; j < MAX_MIXER_SOURCES_COUNT; ++j) {
        if (sources_to_remove[i] == sources[j])
          sources[j] = nullptr;
      }
    }
  }

  return copied_count;
}