diff --git a/client/audiodevs/PipeWire/pipewire.c b/client/audiodevs/PipeWire/pipewire.c index 174f6daf..c69285e4 100644 --- a/client/audiodevs/PipeWire/pipewire.c +++ b/client/audiodevs/PipeWire/pipewire.c @@ -200,7 +200,7 @@ static void pipewire_start(int channels, int sampleRate) pw_thread_loop_unlock(pw.thread); } -static void pipewire_play(uint8_t * data, int size) +static void pipewire_play(uint8_t * data, size_t size) { if (!pw.stream) return; diff --git a/client/audiodevs/PulseAudio/pulseaudio.c b/client/audiodevs/PulseAudio/pulseaudio.c index 3b0dfefc..474d9120 100644 --- a/client/audiodevs/PulseAudio/pulseaudio.c +++ b/client/audiodevs/PulseAudio/pulseaudio.c @@ -288,7 +288,7 @@ static void pulseaudio_start(int channels, int sampleRate) pa_threaded_mainloop_unlock(pa.loop); } -static void pulseaudio_play(uint8_t * data, int size) +static void pulseaudio_play(uint8_t * data, size_t size) { if (!pa.sink) return; diff --git a/client/include/interface/audiodev.h b/client/include/interface/audiodev.h index a573b67a..5e5fb3f4 100644 --- a/client/include/interface/audiodev.h +++ b/client/include/interface/audiodev.h @@ -23,6 +23,7 @@ #include #include +#include struct LG_AudioDevOps { @@ -48,7 +49,7 @@ struct LG_AudioDevOps /* called for each packet of output audio to play * Note: size is the size of data in bytes, not frames/samples */ - void (*play)(uint8_t * data, int size); + void (*play)(uint8_t * data, size_t size); /* called when SPICE reports the audio stream has stopped */ void (*stop)(void); @@ -67,7 +68,7 @@ struct LG_AudioDevOps * Note: currently SPICE only supports S16 samples so always assume so */ void (*start)(int channels, int sampleRate, - void (*dataFn)(uint8_t * data, int size)); + void (*dataFn)(uint8_t * data, size_t size)); /* called when SPICE reports the audio stream has stopped */ void (*stop)(void); diff --git a/client/src/audio.c b/client/src/audio.c index 743226ab..5e284edc 100644 --- a/client/src/audio.c +++ b/client/src/audio.c @@ -45,6 +45,7 @@ typedef struct int volumeChannels; uint16_t volume[8]; bool mute; + uint32_t time; } record; } @@ -75,6 +76,11 @@ void audio_free(void) audio.audioDev = NULL; } +bool audio_supportsPlayback(void) +{ + return audio.audioDev && audio.audioDev->playback.start; +} + void audio_playbackStart(int channels, int sampleRate, PSAudioFormat format, uint32_t time) { @@ -154,3 +160,84 @@ void audio_playbackData(uint8_t * data, size_t size) audio.audioDev->playback.play(data, size); } + +bool audio_supportsRecord(void) +{ + return audio.audioDev && audio.audioDev->record.start; +} + +static void recordData(uint8_t * data, size_t size) +{ + purespice_writeAudio(data, size, 0); +} + +void audio_recordStart(int channels, int sampleRate, PSAudioFormat format) +{ + if (!audio.audioDev) + return; + + static int lastChannels = 0; + static int lastSampleRate = 0; + + if (audio.record.started) + { + if (channels != lastChannels || sampleRate != lastSampleRate) + audio.audioDev->record.stop(); + else + return; + } + + lastChannels = channels; + lastSampleRate = sampleRate; + audio.record.started = true; + + DEBUG_INFO("%d channels @ %dHz", channels, sampleRate); + audio.audioDev->record.start(channels, sampleRate, recordData); + + // if a volume level was stored, set it before we return + if (audio.record.volumeChannels) + audio.audioDev->record.volume( + audio.playback.volumeChannels, + audio.playback.volume); + + // set the inital mute state + audio.audioDev->record.mute(audio.playback.mute); +} + +void audio_recordStop(void) +{ + if (!audio.audioDev || !audio.record.started) + return; + + audio.audioDev->record.stop(); + audio.record.started = false; +} + +void audio_recordVolume(int channels, const uint16_t volume[]) +{ + if (!audio.audioDev || !audio.audioDev->record.volume) + return; + + // store the values so we can restore the state if the stream is restarted + channels = min(ARRAY_LENGTH(audio.record.volume), channels); + memcpy(audio.record.volume, volume, sizeof(uint16_t) * channels); + audio.record.volumeChannels = channels; + + if (!audio.record.started) + return; + + audio.audioDev->record.volume(channels, volume); +} + +void audio_recordMute(bool mute) +{ + if (!audio.audioDev || !audio.audioDev->record.mute) + return; + + // store the value so we can restore it if the stream is restarted + audio.record.mute = mute; + if (!audio.record.started) + return; + + audio.audioDev->record.mute(mute); +} diff --git a/client/src/audio.h b/client/src/audio.h index ed4ae8c6..ec160f1d 100644 --- a/client/src/audio.h +++ b/client/src/audio.h @@ -21,9 +21,16 @@ void audio_init(void); void audio_free(void); +bool audio_supportsPlayback(void); void audio_playbackStart(int channels, int sampleRate, PSAudioFormat format, uint32_t time); void audio_playbackStop(void); void audio_playbackVolume(int channels, const uint16_t volume[]); void audio_playbackMute(bool mute); void audio_playbackData(uint8_t * data, size_t size); + +bool audio_supportsRecord(void); +void audio_recordStart(int channels, int sampleRate, PSAudioFormat format); +void audio_recordStop(void); +void audio_recordVolume(int channels, const uint16_t volume[]); +void audio_recordMute(bool mute); diff --git a/client/src/main.c b/client/src/main.c index fa8f5ba6..145b71cc 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -783,7 +783,6 @@ void spiceReady(void) { // set the intial mouse mode purespice_mouseMode(true); - audio_init(); PSServerInfo info; if (!purespice_getServerInfo(&info)) @@ -807,6 +806,9 @@ void spiceReady(void) int spiceThread(void * arg) { + if (g_params.useSpiceAudio) + audio_init(); + const struct PSConfig config = { .host = g_params.spiceHost, @@ -829,12 +831,20 @@ int spiceThread(void * arg) }, .playback = { - .enable = g_params.useSpiceAudio, + .enable = audio_supportsPlayback(), .start = audio_playbackStart, .volume = audio_playbackVolume, .mute = audio_playbackMute, .stop = audio_playbackStop, .data = audio_playbackData + }, + .record = + { + .enable = audio_supportsRecord(), + .start = audio_recordStart, + .volume = audio_recordVolume, + .mute = audio_recordMute, + .stop = audio_recordStop } };