Some changes
This commit is contained in:
parent
4976384e4f
commit
8a1a50f286
2
github
2
github
@ -1 +1 @@
|
||||
Subproject commit a46f54984b7ad9721c01273e77807469cd2c63a9
|
||||
Subproject commit dda7ad2b270b3c598f43757c0e9aaee1adafa92c
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 */);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
¶meters,
|
||||
(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);
|
||||
}
|
@ -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(¶meters, 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,
|
||||
¶meters,
|
||||
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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user