From 29fa4f8d667d543595e652e7402d395d507ba464 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 12 Nov 2024 12:12:31 -0500 Subject: Add user volume, simple client add cli Signed-off-by: Andrew Opalach --- .gitignore | 1 - src/buffer/audio.c | 69 +++++++++++++++++++--------- src/buffer/audio.h | 15 +++++-- src/buffer/video.c | 6 +-- src/buffer/volume.h | 30 ++++++------- src/codec/ffmpeg/decoder.c | 3 +- src/codec/ffmpeg/encoder.c | 9 ++-- src/codec/ffmpeg/encoder.h | 2 +- src/fruits/cmc/cmc.c | 49 +++++++++++++++++--- src/liana/list.c | 41 ++++++++++++----- src/liana/list.h | 2 +- src/liana/vcr.c | 7 +-- src/libclient/client.c | 20 ++++++++- src/libclient/client.h | 5 ++- src/libsink/sink.c | 7 ++- src/libsink/sink.h | 1 + src/mixer/audio_miniaudio.c | 4 +- src/mixer/mixer.c | 36 +++++++++++++++ src/mixer/mixer.h | 3 ++ src/screen/screen.c | 18 +++++--- src/screen/screen.h | 1 + src/server/server.c | 106 +++++++++++++++++++++++--------------------- src/sink/desktop.c | 5 +++ 23 files changed, 306 insertions(+), 134 deletions(-) diff --git a/.gitignore b/.gitignore index 443f273..ac187d4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ compile_commands.json data/ doc/**/*.html env/bin/ -old*/ src/portal/cpy/portal.c src/portal/cpy/portal.h src/portal/py/config.py diff --git a/src/buffer/audio.c b/src/buffer/audio.c index 8d33f79..3abae42 100644 --- a/src/buffer/audio.c +++ b/src/buffer/audio.c @@ -9,12 +9,12 @@ #include "common_internal.h" #include "volume.h" -#define BUFFER_SIZE (6 * 1000000L) -#define BUFFER_MARK_MIN (2.6 * 1000000L) // Must be a most half of the buffer size. -#define BUFFER_MARK_BUFFERED (2.5 * 1000000L) +#define BUFFER_SIZE (9 * 1000000L) +#define BUFFER_MARK_MIN (4.3 * 1000000L) // Must be a most half of the buffer size. +#define BUFFER_MARK_BUFFERED (4.0 * 1000000L) #ifdef CAMU_AUDIO_BUFFER_FADE -#define FADE_STEP(fmt) (1.1f / (fmt)->sample_rate) +#define FADE_STEP(fmt) (1.f / (fmt)->sample_rate) #endif enum { @@ -29,10 +29,11 @@ static void reset_buffer_state(struct camu_audio_buffer *buf) { buf->pts = -1.0; buf->pause = PAUSE_PAUSED; - al_atomic_store(u8)(&buf->unpause, 0, AL_ATOMIC_RELAXED); - buf->volume = 1.f; + al_atomic_store(s32)(&buf->unpause, 0, AL_ATOMIC_RELAXED); + al_atomic_store(s32)(&buf->volume.set, 0, AL_ATOMIC_RELAXED); #ifdef CAMU_AUDIO_BUFFER_FADE - buf->fade_offset = 0; + buf->fade.offset = 0; + buf->fade.volume = 1.f; #endif buf->buffered = false; al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED); @@ -88,6 +89,12 @@ bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_code return true; } +void camu_audio_buffer_set_volume(struct camu_audio_buffer *buf, f32 volume) +{ + al_atomic_store(f32)(&buf->volume.queued, volume, AL_ATOMIC_RELAXED); + al_atomic_add(s32)(&buf->volume.set, 1, AL_ATOMIC_RELAXED); +} + void camu_audio_buffer_set_latency(struct camu_audio_buffer *buf, f64 latency) { buf->latency = latency; @@ -191,7 +198,7 @@ void camu_audio_buffer_push(struct camu_audio_buffer *buf, struct camu_codec_fra void camu_audio_buffer_unpause(struct camu_audio_buffer *buf) { - al_atomic_store(u8)(&buf->unpause, 1, AL_ATOMIC_RELAXED); + al_atomic_add(s32)(&buf->unpause, 1, AL_ATOMIC_RELAXED); } // Not thread-safe, must be called while the buffer is not being read from or written to. @@ -238,12 +245,23 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re return 0; } + if (al_atomic_load(s32)(&buf->volume.set, AL_ATOMIC_ACQUIRE) > 0) { + buf->volume.user = al_atomic_load(f32)(&buf->volume.queued, AL_ATOMIC_RELAXED); +#ifdef CAMU_AUDIO_BUFFER_FADE + if (buf->fade.volume == -1.f) { + buf->fade.volume = buf->volume.user; + } +#endif + al_atomic_sub(s32)(&buf->volume.set, 1, AL_ATOMIC_RELEASE); + } + if (camu_clock_is_paused(buf->clock)) { #ifdef CAMU_AUDIO_BUFFER_FADE if (buf->pause == PAUSE_PLAYING) { buf->pause = PAUSE_FADING; - buf->fade_offset = 0; - } else if (buf->volume == 0.f || buf->pause == PAUSE_PAUSED) { + buf->fade.offset = 0; + } else if (buf->fade.volume == 0.f || buf->pause == PAUSE_PAUSED) { + buf->fade.offset = 0; al_memset(data, 0, req); if (NOT_PAUSED(buf->pause)) { buf->pause = PAUSE_PAUSED; @@ -263,10 +281,10 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re #endif } - if (al_atomic_load(u8)(&buf->unpause, AL_ATOMIC_ACQUIRE)) { + if (al_atomic_load(s32)(&buf->unpause, AL_ATOMIC_ACQUIRE) > 0) { // If pause != PLAYING here, this will cause unexpected behavior. buf->pause = PAUSE_PAUSED; - al_atomic_store(u8)(&buf->unpause, 0, AL_ATOMIC_RELEASE); + al_atomic_sub(s32)(&buf->unpause, 1, AL_ATOMIC_RELEASE); } f64 pts = camu_clock_get_pts(buf->clock, buf->latency); @@ -274,8 +292,8 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re size_t have = al_ring_buffer_occupied(&buf->rb); #ifdef CAMU_AUDIO_BUFFER_FADE // Cut off fade if it's reaching too far. - if (have < buf->fade_offset) have = 0; - else have -= buf->fade_offset; + if (have < buf->fade.offset) have = 0; + else have -= buf->fade.offset; if (buf->pause != PAUSE_FADING && buf->pause != PAUSE_PLAYING) { #endif pts -= buf->pts; @@ -350,8 +368,8 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re if (buf->pause == PAUSE_FADING) { // Peek into the ring buffer. This won't consume the data, so when // we resume it will fade in from the same position the fade out started. - ret = al_ring_buffer_peek(&buf->rb, data, buf->fade_offset, req); - buf->fade_offset += ret; + ret = al_ring_buffer_peek(&buf->rb, data, buf->fade.offset, req); + buf->fade.offset += ret; } else { #endif // Read as much as we determined we can. @@ -361,19 +379,30 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re #ifdef CAMU_AUDIO_BUFFER_FADE } f32 step = 0.f; - if (buf->pause == PAUSE_FADING) { + if (buf->pause == PAUSE_FADING || buf->fade.volume > buf->volume.user) { step = -FADE_STEP(&buf->fmt.req); - } else if (buf->volume < 1.f) { + } else if (buf->fade.volume < buf->volume.user) { step = FADE_STEP(&buf->fmt.req); } - if (step != 0.f || buf->volume != 1.f) { - buf->volume = apply_volume(data, req, &buf->fmt.req, buf->volume, step); + if (step != 0.f || buf->fade.volume != 1.f) { + if (buf->volume.user > 1.f) { + step *= buf->volume.user; + } + buf->fade.volume = apply_volume(data, req, &buf->fmt.req, buf->fade.volume, buf->volume.user, step); + } +#else + if (buf->volume.user != 1.f) { + apply_volume(data, req, &buf->fmt.req, buf->volume.user, 0.f, 0.f); } #endif } // Check if we should request to uncork. +#ifdef CAMU_AUDIO_BUFFER_FADE + if (flow == FLOWING && buf->pause != PAUSE_FADING) { +#else if (flow == FLOWING) { +#endif ret = al_atomic_load(size_t)(&buf->uncork_at, AL_ATOMIC_RELAXED); if (ret && have - req <= ret) { buf->callback(buf->userdata, CAMU_BUFFER_UNCORK); diff --git a/src/buffer/audio.h b/src/buffer/audio.h index 0f84d5a..1d49448 100644 --- a/src/buffer/audio.h +++ b/src/buffer/audio.h @@ -17,7 +17,7 @@ struct camu_audio_buffer { f64 pts; u8 pause; - atomic(u8) unpause; + atomic(s32) unpause; struct camu_clock *clock; bool ignore_desync; f64 latency; @@ -36,9 +36,17 @@ struct camu_audio_buffer { struct camu_peak_buffer peak; atomic(size_t) uncork_at; - f32 volume; + struct { + f32 user; + atomic(f32) queued; + atomic(s32) set; + } volume; + #ifdef CAMU_AUDIO_BUFFER_FADE - size_t fade_offset; + struct { + f32 volume; + size_t offset; + } fade; #endif #ifdef CAMU_MIXER_THREADED @@ -51,6 +59,7 @@ struct camu_audio_buffer { bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *clock, struct camu_mixer *mixer); bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_codec_stream *stream); +void camu_audio_buffer_set_volume(struct camu_audio_buffer *buf, f32 volume); void camu_audio_buffer_set_latency(struct camu_audio_buffer *buf, f64 latency); void camu_audio_buffer_push(struct camu_audio_buffer *buf, struct camu_codec_frame *frame); void camu_audio_buffer_unpause(struct camu_audio_buffer *buf); diff --git a/src/buffer/video.c b/src/buffer/video.c index 91c3687..a8b35c6 100644 --- a/src/buffer/video.c +++ b/src/buffer/video.c @@ -10,9 +10,9 @@ #endif #endif -#define BUFFER_MARK_LOW 0.199998 -#define BUFFER_MARK_BUFFERED 0.133332 -#define BUFFER_MARK_HIGH 0.33333 +#define BUFFER_MARK_LOW ((1.0 / 30.0) * 6) +#define BUFFER_MARK_BUFFERED ((1.0 / 30.0) * 8) +#define BUFFER_MARK_HIGH ((1.0 / 30.0) * 12) #define BUFFER_MARK_RESET (BUFFER_MARK_HIGH * 2.0) bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock, struct camu_renderer *renderer) diff --git a/src/buffer/volume.h b/src/buffer/volume.h index 0cad8d3..d39affc 100644 --- a/src/buffer/volume.h +++ b/src/buffer/volume.h @@ -2,26 +2,24 @@ AL_UNUSED_FUNCTION_PUSH -// https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1AB9U8lJL6yAngGVG6AMKpaAVxYMQ0hwBk8DTAA5dwAjTGIvAFZSAAdUBUJbBmc3Dy9Y%2BMSBPwDgljCIyWjLTGskoQImYgIU909pErKBCqqCHKDQ8KiLSuratIbetv8O/K6igEoLVFdiZHYOAFIAJgBmf2Q3LABqRdXHBQJ8QQA6BD3sRY0AQRX1hk3XHb2Do6wqM4ur27WNrcxdvsqLRUEwCJ9Vpcbt87u8Rttrj5jABZa4ADQgTFI2xCE22%2BIJu0ijm%2BEEWAHYAEKEmm0ukExbE76E0wEACeMUwqCopkxeOMTEBABFtkw9tSiSSbizjOzOdzebjtsYQsKceL8YypdcWYKLsrVXsAGLKwUgA0awlamHkoUTGFrOEBBFI5EASUCmOxStp1puZKp9KD9L9OoJrI5XJ5xj5prVYtWEtDMrlUcV/MNqxFIUtyfDev2BsBJoF23NKstDKZ0Nt9uhjswNGdiOMjh812RyggDGxIOxCDweJb7s9A%2Bxw/RED72wYEzrtxuqfe238BAAHLLtgo1%2BLvkvG9tXKuNwRDzvE3vI8vV1wAGybhR33eLq8Ho%2BCO%2Bb1xPi8vznXwRVmWB8gOfa59yoQ9VyAr9QN/cDX0g1db0kB8ULAiCoMEFCv3Q%2BDL3/A9gVBU8qDgykCMwZd0BmEJ6G2Kg8Io6EbkOME8GQBigOVAUCAIYg8BCVwCEwUxtigBhUH8WgRjnUUYhiWg2WMAA3Fx3FEsjlmobiACp0DBLEtzwAAvUTTwUVhFNEtBXEEbEFG45AEEMAJaGMWz7K45ZtjU1JMGxLStxEmJ5wpZiw3xfhiHE1xuLwNUNA1BKXi3Kz6A8mZBGSlYKOWSk8XC5k6TwSCIBS5ZIm2ZzXMwdzPNPPYhSa7YNEKqliuDPyNLVFs2w7LturYXZ8uCzAYmxDQTiobEuGm%2B14JDW1OppRjJDSlhrLVagUImAzKkZSk8EZZrFrpSzNvo3SWqGzAwPpfaxUiI6Tu2rSJj69tOwgC7rOxI0fAAFRRD1/qBlF0QWiKVopZrpQJYhMAIWYGF89S2DA2GYRuVdthYJh/AgNS8HQMKOvhqLuMew6uGWSRlhO%2B6CSC261TmpKzvxVmWqYBSlNU9HNKAiBHtmunlmxCW0f8yaTg0LguCoKGVsR5HiFRjnodtDgploThIl4TxuF4VBOEcLcZjmAE7h4UgCE0HWpgAaxASJVhOLgAE5PbXenPciOnPdWLhoj1jhJENh3SFNjheAUEANDth2pjgWAkDQTa6HCchKAzmIs4iZBNkMYAuFWeW%2BDoETiHjiAQijkJ/CqNlOFtxvmGINkAHkQm0Up7eN0gM7YQQu4YJSo6wITgEcMRaHjwesHxoxxA4LRSHwRGyhUzAF/XzBVFKYSFlt1c6qjmSQmIZvnCwKP%2BLwFhW94HfiBCeJMCFTBl%2BAGSjGTvgBhgAKAAGp4EwAAdy7pyI2tt%2BCCBEGIdgUgZCCEUCodQa8dAgAlgYf%2BpgPL6EEvHSAUxUAxBsAIBeABaQ46AmrIG9tsahXcfLUPxnMc4WYTIMFfmwlgyMAhNR4Xwk2r8BJYBIRAKYjRKGeAgA4fonguCkF8CMPIBQ9BxASHIpRWjMhyPaBoroKjZHlCGHo0xdV%2B7mNaEYzoERTEWPRsonodj1EOIkDIy28wvH6H1pHLB0dODbFUGuW81CULVTwcAbYZc5Ye3ErgQgJARrBwmLwAeWg5ykBdpIW8Jw1waHlpEckGhJDkkiBoW8a5yT%2BPDoE9eMc44JyTlgnJYdliNJNpwTJycpivwSHYSQQA - -static inline f32 apply_volume_f32(f32 *data, size_t sample_count, s32 channel_count, f32 volume, f32 step) +static inline f32 apply_volume_f32(f32 *data, size_t sample_count, s32 channel_count, f32 volume, f32 user, f32 step) { for (u32 i = 0; i < sample_count; i++) { - if (i % channel_count == 0) { - volume = AL_CLAMP(volume + step, 0.f, 1.f); + if (step != 0.f && i % channel_count == 0) { + volume = AL_CLAMP(volume + step, 0.f, user); } f64 sample = (f64)data[i]; sample *= volume; - data[i] = (f32)AL_CLAMP(sample, FLT_MIN, FLT_MAX); + data[i] = (f32)AL_CLAMP(sample, -1.0, 1.0); } return volume; } -static inline f32 apply_volume_s32(s32 *data, size_t sample_count, s32 channel_count, f32 volume, f32 step) +static inline f32 apply_volume_s32(s32 *data, size_t sample_count, s32 channel_count, f32 volume, f32 user, f32 step) { for (u32 i = 0; i < sample_count; i++) { - if (i % channel_count == 0) { - volume = AL_CLAMP(volume + step, 0.f, 1.f); + if (step != 0.f && i % channel_count == 0) { + volume = AL_CLAMP(volume + step, 0.f, user); } f64 sample = (f64)data[i]; sample *= volume; @@ -30,11 +28,11 @@ static inline f32 apply_volume_s32(s32 *data, size_t sample_count, s32 channel_c return volume; } -static inline f32 apply_volume_s16(s16 *data, size_t sample_count, s32 channel_count, f32 volume, f32 step) +static inline f32 apply_volume_s16(s16 *data, size_t sample_count, s32 channel_count, f32 volume, f32 user, f32 step) { for (u32 i = 0; i < sample_count; i++) { - if (i % channel_count == 0) { - volume = AL_CLAMP(volume + step, 0.f, 1.f); + if (step != 0.f && i % channel_count == 0) { + volume = AL_CLAMP(volume + step, 0.f, user); } f64 sample = (f64)data[i]; sample *= volume; @@ -43,22 +41,22 @@ static inline f32 apply_volume_s16(s16 *data, size_t sample_count, s32 channel_c return volume; } -static inline f32 apply_volume(u8 *data, size_t size, struct camu_audio_format *fmt, f32 volume, f32 step) +static inline f32 apply_volume(u8 *data, size_t size, struct camu_audio_format *fmt, f32 volume, f32 user, f32 step) { size_t sample_count; switch (camu_audio_format_bytes_per_sample(fmt)) { case 4: sample_count = size / 4; if (fmt->format == CAMU_SAMPLE_FORMAT_S32) { - return apply_volume_s32((s32 *)data, sample_count, fmt->channel_count, volume, step); + return apply_volume_s32((s32 *)data, sample_count, fmt->channel_count, volume, user, step); } else if (fmt->format == CAMU_SAMPLE_FORMAT_FLT) { - return apply_volume_f32((f32 *)data, sample_count, fmt->channel_count, volume, step); + return apply_volume_f32((f32 *)data, sample_count, fmt->channel_count, volume, user, step); } break; case 2: sample_count = size / 2; if (fmt->format == CAMU_SAMPLE_FORMAT_S16) { - return apply_volume_s16((s16 *)data, sample_count, fmt->channel_count, volume, step); + return apply_volume_s16((s16 *)data, sample_count, fmt->channel_count, volume, user, step); } break; } diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c index 03940a0..d3ab3b2 100644 --- a/src/codec/ffmpeg/decoder.c +++ b/src/codec/ffmpeg/decoder.c @@ -43,8 +43,7 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend av->codec_context->thread_type = FF_THREAD_SLICE; al_log_debug("ff_decoder", "Using %i threads for decoder.", cpus); -//#ifndef CAMU_SINK_NO_VIDEO -#if 0 +#if !defined CAMU_SINK_NO_VIDEO && defined CAMU_RENDERER_VULKAN if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO && renderer && renderer->get_buffer2) { av->codec_context->get_buffer2 = renderer->get_buffer2; av->codec_context->opaque = renderer->opaque; diff --git a/src/codec/ffmpeg/encoder.c b/src/codec/ffmpeg/encoder.c index 83a8b55..11aaaff 100644 --- a/src/codec/ffmpeg/encoder.c +++ b/src/codec/ffmpeg/encoder.c @@ -11,7 +11,6 @@ #include "encoder.h" -#define BITRATE 256000 #define FRAME_DURATION 20 #define SAMPLES_PER_FRAME (48000 / 1000.0 * FRAME_DURATION) #define CHANNELS 2 @@ -21,7 +20,7 @@ static void free_frame_data(struct camu_ff_encoder *enc) if (enc->frame && enc->frame->nb_samples > 0) av_freep(&enc->frame->data[0]); } -bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext) +bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext, u32 bitrate) { al_memset(enc, 0, sizeof(struct camu_ff_encoder)); @@ -42,7 +41,7 @@ bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char enc->codec_context->sample_rate = 48000; av_channel_layout_default(&enc->codec_context->ch_layout, CHANNELS); enc->codec_context->sample_fmt = AV_SAMPLE_FMT_FLT; - enc->codec_context->bit_rate = BITRATE; + enc->codec_context->bit_rate = bitrate; enc->codec_context->frame_size = SAMPLES_PER_FRAME; enc->codec_context->time_base = (AVRational){ 1, enc->codec_context->sample_rate }; enc->codec_context->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; @@ -53,7 +52,9 @@ bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char } AVDictionary *opts = NULL; - av_dict_set(&opts, "b", "256k", 0); + char kbps[16]; + al_snprintf(kbps, sizeof(kbps), "%uk", bitrate / 1000); + av_dict_set(&opts, "b", kbps, 0); av_dict_set(&opts, "vbr", "off", 0); av_dict_set(&opts, "compression_level", "10", 0); av_dict_set(&opts, "frame_duration", "20", 0); diff --git a/src/codec/ffmpeg/encoder.h b/src/codec/ffmpeg/encoder.h index c0d953c..238403f 100644 --- a/src/codec/ffmpeg/encoder.h +++ b/src/codec/ffmpeg/encoder.h @@ -18,7 +18,7 @@ struct camu_ff_encoder { void *userdata; }; -bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext); +bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext, u32 bitrate); void camu_ff_encoder_push(struct camu_ff_encoder *enc, u8 **data, s32 sample_count); void camu_ff_encoder_reset(struct camu_ff_encoder *enc); void camu_ff_encoder_close(struct camu_ff_encoder *enc); diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c index ad7c75e..d39d639 100644 --- a/src/fruits/cmc/cmc.c +++ b/src/fruits/cmc/cmc.c @@ -8,8 +8,14 @@ #include "cmc.h" +enum { + CLI_ADD = 0 +}; + struct cmc { struct aki_event_loop loop; + u8 command; + array(str) args; struct camu_client client; struct camu_post_cache cache; array(struct cmc_search *) searches; @@ -29,7 +35,16 @@ static void client_callback(void *userdata, u8 op, void *opaque) struct cmc *c = (struct cmc *)userdata; switch (op) { case CAMU_CLIENT_LOGIN: { - camu_client_create_search(&c->client, al_str_c("youtube"), al_str_c("")); + switch (c->command) { + case CLI_ADD: { + str *arg; + al_array_foreach_ptr(c->args, i, arg) { + camu_client_add_from_path(&c->client, al_str_c("default"), arg); + } + camu_client_disconnect(&c->client); + break; + } + } break; } case CAMU_CLIENT_SEARCH_CREATED: { @@ -62,7 +77,7 @@ static void client_callback(void *userdata, u8 op, void *opaque) al_array_push(page->list, s); } al_array_push(search->pages, page); - camu_client_add(&c->client, al_str_c("default"), &al_array_at(page->list, 0), 0); + camu_client_add_from_post(&c->client, al_str_c("default"), &al_array_at(page->list, 0), 0); break; } } @@ -70,22 +85,46 @@ static void client_callback(void *userdata, u8 op, void *opaque) static struct cmc c = { 0 }; +#ifndef _WIN32 +static bool parse_cmd(s32 argc, char *argv[]) +#else +static bool parse_cmd(s32 argc, wchar_t **argv) +#endif +{ + if (argc < 2) return false; + if (al_str_eq(al_str_cr(argv[1]), al_str_c("add"))) { + if (argc < 3) return false; + c.command = CLI_ADD; + char path[PATH_MAX]; + str arg; + for (s32 i = 2; i < argc; i++) { + if (realpath(argv[i], path)) { + al_str_clone(&arg, al_str_cr(path)); + al_array_push(c.args, arg); + } + } + } + return true; +} + + #ifndef _WIN32 s32 main(s32 argc, char *argv[]) #else s32 wmain(s32 argc, wchar_t **argv) #endif { - (void)argc; - (void)argv; if (!aki_common_init()) return EXIT_FAILURE; aki_event_loop_init(&c.loop); - camu_post_cache_init(&c.cache); + al_array_init(c.args); + camu_post_cache_init(&c.cache); al_array_init(c.searches); + if (!parse_cmd(argc, argv)) return EXIT_FAILURE; + c.client.callback = client_callback; c.client.userdata = &c; if (!camu_client_login(&c.client, &c.loop, CAMU_LOCAL_TYPE, CAMU_LOCAL_ADDR, CAMU_PORT, al_str_c("andrew"))) { diff --git a/src/liana/list.c b/src/liana/list.c index df91140..65efc9d 100644 --- a/src/liana/list.c +++ b/src/liana/list.c @@ -52,6 +52,8 @@ void lia_list_init(struct lia_list *list, str *name) list->cmd = NULL; } +static void pump_queue(struct lia_list *list); + static bool assume_ended(struct lia_list_entry *entry, u64 at) { if (entry->duration == 0) return true; @@ -177,18 +179,32 @@ static bool handle_add(struct lia_list *list, struct lia_list_entry *entry) return true; } -static void unset_all(struct lia_list *list) + +static void unset_all(struct lia_list *list, struct lia_list_entry *previous) { struct lia_list_sink *sink; al_array_foreach(list->sinks, i, sink) { sink->set = -1; sink->queued = -1; } + if (previous) { + list->cmd->op = SKIPTO; + struct lia_list_entry *entry; + al_array_foreach(list->entries, i, entry) { + if (entry->opaque == previous->opaque) { + list->cmd->i = list->current; + list->current = i; + list->cmd->sequence = i; + break; + } + } + pump_queue(list); + } } static void handle_unset(struct lia_list *list) { - unset_all(list); + unset_all(list, NULL); list->current = list->entries.size - 1; list->previous = -1; list->queued = -1; @@ -405,30 +421,33 @@ static void handle_end(struct lia_list *list, s32 sequence) static void handle_reverse(struct lia_list *list) { + struct lia_list_entry *previous = al_array_at(list->entries, list->current); u32 size = list->entries.size; for (u32 i = 0; i < size; i++) { u32 tail = size - (i + 1); if (tail <= i) break; AL_SWAP(al_array_at(list->entries, i), al_array_at(list->entries, tail), struct lia_list_entry *); } - unset_all(list); + unset_all(list, previous); } static void handle_sort(struct lia_list *list) { + struct lia_list_entry *previous = al_array_at(list->entries, list->current); al_array_sort(list->entries, struct lia_list_entry *, camu_db_compare); - unset_all(list); + unset_all(list, previous); } static void handle_shuffle(struct lia_list *list) { + struct lia_list_entry *previous = al_array_at(list->entries, list->current); u32 size = list->entries.size; if (size == 0) return; for (u32 i = 0; i < size - 1; i++) { u32 j = i + al_rand() / (AL_RAND_MAX / (size - i) + 1); AL_SWAP(al_array_at(list->entries, i), al_array_at(list->entries, j), struct lia_list_entry *); } - unset_all(list); + unset_all(list, previous); } static void handle_clear(struct lia_list *list) @@ -439,13 +458,13 @@ static void handle_clear(struct lia_list *list) al_free(entry); } list->entries.size = 0; - unset_all(list); + unset_all(list, NULL); list->current = -1; list->previous = -1; list->idle = true; } -static void pump_queue(struct lia_list *list) +void pump_queue(struct lia_list *list) { if (!list->cmd) { if (list->queue.size == 0) return; @@ -490,16 +509,16 @@ static void pump_queue(struct lia_list *list) break; case REVERSE: handle_reverse(list); - break; + return; case SORT: handle_sort(list); - break; + return; case SHUFFLE: handle_shuffle(list); - break; + return; case CLEAR: handle_clear(list); - break; + return; } al_free(cmd); list->cmd = NULL; diff --git a/src/liana/list.h b/src/liana/list.h index 4f56641..7a24104 100644 --- a/src/liana/list.h +++ b/src/liana/list.h @@ -7,7 +7,7 @@ #define LIANA_SEQUENCE_ANY -1 #define LIANA_TIMESTAMP_INVALID UINT64_MAX -#define LIANA_BASE_DELAY 475000Lu // 475ms +#define LIANA_BASE_DELAY 575000Lu // 575ms #define LIANA_BASE_PING 150000Lu // 150ms #define LIANA_PAUSE_DELAY LIANA_BASE_PING #define LIANA_DELAY_IGNORE 0Lu diff --git a/src/liana/vcr.c b/src/liana/vcr.c index 0f3eac4..213d5cb 100644 --- a/src/liana/vcr.c +++ b/src/liana/vcr.c @@ -109,9 +109,9 @@ static void cork_if_buffered(struct lia_vcr *vcr) { u8 buffered = 1; struct lia_vcr_track *track; - //al_array_foreach(vcr->tracks, i, track) { - // buffered &= al_atomic_load(u8)(&track->buffered, AL_ATOMIC_RELAXED); - //} + al_array_foreach(vcr->tracks, i, track) { + buffered &= al_atomic_load(u8)(&track->buffered, AL_ATOMIC_RELAXED); + } if (buffered) { aki_packet_stream_cork(vcr->data, true); al_array_foreach(vcr->tracks, i, track) { @@ -140,6 +140,7 @@ bool lia_vcr_push_packet(struct lia_vcr *vcr, struct aki_packet *packet) if (!aki_packet_cache_send_packet(&track->cache, packet)) { aki_packet_free(packet); } else if ((count = al_atomic_add(s32)(&vcr->count, 1, AL_ATOMIC_RELAXED)) > vcr->mark.buffered) { + vcr->mark.buffered = count; cork_if_buffered(vcr); } break; diff --git a/src/libclient/client.c b/src/libclient/client.c index 0776842..d7a2325 100644 --- a/src/libclient/client.c +++ b/src/libclient/client.c @@ -50,7 +50,7 @@ static void connection_callback(void *userdata, struct aki_rpc_connection *conn) static void connection_closed_callback(void *userdata, struct aki_rpc_connection *conn) { struct camu_client *client = (struct camu_client *)userdata; - al_assert(client->conn == conn); + al_assert(client->conn == NULL || client->conn == conn); client->conn = NULL; } @@ -59,6 +59,7 @@ bool camu_client_login(struct camu_client *client, struct aki_event_loop *loop, { client->loop = loop; al_str_clone(&client->username, username); + client->conn = NULL; aki_rpc_init(&client->client, client->loop, connection_callback, connection_closed_callback, client); for (u32 i = 0; i < AL_ARRAY_SIZE(commands); i++) { commands[i].userdata = client; @@ -109,7 +110,17 @@ void camu_client_get_page(struct camu_client *client, s32 id, u32 num) aki_rpc_connection_command(client->conn, packet, NULL, NULL); } -void camu_client_add(struct camu_client *client, str *list, str *unique_id, u32 index) +void camu_client_add_from_path(struct camu_client *client, str *list, str *path) +{ + struct aki_packet *packet = aki_rpc_get_packet(&client->client, CAMU_SERVER_LIST_ACTION); + aki_packet_write_str(packet, list); + aki_packet_write_u8(packet, CAMU_LIST_ADD); + aki_packet_write_u8(packet, CAMU_RESOURCE_FILE); + aki_packet_write_str(packet, path); + aki_rpc_connection_command(client->conn, packet, NULL, NULL); +} + +void camu_client_add_from_post(struct camu_client *client, str *list, str *unique_id, u32 index) { struct aki_packet *packet = aki_rpc_get_packet(&client->client, CAMU_SERVER_LIST_ACTION); aki_packet_write_str(packet, list); @@ -119,3 +130,8 @@ void camu_client_add(struct camu_client *client, str *list, str *unique_id, u32 aki_packet_write_u32(packet, index); aki_rpc_connection_command(client->conn, packet, NULL, NULL); } + +void camu_client_disconnect(struct camu_client *client) +{ + aki_rpc_conn_flush(client->conn); +} diff --git a/src/libclient/client.h b/src/libclient/client.h index 02a6639..c073c9e 100644 --- a/src/libclient/client.h +++ b/src/libclient/client.h @@ -28,4 +28,7 @@ void camu_client_toggle_sink(struct camu_client *client, str *sink, str *list, b void camu_client_create_search(struct camu_client *client, str *module, str *query); void camu_client_get_page(struct camu_client *client, s32 id, u32 num); -void camu_client_add(struct camu_client *client, str *list, str *unique_id, u32 index); +void camu_client_add_from_path(struct camu_client *client, str *list, str *path); +void camu_client_add_from_post(struct camu_client *client, str *list, str *unique_id, u32 index); + +void camu_client_disconnect(struct camu_client *client); diff --git a/src/libsink/sink.c b/src/libsink/sink.c index ee2b216..894dc40 100644 --- a/src/libsink/sink.c +++ b/src/libsink/sink.c @@ -1051,6 +1051,7 @@ static void connection_callback(void *userdata, struct aki_rpc_connection *conn) { struct camu_sink *sink = (struct camu_sink *)userdata; sink->conn = conn; + aki_timer_stop(&sink->reconnect_timer); struct aki_packet *packet = aki_rpc_get_packet(&sink->client, CAMU_SERVER_IDENTIFY); aki_packet_write_u8(packet, CAMU_SINK); aki_packet_write_str(packet, &sink->name); @@ -1062,7 +1063,6 @@ static void reconnect_timer_callback(void *userdata, struct aki_timer *timer) struct camu_sink *sink = (struct camu_sink *)userdata; (void)timer; aki_rpc_reconnect(&sink->client, &sink->addr, sink->port); - aki_timer_stop(&sink->reconnect_timer); } static void connection_closed_callback(void *userdata, struct aki_rpc_connection *conn) @@ -1165,6 +1165,11 @@ void camu_sink_reseek(struct camu_sink *sink) }); } +void camu_sink_offset_volume(struct camu_sink *sink, f64 amount) +{ + camu_mixer_offset_volume(sink->audio.mixer, amount); +} + void camu_sink_stop(struct camu_sink *sink) { aki_mutex_lock(&sink->mutex); diff --git a/src/libsink/sink.h b/src/libsink/sink.h index 0b55ad6..5feadf5 100644 --- a/src/libsink/sink.h +++ b/src/libsink/sink.h @@ -112,6 +112,7 @@ void camu_sink_shuffle(struct camu_sink *sink); void camu_sink_toggle_pause(struct camu_sink *sink); void camu_sink_seek(struct camu_sink *sink, f64 pos); void camu_sink_reseek(struct camu_sink *sink); +void camu_sink_offset_volume(struct camu_sink *sink, f64 amount); void camu_sink_stop(struct camu_sink *sink); void camu_sink_close(struct camu_sink *sink); void camu_sink_free(struct camu_sink *sink); diff --git a/src/mixer/audio_miniaudio.c b/src/mixer/audio_miniaudio.c index 2a93491..7db1b5a 100644 --- a/src/mixer/audio_miniaudio.c +++ b/src/mixer/audio_miniaudio.c @@ -158,9 +158,9 @@ static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opa ma->config.performanceProfile = ma_performance_profile_conservative; ma->config.periods = DEFAULT_PERIODS; ma->config.periodSizeInMilliseconds = DEFAULT_PERIOD_SIZE_IN_MILLISECONDS; - ma->config.playback.format = ma_format_s32; + ma->config.playback.format = ma_format_unknown; ma->config.playback.channels = 2; - ma->config.sampleRate = 44100; + ma->config.sampleRate = 0; ma->config.dataCallback = data_callback; ma->config.pUserData = ma; ma->config.noPreSilencedOutputBuffer = MA_TRUE; diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index 5af63b7..7608efc 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -68,6 +68,7 @@ bool camu_mixer_init(struct camu_mixer *mixer, struct camu_audio *audio) mixer->audio->data_callback = data_callback; mixer->audio->userdata = mixer; mixer->audio->init(mixer->audio, al_str_c("cmv")); + mixer->volume = 1.f; mixer->paused = true; mixer->empty_after = 0; al_array_init(mixer->buffers); @@ -85,6 +86,40 @@ void camu_mixer_pick_format(struct camu_mixer *mixer, struct camu_resampler_form mixer->audio->pick_format(mixer->audio, fmt); } +void camu_mixer_set_volume(struct camu_mixer *mixer, f32 volume) +{ +#ifdef CAMU_MIXER_THREADED + aki_mutex_lock(&mixer->mutex); +#endif + mixer->volume = volume; + struct camu_audio_buffer *buf; + al_array_foreach(mixer->buffers, i, buf) { + camu_audio_buffer_set_volume(buf, mixer->volume); + } +#ifdef CAMU_MIXER_THREADED + aki_mutex_unlock(&mixer->mutex); +#endif +} + +void camu_mixer_offset_volume(struct camu_mixer *mixer, f32 amount) +{ +#ifdef CAMU_MIXER_THREADED + aki_mutex_lock(&mixer->mutex); +#endif + if (FLT_MAX - mixer->volume < amount) { + mixer->volume = FLT_MAX; + } else { + mixer->volume = AL_MAX(0.f, mixer->volume + amount); + } + struct camu_audio_buffer *buf; + al_array_foreach(mixer->buffers, i, buf) { + camu_audio_buffer_set_volume(buf, mixer->volume); + } +#ifdef CAMU_MIXER_THREADED + aki_mutex_unlock(&mixer->mutex); +#endif +} + f64 camu_mixer_get_latency(struct camu_mixer *mixer) { return mixer->audio->get_latency(mixer->audio) / 1000000.0; @@ -92,6 +127,7 @@ f64 camu_mixer_get_latency(struct camu_mixer *mixer) static void add_buffer_internal(struct camu_mixer *mixer, struct camu_audio_buffer *buf) { + camu_audio_buffer_set_volume(buf, mixer->volume); #ifdef CAMU_MIXER_THREADED al_atomic_store(u8)(&buf->ref, 1, AL_ATOMIC_RELAXED); #endif diff --git a/src/mixer/mixer.h b/src/mixer/mixer.h index 9a15751..356e2b1 100644 --- a/src/mixer/mixer.h +++ b/src/mixer/mixer.h @@ -15,6 +15,7 @@ enum { struct camu_mixer { struct camu_audio *audio; + f32 volume; struct camu_resampler_format fmt; bool paused; u8 empty_after; @@ -31,6 +32,8 @@ struct camu_mixer { bool camu_mixer_init(struct camu_mixer *mixer, struct camu_audio *audio); void camu_mixer_pick_format(struct camu_mixer *mixer, struct camu_resampler_format *fmt); +void camu_mixer_set_volume(struct camu_mixer *mixer, f32 volume); +void camu_mixer_offset_volume(struct camu_mixer *mixer, f32 amount); f64 camu_mixer_get_latency(struct camu_mixer *mixer); void camu_mixer_add_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf); void camu_mixer_remove_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf); diff --git a/src/screen/screen.c b/src/screen/screen.c index f949c1d..aa61ba6 100644 --- a/src/screen/screen.c +++ b/src/screen/screen.c @@ -133,15 +133,19 @@ static bool mouse_button_callback(void *userdata, u8 state, u8 button) static bool scroll_callback(void *userdata, f64 y) { struct camu_screen *scr = (struct camu_screen *)userdata; - struct camu_view *view = get_view_from_mouse_pos(scr); - if (view) { - if (scr->flags & CAMU_SCREEN_ZOOM_PAN_SIMPLE) { - y = -y / SCROLL_MULTIPLIER; - if (camu_view_zoom_simple(view, scr->width, scr->height, scr->last_mouse_x, scr->last_mouse_y, y)) { - view->mode = CAMU_VIEW_DETACHED; - return true; + y = -y / SCROLL_MULTIPLIER; + if (!(scr->flags & CAMU_SCREEN_MODIFIER)) { + struct camu_view *view = get_view_from_mouse_pos(scr); + if (view) { + if (scr->flags & CAMU_SCREEN_ZOOM_PAN_SIMPLE) { + if (camu_view_zoom_simple(view, scr->width, scr->height, scr->last_mouse_x, scr->last_mouse_y, y)) { + view->mode = CAMU_VIEW_DETACHED; + return true; + } } } + } else { + scr->callback(scr->userdata, CAMU_SCREEN_VOLUME, &y); } return false; } diff --git a/src/screen/screen.h b/src/screen/screen.h index a10796a..0c56cf9 100644 --- a/src/screen/screen.h +++ b/src/screen/screen.h @@ -34,6 +34,7 @@ enum { CAMU_SCREEN_TOGGLE_PAUSE, CAMU_SCREEN_SEEK, CAMU_SCREEN_RESEEK, + CAMU_SCREEN_VOLUME, CAMU_SCREEN_CLOSE }; diff --git a/src/server/server.c b/src/server/server.c index a77046b..6fc2892 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -188,56 +188,6 @@ void handle_toggle_sink(struct camu_server *server, str *name, struct camu_serve } } -static bool client_command_callback(void *userdata, struct aki_rpc_connection *conn, - struct aki_packet *packet, struct aki_packet *rpacket) -{ - struct camu_server *server = (struct camu_server *)userdata; - (void)rpacket; - - struct camu_server_client *client = get_client_by_connection(server, conn); - if (!client) goto out; - - u8 op = aki_packet_read_u8(packet); - switch (op) { - case CAMU_CLIENT_CREATE_LIST: { - str name; - aki_packet_read_str(packet, &name); - struct lia_list *list = al_alloc_object(struct lia_list); - lia_list_init(list, &name); - al_array_push(server->lists, list); - break; - } - case CAMU_CLIENT_TOGGLE_SINK: { - str name; - aki_packet_read_str(packet, &name); - struct camu_server_sink *sink = get_sink_from_name(server, &name); - if (!sink) goto out; - aki_packet_read_str(packet, &name); // list name. - bool enable = aki_packet_read_bool(packet); - handle_toggle_sink(server, &name, sink, enable); - break; - } - case CAMU_CLIENT_CREATE_SEARCH: { - str module; - aki_packet_read_str(packet, &module); - str query; - aki_packet_read_str(packet, &query); - camu_portal_create_search(&server->bridge, &module, &query, client_portal_callback, conn); - break; - } - case CAMU_CLIENT_GET_PAGE: { - s32 id = aki_packet_read_s32(packet); - u32 num = aki_packet_read_u32(packet); - camu_portal_get_page(&server->bridge, id, num, client_portal_callback, conn); - break; - } - } - -out: - aki_packet_free(packet); - return true; -} - static void node_callback(void *userdata, u8 op, u64 duration) { struct camu_resource *resource = (struct camu_resource *)userdata; @@ -293,6 +243,58 @@ static void list_callback(void *userdata, u8 op, struct lia_list_entry *entry, v } } +static bool client_command_callback(void *userdata, struct aki_rpc_connection *conn, + struct aki_packet *packet, struct aki_packet *rpacket) +{ + struct camu_server *server = (struct camu_server *)userdata; + (void)rpacket; + + struct camu_server_client *client = get_client_by_connection(server, conn); + if (!client) goto out; + + u8 op = aki_packet_read_u8(packet); + switch (op) { + case CAMU_CLIENT_CREATE_LIST: { + str name; + aki_packet_read_str(packet, &name); + struct lia_list *list = al_alloc_object(struct lia_list); + lia_list_init(list, &name); + list->callback = list_callback; + list->userdata = server; + al_array_push(server->lists, list); + break; + } + case CAMU_CLIENT_TOGGLE_SINK: { + str name; + aki_packet_read_str(packet, &name); + struct camu_server_sink *sink = get_sink_from_name(server, &name); + if (!sink) goto out; + aki_packet_read_str(packet, &name); // list name. + bool enable = aki_packet_read_bool(packet); + handle_toggle_sink(server, &name, sink, enable); + break; + } + case CAMU_CLIENT_CREATE_SEARCH: { + str module; + aki_packet_read_str(packet, &module); + str query; + aki_packet_read_str(packet, &query); + camu_portal_create_search(&server->bridge, &module, &query, client_portal_callback, conn); + break; + } + case CAMU_CLIENT_GET_PAGE: { + s32 id = aki_packet_read_s32(packet); + u32 num = aki_packet_read_u32(packet); + camu_portal_get_page(&server->bridge, id, num, client_portal_callback, conn); + break; + } + } + +out: + aki_packet_free(packet); + return true; +} + static void handle_add_command(struct camu_server *server, struct lia_list *list, struct aki_packet *packet) { u8 op = aki_packet_read_u8(packet); @@ -300,11 +302,13 @@ static void handle_add_command(struct camu_server *server, struct lia_list *list case CAMU_RESOURCE_FILE: { str path; aki_packet_read_str(packet, &path); + struct cch_entry *entry = cch_handler_file_create(&path); + if (!entry) return; struct camu_resource_file *resource = al_alloc_object(struct camu_resource_file); resource->r.type = CAMU_RESOURCE_FILE; resource->r.load = CAMU_RESOURCE_NOT_LOADED; al_str_clone(&resource->path, &path); - resource->r.entry = cch_handler_file_create(&path); + resource->r.entry = entry; resource->r.node = lia_server_create_node(&server->data.server, resource->r.entry); resource->r.node->callback = node_callback; resource->r.node->userdata = (struct camu_resource *)resource; diff --git a/src/sink/desktop.c b/src/sink/desktop.c index ddde86b..2e2863f 100644 --- a/src/sink/desktop.c +++ b/src/sink/desktop.c @@ -116,6 +116,11 @@ static void screen_callback(void *userdata, u8 op, void *opaque) case CAMU_SCREEN_RESEEK: camu_sink_reseek(&c->sink); break; + case CAMU_SCREEN_VOLUME: { + f64 amount = *(f64 *)opaque; + camu_sink_offset_volume(&c->sink, (f32)amount); + break; + } case CAMU_SCREEN_CLOSE: c->should_quit = 1; break; -- cgit v1.2.3-101-g0448