// Copyright 2007-2021 The Mumble Developers. All rights reserved. // Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file at the root of the // Mumble source tree or at . #ifndef MUMBLE_MUMBLE_PULSEAUDIO_H_ #define MUMBLE_MUMBLE_PULSEAUDIO_H_ #include "AudioInput.h" #include "AudioOutput.h" #include #include #include #include #include #include #include #include #include #include #include #include #include struct PulseAttenuation { uint32_t index; QString name; QString stream_restore_id; pa_cvolume normal_volume; pa_cvolume attenuated_volume; }; class PulseAudio : public QObject { private: Q_OBJECT Q_DISABLE_COPY(PulseAudio) protected: QLibrary m_lib; public: bool m_ok; const char *(*get_library_version)(); const char *(*strerror)(int error); void (*operation_unref)(pa_operation *o); int (*cvolume_equal)(const pa_cvolume *a, const pa_cvolume *b); pa_cvolume *(*sw_cvolume_multiply_scalar)(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b); int (*sample_spec_equal)(const pa_sample_spec *a, const pa_sample_spec *b); int (*channel_map_equal)(const pa_channel_map *a, const pa_channel_map *b); pa_proplist *(*proplist_new)(); void (*proplist_free)(pa_proplist *p); const char *(*proplist_gets)(const pa_proplist *p, const char *key); int (*proplist_sets)(pa_proplist *p, const char *key, const char *value); pa_threaded_mainloop *(*threaded_mainloop_new)(); void (*threaded_mainloop_free)(pa_threaded_mainloop *m); int (*threaded_mainloop_start)(pa_threaded_mainloop *m); void (*threaded_mainloop_stop)(pa_threaded_mainloop *m); void (*threaded_mainloop_lock)(pa_threaded_mainloop *m); void (*threaded_mainloop_unlock)(pa_threaded_mainloop *m); pa_mainloop_api *(*threaded_mainloop_get_api)(pa_threaded_mainloop *m); int (*context_errno)(const pa_context *c); pa_context *(*context_new_with_proplist)(pa_mainloop_api *mainloop, const char *name, const pa_proplist *proplist); void (*context_unref)(pa_context *c); int (*context_connect)(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api); void (*context_disconnect)(pa_context *c); pa_operation *(*context_subscribe)(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata); pa_context_state_t (*context_get_state)(const pa_context *c); pa_operation *(*context_get_server_info)(pa_context *c, pa_server_info_cb_t cb, void *userdata); pa_operation *(*context_get_sink_info_by_name)(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata); pa_operation *(*context_get_sink_info_list)(pa_context *c, pa_sink_info_cb_t cb, void *userdata); pa_operation *(*context_get_sink_input_info_list)(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata); pa_operation *(*context_get_source_info_list)(pa_context *c, pa_source_info_cb_t cb, void *userdata); pa_operation *(*context_set_sink_input_volume)(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); void (*context_set_state_callback)(pa_context *c, pa_context_notify_cb_t cb, void *userdata); void (*context_set_subscribe_callback)(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata); pa_stream *(*stream_new)(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map); void (*stream_unref)(pa_stream *s); int (*stream_connect_playback)(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume, pa_stream *sync_stream); int (*stream_connect_record)(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags); int (*stream_disconnect)(pa_stream *s); int (*stream_peek)(pa_stream *p, const void **data, size_t *nbytes); int (*stream_write)(pa_stream *p, const void *data, size_t nbytes, pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek); int (*stream_drop)(pa_stream *p); pa_operation *(*stream_cork)(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata); pa_stream_state_t (*stream_get_state)(const pa_stream *p); pa_context *(*stream_get_context)(const pa_stream *p); const pa_sample_spec *(*stream_get_sample_spec)(pa_stream *s); const pa_channel_map *(*stream_get_channel_map)(pa_stream *s); const pa_buffer_attr *(*stream_get_buffer_attr)(pa_stream *s); void (*stream_set_state_callback)(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata); void (*stream_set_read_callback)(pa_stream *p, pa_stream_request_cb_t cb, void *userdata); void (*stream_set_write_callback)(pa_stream *p, pa_stream_request_cb_t cb, void *userdata); pa_operation *(*ext_stream_restore_read)(pa_context *c, pa_ext_stream_restore_read_cb_t cb, void *userdata); pa_operation *(*ext_stream_restore_write)(pa_context *c, pa_update_mode_t mode, const pa_ext_stream_restore_info data[], unsigned n, int apply_immediately, pa_context_success_cb_t cb, void *userdata); public: PulseAudio(); }; class PulseAudioSystem : public QObject { private: Q_OBJECT Q_DISABLE_COPY(PulseAudioSystem) protected: void wakeup(); PulseAudio m_pulseAudio; pa_context *pacContext; pa_stream *pasInput, *pasOutput, *pasSpeaker; pa_threaded_mainloop *pam; pa_defer_event *pade; bool bSourceDone, bSinkDone, bServerDone, bRunning; QString qsDefaultInput, qsDefaultOutput; int iDelayCache; QString qsOutputCache, qsInputCache, qsEchoCache; bool bEchoMultiCache; QHash< QString, QString > qhEchoMap; QHash< QString, pa_sample_spec > qhSpecMap; QHash< QString, pa_channel_map > qhChanMap; bool bAttenuating; int iRemainingOperations; int iSinkId; QHash< uint32_t, PulseAttenuation > qhVolumes; QList< uint32_t > qlMatchedSinks; QHash< QString, PulseAttenuation > qhUnmatchedSinks; QHash< QString, PulseAttenuation > qhMissingSinks; static void defer_event_callback(pa_mainloop_api *a, pa_defer_event *e, void *userdata); static void context_state_callback(pa_context *c, void *userdata); static void subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); static void sink_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata); static void source_callback(pa_context *c, const pa_source_info *i, int eol, void *userdata); static void server_callback(pa_context *c, const pa_server_info *i, void *userdata); static void sink_info_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata); static void write_stream_callback(pa_stream *s, void *userdata); static void read_stream_callback(pa_stream *s, void *userdata); static void read_callback(pa_stream *s, size_t bytes, void *userdata); static void write_callback(pa_stream *s, size_t bytes, void *userdata); static void volume_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata); static void restore_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata); static void stream_restore_read_callback(pa_context *c, const pa_ext_stream_restore_info *i, int eol, void *userdata); static void restore_volume_success_callback(pa_context *c, int success, void *userdata); void contextCallback(pa_context *c); void eventCallback(pa_mainloop_api *a, pa_defer_event *e); void query(); QString outputDevice() const; QString inputDevice() const; void setVolumes(); PulseAttenuation *getAttenuation(QString stream_restore_id); public: QHash< QString, QString > qhInput; QHash< QString, QString > qhOutput; bool bPulseIsGood; QMutex qmWait; QWaitCondition qwcWait; void wakeup_lock(); PulseAudioSystem(); ~PulseAudioSystem() Q_DECL_OVERRIDE; }; class PulseAudioInput : public AudioInput { friend class PulseAudioSystem; private: Q_OBJECT Q_DISABLE_COPY(PulseAudioInput) protected: QMutex qmMutex; QWaitCondition qwcWait; pa_sample_spec pssMic, pssEcho; public: PulseAudioInput(); ~PulseAudioInput() Q_DECL_OVERRIDE; void run() Q_DECL_OVERRIDE; }; class PulseAudioOutput : public AudioOutput { friend class PulseAudioSystem; private: Q_OBJECT Q_DISABLE_COPY(PulseAudioOutput) protected: QMutex qmMutex; QWaitCondition qwcWait; pa_sample_spec pss; pa_channel_map pcm; public: PulseAudioOutput(); ~PulseAudioOutput() Q_DECL_OVERRIDE; void run() Q_DECL_OVERRIDE; }; #endif