Some changes

This commit is contained in:
WolverinDEV 2021-05-09 12:53:58 +02:00
parent 4976384e4f
commit 8a1a50f286
12 changed files with 109 additions and 72 deletions

2
github

@ -1 +1 @@
Subproject commit a46f54984b7ad9721c01273e77807469cd2c63a9
Subproject commit dda7ad2b270b3c598f43757c0e9aaee1adafa92c

View File

@ -106,7 +106,7 @@ export class NativeAudioPlayer implements AudioBackend {
}
getDefaultDeviceId(): string {
return native.audio.available_devices().find(entry => entry.output_default).device_id;
return native.audio.available_devices().find(entry => entry.output_default)?.device_id || "default";
}
isDeviceRefreshAvailable(): boolean {

View File

@ -111,10 +111,6 @@ bool AudioInput::record(std::string& error) {
return false;
}
if(this->input_recorder->sample_rate() != this->sample_rate()) {
this->resampler_ = std::make_unique<AudioResampler>(this->input_recorder->sample_rate(), this->sample_rate(), this->channel_count_);
}
this->input_recorder->register_consumer(this);
if(!this->input_recorder->start(error)) {
this->input_recorder->remove_consumer(this);
@ -134,6 +130,8 @@ void AudioInput::stop() {
this->input_recorder->remove_consumer(this);
this->input_recorder->stop_if_possible();
this->input_recorder.reset();
this->resampler_ = nullptr;
}
std::vector<std::shared_ptr<AudioInputConsumer>> AudioInput::consumers() {
@ -204,11 +202,24 @@ void AudioInput::allocate_input_buffer_samples(size_t samples) {
}
}
void AudioInput::consume(const void *input, size_t sample_count, size_t channels) {
void AudioInput::consume(const void *input, size_t sample_count, size_t sample_rate, size_t channels) {
constexpr static auto kTempBufferMaxSampleCount{1024 * 8};
float temp_buffer[kTempBufferMaxSampleCount];
/* TODO: Short circuit for silence here */
if(!this->resampler_ || this->current_input_sample_rate != sample_rate) {
log_info(category::audio, tr("Input sample rate changed from {} to {}"), this->resampler_ ? this->resampler_->output_rate() : 0, sample_rate);
this->current_input_sample_rate = sample_rate;
this->resampler_ = std::make_unique<AudioResampler>(this->sample_rate(), sample_rate, this->channel_count());
if(!this->resampler_->valid()) {
log_critical(category::audio, tr("Failed to allocate a new resampler. Audio input will be silent."));
}
}
if(!this->resampler_->valid()) {
return;
}
if(channels != this->channel_count_) {
if(channels < 1 || channels > 2) {
@ -225,25 +236,16 @@ void AudioInput::consume(const void *input, size_t sample_count, size_t channels
input = temp_buffer;
}
if(this->resampler_) {
const auto expected_size = this->resampler_->estimated_output_size(sample_count);
this->allocate_input_buffer_samples(expected_size);
size_t resampled_sample_count{expected_size};
if(!this->resampler_->process(this->input_buffer.write_ptr(), input, sample_count, resampled_sample_count)) {
log_error(category::audio, tr("Failed to resample input audio."));
log_error(category::audio, tr("Failed to resample audio input."));
return;
}
this->input_buffer.advance_write_ptr(resampled_sample_count * this->channel_count_ * sizeof(float));
} else {
this->allocate_input_buffer_samples(sample_count);
const auto sample_byte_size = sample_count * this->channel_count_ * sizeof(float);
memcpy(this->input_buffer.write_ptr(), input, sample_byte_size);
this->input_buffer.advance_write_ptr(sample_byte_size);
}
audio::encode_event_loop->schedule(this->event_loop_entry);
}

View File

@ -69,7 +69,7 @@ namespace tc::audio {
AudioInput* input{nullptr};
};
void consume(const void *, size_t, size_t) override;
void consume(const void *, size_t, size_t, size_t) override;
void process_audio();
void process_audio_chunk(float *);
@ -92,6 +92,8 @@ namespace tc::audio {
std::unique_ptr<AudioResampler> resampler_{nullptr};
std::shared_ptr<AudioDevice> input_device{};
size_t current_input_sample_rate{0};
float volume_{1.f};
void allocate_input_buffer_samples(size_t /* sample count */);

View File

@ -94,7 +94,7 @@ void InputDeviceAudioLevelMeter::stop() {
}
/* Note the parameter order! */
void InputDeviceAudioLevelMeter::consume(const void *buffer, size_t sample_count, size_t channel_count) {
void InputDeviceAudioLevelMeter::consume(const void *buffer, size_t sample_count, size_t /* sample rate */, size_t channel_count) {
this->analyze_buffer((const float*) buffer, channel_count, sample_count);
}

View File

@ -55,7 +55,7 @@ namespace tc::audio {
std::shared_ptr<AudioDevice> target_device{};
std::shared_ptr<AudioDeviceRecord> recorder_instance{};
void consume(const void *, size_t, size_t) override;
void consume(const void *, size_t, size_t, size_t) override;
};
class AudioInputAudioLevelMeter : public AbstractAudioLevelMeter {

View File

@ -11,7 +11,7 @@ namespace tc::audio {
public:
class Consumer {
public:
virtual void consume(const void* /* buffer */, size_t /* samples */, size_t /* channel count */) = 0;
virtual void consume(const void* /* buffer */, size_t /* samples */, size_t /* sample rate */, size_t /* channel count */) = 0;
};
[[nodiscard]] virtual size_t sample_rate() const = 0;

View File

@ -10,8 +10,8 @@
namespace tc::audio::pa {
class PortAudioPlayback : public AudioDevicePlayback {
public:
static constexpr auto kChannelCount{2};
static constexpr auto kSampleRate{48000};
static constexpr auto kDefaultChannelCount{2};
static constexpr std::array<size_t, 2> kSupportedSampleRates{48000, 44100};
explicit PortAudioPlayback(PaDeviceIndex index, const PaDeviceInfo* info);
virtual ~PortAudioPlayback();
@ -28,13 +28,14 @@ namespace tc::audio::pa {
const PaDeviceInfo* info;
PaStream* stream{nullptr};
size_t source_sample_rate{0};
size_t source_channel_count{0};
};
class PortAudioRecord : public AudioDeviceRecord {
public:
static constexpr auto kChannelCount{2};
static constexpr auto kSampleRate{48000};
static constexpr auto kDefaultChannelCount{2};
static constexpr std::array<size_t, 2> kSupportedSampleRates{48000, 44100};
explicit PortAudioRecord(PaDeviceIndex index, const PaDeviceInfo* info);
virtual ~PortAudioRecord();
@ -47,6 +48,7 @@ namespace tc::audio::pa {
private:
void read_callback(const void *input, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags);
size_t source_sample_rate{0};
size_t source_channel_count{0};
PaDeviceIndex index;

View File

@ -49,34 +49,48 @@ bool PortAudioPlayback::impl_start(std::string &error) {
parameters.sampleFormat = paFloat32;
parameters.suggestedLatency = this->info->defaultLowInputLatency;
if(this->info->maxOutputChannels < kChannelCount) {
if(this->info->maxOutputChannels < kDefaultChannelCount) {
parameters.channelCount = (int) 1;
this->source_channel_count = 1;
} else {
parameters.channelCount = (int) kChannelCount;
this->source_channel_count = kChannelCount;
parameters.channelCount = (int) kDefaultChannelCount;
this->source_channel_count = kDefaultChannelCount;
}
log_debug(category::audio, "Opening playback device {} (MaxChannels: {}, Channels: {})", this->info->name, this->info->maxOutputChannels, this->source_channel_count);
for(const auto& sample_rate : kSupportedSampleRates) {
auto err = Pa_OpenStream(
&this->stream,
nullptr,
&parameters,
(double) kSampleRate,
(double) sample_rate,
paFramesPerBufferUnspecified,
paClipOff,
proxied_write_callback,
this
);
log_debug(category::audio, "Open result for playback device {} (MaxChannels: {}, Channels: {}, Sample rate: {}): {}", this->info->name, this->info->maxOutputChannels, this->source_channel_count, sample_rate, err);
if(err == paNoError) {
this->source_sample_rate = sample_rate;
break;
}
if(err != paNoError) {
this->stream = nullptr;
if(err == paInvalidSampleRate) {
/* Try next sample rate */
continue;
}
error = std::string{Pa_GetErrorText(err)} + " (open stream: " + std::to_string(err) + ")";
return false;
}
err = Pa_StartStream(this->stream);
if(!this->stream) {
error = "no supported sample rate found";
return false;
}
auto err = Pa_StartStream(this->stream);
if(err != paNoError) {
error = std::string{Pa_GetErrorText(err)} + "(start stream: " + std::to_string(err) + ")";
err = Pa_CloseStream(this->stream);
@ -101,13 +115,12 @@ void PortAudioPlayback::impl_stop() {
}
size_t PortAudioPlayback::current_sample_rate() const {
/* We currently only support one sample rate */
return (size_t) kSampleRate;
return this->source_sample_rate;
}
void PortAudioPlayback::write_callback(void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) {
(void) timeInfo;
(void) statusFlags;
this->fill_buffer(output, frameCount, kSampleRate, this->source_channel_count);
this->fill_buffer(output, frameCount, this->source_sample_rate, this->source_channel_count);
}

View File

@ -16,7 +16,6 @@ PortAudioRecord::~PortAudioRecord() {
}
bool PortAudioRecord::impl_start(std::string &error) {
//TODO: Detect correct sample rate
{
auto device_info = Pa_GetDeviceInfo(this->index);
if(this->info != device_info) {
@ -42,35 +41,52 @@ bool PortAudioRecord::impl_start(std::string &error) {
PaStreamParameters parameters{};
memset(&parameters, 0, sizeof(parameters));
if(this->info->maxInputChannels < kChannelCount) {
if(this->info->maxInputChannels < kDefaultChannelCount) {
parameters.channelCount = (int) 1;
this->source_channel_count = 1;
} else {
parameters.channelCount = (int) kChannelCount;
this->source_channel_count = kChannelCount;
parameters.channelCount = (int) kDefaultChannelCount;
this->source_channel_count = kDefaultChannelCount;
}
log_debug(category::audio, "Opening record device {} (MaxChannels: {}, Channels: {})", this->info->name, this->info->maxInputChannels, this->source_channel_count);
parameters.device = this->index;
parameters.sampleFormat = paFloat32;
parameters.suggestedLatency = this->info->defaultLowInputLatency;
for(const auto& sample_rate : kSupportedSampleRates) {
auto err = Pa_OpenStream(
&this->stream,
&parameters,
nullptr,
(double) kSampleRate,
(double) sample_rate,
paFramesPerBufferUnspecified,
paClipOff,
proxied_read_callback,
this);
this
);
log_debug(category::audio, "Open result for record device {} (MaxChannels: {}, Channels: {}, Sample rate: {}): {}", this->info->name, this->info->maxInputChannels, this->source_channel_count, sample_rate, err);
if(err == paNoError) {
this->source_sample_rate = sample_rate;
break;
}
if(err != paNoError) {
this->stream = nullptr;
if(err == paInvalidSampleRate) {
/* Try next sample rate */
continue;
}
error = std::string{Pa_GetErrorText(err)} + " (open stream: " + std::to_string(err) + ")";
return false;
}
err = Pa_StartStream(this->stream);
if(!this->stream) {
error = "no supported sample rate found";
return false;
}
auto err = Pa_StartStream(this->stream);
if(err != paNoError) {
error = std::string{Pa_GetErrorText(err)} + "(start stream: " + std::to_string(err) + ")";
err = Pa_CloseStream(this->stream);
@ -98,13 +114,13 @@ void PortAudioRecord::impl_stop() {
}
size_t PortAudioRecord::sample_rate() const {
return kSampleRate;
return this->source_sample_rate;
}
void PortAudioRecord::read_callback(const void *input, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) {
std::lock_guard consumer_lock{this->consumer_lock};
for(auto& consumer : this->_consumers) {
consumer->consume(input, frameCount, this->source_channel_count);
consumer->consume(input, frameCount, this->source_sample_rate, this->source_channel_count);
}
}

View File

@ -190,6 +190,8 @@ namespace tc::audio::sounds {
return false;
}
/* The execute lock must be locked else some internal state could be altered */
auto execute_lock = self->execute_lock(true);
if(self->state_ == PLAYER_STATE_PLAYING) {
log_warn(category::audio, tr("Having an audio underflow while playing a sound."));
} else if(self->state_ == PLAYER_STATE_AWAIT_FINISH) {
@ -204,6 +206,8 @@ namespace tc::audio::sounds {
return;
}
/* The execute lock must be locked else some internal state could be altered */
auto execute_lock = self->execute_lock(true);
if(self->could_enqueue_next_buffer() && self->state_ == PLAYER_STATE_PLAYING) {
audio::decode_event_loop->schedule(self);
}

View File

@ -51,8 +51,6 @@ using namespace std;
bool is_executable(const std::string& file) {
chmod(file.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
/* We chmod above but for some reasons access still fails. This should be fixed but I want to finish the linux client... */
return true;
return access(file.c_str(), F_OK | X_OK) == 0;
}
@ -91,7 +89,7 @@ inline std::string build_callback_info(const std::string& error_id, const std::s
void execute_callback_fail_exit(const std::string& error, const std::string& error_message) {
file::rollback();
if(!is_executable(config::callback_file)) {
if(!is_executable(config::callback_file)) {w
logger::fatal("callback file (%s) is not executable! Ignoring fail callback", config::callback_file.c_str());
logger::flush();
exit(1);