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; }
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; }