mirror of
https://github.com/badaix/snapcast.git
synced 2025-07-29 06:18:39 +02:00
rename files to snake_case
This commit is contained in:
parent
ea3921d8b7
commit
a30f548a31
87 changed files with 181 additions and 212 deletions
297
client/player/alsa_player.cpp
Normal file
297
client/player/alsa_player.cpp
Normal file
|
@ -0,0 +1,297 @@
|
|||
/***
|
||||
This file is part of snapcast
|
||||
Copyright (C) 2014-2019 Johannes Pohl
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "alsa_player.hpp"
|
||||
#include "common/aixlog.hpp"
|
||||
#include "common/snap_exception.hpp"
|
||||
#include "common/str_compat.hpp"
|
||||
|
||||
//#define BUFFER_TIME 120000
|
||||
#define PERIOD_TIME 30000
|
||||
|
||||
using namespace std;
|
||||
|
||||
AlsaPlayer::AlsaPlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream) : Player(pcmDevice, stream), handle_(nullptr), buff_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::initAlsa()
|
||||
{
|
||||
unsigned int tmp, rate;
|
||||
int pcm, channels;
|
||||
snd_pcm_hw_params_t* params;
|
||||
int buff_size;
|
||||
|
||||
const SampleFormat& format = stream_->getFormat();
|
||||
rate = format.rate;
|
||||
channels = format.channels;
|
||||
|
||||
/* Open the PCM device in playback mode */
|
||||
if ((pcm = snd_pcm_open(&handle_, pcmDevice_.name.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
||||
throw SnapException("Can't open " + pcmDevice_.name + " PCM device: " + snd_strerror(pcm));
|
||||
|
||||
/* struct snd_pcm_playback_info_t pinfo;
|
||||
if ( (pcm = snd_pcm_playback_info( pcm_handle, &pinfo )) < 0 )
|
||||
fprintf( stderr, "Error: playback info error: %s\n", snd_strerror( err ) );
|
||||
printf("buffer: '%d'\n", pinfo.buffer_size);
|
||||
*/
|
||||
/* Allocate parameters object and fill it with default values*/
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
|
||||
if ((pcm = snd_pcm_hw_params_any(handle_, params)) < 0)
|
||||
throw SnapException("Can't fill params: " + string(snd_strerror(pcm)));
|
||||
|
||||
/* Set parameters */
|
||||
if ((pcm = snd_pcm_hw_params_set_access(handle_, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||
throw SnapException("Can't set interleaved mode: " + string(snd_strerror(pcm)));
|
||||
|
||||
snd_pcm_format_t snd_pcm_format;
|
||||
if (format.bits == 8)
|
||||
snd_pcm_format = SND_PCM_FORMAT_S8;
|
||||
else if (format.bits == 16)
|
||||
snd_pcm_format = SND_PCM_FORMAT_S16_LE;
|
||||
else if ((format.bits == 24) && (format.sampleSize == 4))
|
||||
snd_pcm_format = SND_PCM_FORMAT_S24_LE;
|
||||
else if (format.bits == 32)
|
||||
snd_pcm_format = SND_PCM_FORMAT_S32_LE;
|
||||
else
|
||||
throw SnapException("Unsupported sample format: " + cpt::to_string(format.bits));
|
||||
|
||||
pcm = snd_pcm_hw_params_set_format(handle_, params, snd_pcm_format);
|
||||
if (pcm == -EINVAL)
|
||||
{
|
||||
if (snd_pcm_format == SND_PCM_FORMAT_S24_LE)
|
||||
{
|
||||
snd_pcm_format = SND_PCM_FORMAT_S32_LE;
|
||||
volCorrection_ = 256;
|
||||
}
|
||||
if (snd_pcm_format == SND_PCM_FORMAT_S8)
|
||||
{
|
||||
snd_pcm_format = SND_PCM_FORMAT_U8;
|
||||
}
|
||||
}
|
||||
|
||||
pcm = snd_pcm_hw_params_set_format(handle_, params, snd_pcm_format);
|
||||
if (pcm < 0)
|
||||
{
|
||||
cerr << "error: " << pcm << "\n";
|
||||
stringstream ss;
|
||||
ss << "Can't set format: " << string(snd_strerror(pcm)) << ", supported: ";
|
||||
for (int format = 0; format <= (int)SND_PCM_FORMAT_LAST; format++)
|
||||
{
|
||||
snd_pcm_format_t snd_pcm_format = static_cast<snd_pcm_format_t>(format);
|
||||
if (snd_pcm_hw_params_test_format(handle_, params, snd_pcm_format) == 0)
|
||||
ss << snd_pcm_format_name(snd_pcm_format) << " ";
|
||||
}
|
||||
throw SnapException(ss.str());
|
||||
}
|
||||
|
||||
if ((pcm = snd_pcm_hw_params_set_channels(handle_, params, channels)) < 0)
|
||||
throw SnapException("Can't set channels number: " + string(snd_strerror(pcm)));
|
||||
|
||||
if ((pcm = snd_pcm_hw_params_set_rate_near(handle_, params, &rate, nullptr)) < 0)
|
||||
throw SnapException("Can't set rate: " + string(snd_strerror(pcm)));
|
||||
|
||||
unsigned int period_time;
|
||||
snd_pcm_hw_params_get_period_time_max(params, &period_time, nullptr);
|
||||
if (period_time > PERIOD_TIME)
|
||||
period_time = PERIOD_TIME;
|
||||
|
||||
unsigned int buffer_time = 4 * period_time;
|
||||
|
||||
snd_pcm_hw_params_set_period_time_near(handle_, params, &period_time, nullptr);
|
||||
snd_pcm_hw_params_set_buffer_time_near(handle_, params, &buffer_time, nullptr);
|
||||
|
||||
// long unsigned int periodsize = stream_->format.msRate() * 50;//2*rate/50;
|
||||
// if ((pcm = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, params, &periodsize)) < 0)
|
||||
// LOG(ERROR) << "Unable to set buffer size " << (long int)periodsize << ": " << snd_strerror(pcm) << "\n";
|
||||
|
||||
/* Write parameters */
|
||||
if ((pcm = snd_pcm_hw_params(handle_, params)) < 0)
|
||||
throw SnapException("Can't set harware parameters: " + string(snd_strerror(pcm)));
|
||||
|
||||
/* Resume information */
|
||||
LOG(DEBUG) << "PCM name: " << snd_pcm_name(handle_) << "\n";
|
||||
LOG(DEBUG) << "PCM state: " << snd_pcm_state_name(snd_pcm_state(handle_)) << "\n";
|
||||
snd_pcm_hw_params_get_channels(params, &tmp);
|
||||
LOG(DEBUG) << "channels: " << tmp << "\n";
|
||||
|
||||
snd_pcm_hw_params_get_rate(params, &tmp, nullptr);
|
||||
LOG(DEBUG) << "rate: " << tmp << " bps\n";
|
||||
|
||||
/* Allocate buffer to hold single period */
|
||||
snd_pcm_hw_params_get_period_size(params, &frames_, nullptr);
|
||||
LOG(INFO) << "frames: " << frames_ << "\n";
|
||||
|
||||
buff_size = frames_ * format.frameSize; // channels * 2 /* 2 -> sample size */;
|
||||
buff_ = (char*)malloc(buff_size);
|
||||
|
||||
snd_pcm_hw_params_get_period_time(params, &tmp, nullptr);
|
||||
LOG(DEBUG) << "period time: " << tmp << "\n";
|
||||
|
||||
snd_pcm_sw_params_t* swparams;
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
snd_pcm_sw_params_current(handle_, swparams);
|
||||
|
||||
snd_pcm_sw_params_set_avail_min(handle_, swparams, frames_);
|
||||
snd_pcm_sw_params_set_start_threshold(handle_, swparams, frames_);
|
||||
// snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, frames_);
|
||||
snd_pcm_sw_params(handle_, swparams);
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::uninitAlsa()
|
||||
{
|
||||
if (handle_ != nullptr)
|
||||
{
|
||||
snd_pcm_drain(handle_);
|
||||
snd_pcm_close(handle_);
|
||||
handle_ = nullptr;
|
||||
}
|
||||
|
||||
if (buff_ != nullptr)
|
||||
{
|
||||
free(buff_);
|
||||
buff_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::start()
|
||||
{
|
||||
initAlsa();
|
||||
Player::start();
|
||||
}
|
||||
|
||||
|
||||
AlsaPlayer::~AlsaPlayer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::stop()
|
||||
{
|
||||
Player::stop();
|
||||
uninitAlsa();
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::worker()
|
||||
{
|
||||
snd_pcm_sframes_t pcm;
|
||||
snd_pcm_sframes_t framesDelay;
|
||||
long lastChunkTick = chronos::getTickCount();
|
||||
|
||||
while (active_)
|
||||
{
|
||||
if (handle_ == nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
initAlsa();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(ERROR) << "Exception in initAlsa: " << e.what() << endl;
|
||||
chronos::sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
// snd_pcm_avail_delay(handle_, &framesAvail, &framesDelay);
|
||||
snd_pcm_delay(handle_, &framesDelay);
|
||||
chronos::usec delay((chronos::usec::rep)(1000 * (double)framesDelay / stream_->getFormat().msRate()));
|
||||
// LOG(INFO) << "delay: " << framesDelay << ", delay[ms]: " << delay.count() / 1000 << "\n";
|
||||
|
||||
if (stream_->getPlayerChunk(buff_, delay, frames_))
|
||||
{
|
||||
lastChunkTick = chronos::getTickCount();
|
||||
adjustVolume(buff_, frames_);
|
||||
if ((pcm = snd_pcm_writei(handle_, buff_, frames_)) == -EPIPE)
|
||||
{
|
||||
LOG(ERROR) << "XRUN\n";
|
||||
snd_pcm_prepare(handle_);
|
||||
}
|
||||
else if (pcm < 0)
|
||||
{
|
||||
LOG(ERROR) << "ERROR. Can't write to PCM device: " << snd_strerror(pcm) << "\n";
|
||||
uninitAlsa();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(INFO) << "Failed to get chunk\n";
|
||||
while (active_ && !stream_->waitForChunk(100))
|
||||
{
|
||||
LOG(DEBUG) << "Waiting for chunk\n";
|
||||
if ((handle_ != nullptr) && (chronos::getTickCount() - lastChunkTick > 5000))
|
||||
{
|
||||
LOG(NOTICE) << "No chunk received for 5000ms. Closing ALSA.\n";
|
||||
uninitAlsa();
|
||||
stream_->clearChunks();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
vector<PcmDevice> AlsaPlayer::pcm_list()
|
||||
{
|
||||
void **hints, **n;
|
||||
char *name, *descr, *io;
|
||||
vector<PcmDevice> result;
|
||||
PcmDevice pcmDevice;
|
||||
|
||||
if (snd_device_name_hint(-1, "pcm", &hints) < 0)
|
||||
return result;
|
||||
n = hints;
|
||||
size_t idx(0);
|
||||
while (*n != nullptr)
|
||||
{
|
||||
name = snd_device_name_get_hint(*n, "NAME");
|
||||
descr = snd_device_name_get_hint(*n, "DESC");
|
||||
io = snd_device_name_get_hint(*n, "IOID");
|
||||
if (io != nullptr && strcmp(io, "Output") != 0)
|
||||
goto __end;
|
||||
pcmDevice.name = name;
|
||||
if (descr == nullptr)
|
||||
{
|
||||
pcmDevice.description = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
pcmDevice.description = descr;
|
||||
}
|
||||
pcmDevice.idx = idx++;
|
||||
result.push_back(pcmDevice);
|
||||
|
||||
__end:
|
||||
if (name != nullptr)
|
||||
free(name);
|
||||
if (descr != nullptr)
|
||||
free(descr);
|
||||
if (io != nullptr)
|
||||
free(io);
|
||||
n++;
|
||||
}
|
||||
snd_device_name_free_hint(hints);
|
||||
return result;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue