diff --git a/MANUAL.md b/MANUAL.md index 4ca6293..ef93b64 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -196,7 +196,7 @@ All fifteen phone applications are compiled as shared-object plugins (`/usr/lib/ 13. **Ringtones and ringer manager (`ringtones.so` / `xringtones`)**: - **Open**: select **Ringtones** from the System, Config, or Audio menu, or run `xringtones`. - - **Controls**: preview built-in and user ringtones (`~/Ringtones/`, `~/Music/`, `/usr/share/sxmo/ringtones/`) at the configured ringtone volume with `*` or **Left softkey (`Play` / `Stop`)**, press `Return` to set the active ringtone, and switch to **`2:Settings`** to configure **Ringer Mode**, **Ring Volume** (`0%`–`100%`, also adjustable in **XDrawer**), **Ring Duration**, **Ring Loop Count**, and 3-level hardware **Vibrate Strength** (`Level 1` gentle `1400 mV`, `Level 2` medium `2100 mV`, `Level 3` strong `3100 mV`). + - **Controls**: preview built-in, tracker module (`.mod`, `.xm`, `.it`, `.s3m`, etc.), and user ringtones (`~/Ringtones/`, `~/Music/`, `/usr/share/sxmo/ringtones/`, `/usr/share/sxmo/music/`) at the configured ringtone volume with `*` or **Left softkey (`Play` / `Stop`)**, press `Return` to set the active ringtone, and switch to **`2:Settings`** to configure **Ringer Mode**, **Ring Volume** (`0%`–`100%`, also adjustable in **XDrawer**), **Ring Duration**, **Ring Loop Count**, and 3-level hardware **Vibrate Strength** (`Level 1` gentle `1400 mV`, `Level 2` medium `2100 mV`, `Level 3` strong `3100 mV`). 14. **Desktop wallpaper manager (`wallpapers.so` / `xwallpapers` / `xbackground`)**: - **Open**: select **Wallpapers** from the System, Config, or Apps menu, or run `xwallpapers`. diff --git a/overlay/rootfs/etc/skel/.config/wireplumber/wireplumber.conf.d/msm8916-s16le.conf b/overlay/rootfs/etc/skel/.config/wireplumber/wireplumber.conf.d/msm8916-s16le.conf new file mode 100644 index 0000000..fe07420 --- /dev/null +++ b/overlay/rootfs/etc/skel/.config/wireplumber/wireplumber.conf.d/msm8916-s16le.conf @@ -0,0 +1,35 @@ +monitor.alsa.rules = [ + { + matches = [ + { + device.name = "alsa_card.comprC0" + } + ] + actions = { + update-props = { + device.disabled = true + } + } + }, + { + matches = [ + { + node.name = "~alsa_output.platform-7702000.*" + }, + { + node.name = "~alsa_output.platform-sound.*" + }, + { + node.name = "~alsa_input.platform-7702000.*" + }, + { + node.name = "~alsa_input.platform-sound.*" + } + ] + actions = { + update-props = { + audio.format = "S16LE" + } + } + } +] diff --git a/overlay/rootfs/etc/wireplumber/wireplumber.conf.d/msm8916-s16le.conf b/overlay/rootfs/etc/wireplumber/wireplumber.conf.d/msm8916-s16le.conf new file mode 100644 index 0000000..fe07420 --- /dev/null +++ b/overlay/rootfs/etc/wireplumber/wireplumber.conf.d/msm8916-s16le.conf @@ -0,0 +1,35 @@ +monitor.alsa.rules = [ + { + matches = [ + { + device.name = "alsa_card.comprC0" + } + ] + actions = { + update-props = { + device.disabled = true + } + } + }, + { + matches = [ + { + node.name = "~alsa_output.platform-7702000.*" + }, + { + node.name = "~alsa_output.platform-sound.*" + }, + { + node.name = "~alsa_input.platform-7702000.*" + }, + { + node.name = "~alsa_input.platform-sound.*" + } + ] + actions = { + update-props = { + audio.format = "S16LE" + } + } + } +] diff --git a/overlay/rootfs/home/user/.config/wireplumber/wireplumber.conf.d/msm8916-s16le.conf b/overlay/rootfs/home/user/.config/wireplumber/wireplumber.conf.d/msm8916-s16le.conf new file mode 100644 index 0000000..fe07420 --- /dev/null +++ b/overlay/rootfs/home/user/.config/wireplumber/wireplumber.conf.d/msm8916-s16le.conf @@ -0,0 +1,35 @@ +monitor.alsa.rules = [ + { + matches = [ + { + device.name = "alsa_card.comprC0" + } + ] + actions = { + update-props = { + device.disabled = true + } + } + }, + { + matches = [ + { + node.name = "~alsa_output.platform-7702000.*" + }, + { + node.name = "~alsa_output.platform-sound.*" + }, + { + node.name = "~alsa_input.platform-7702000.*" + }, + { + node.name = "~alsa_input.platform-sound.*" + } + ] + actions = { + update-props = { + audio.format = "S16LE" + } + } + } +] diff --git a/overlay/rootfs/usr/bin/sxmo_audio.sh b/overlay/rootfs/usr/bin/sxmo_audio.sh index fe5d38a..ed25abe 100755 --- a/overlay/rootfs/usr/bin/sxmo_audio.sh +++ b/overlay/rootfs/usr/bin/sxmo_audio.sh @@ -2,6 +2,8 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2022 Sxmo Contributors +export PATH="${XDG_CONFIG_HOME:-$HOME/.config}/sxmo/hooks:/usr/share/sxmo/default_hooks:$PATH" + # shellcheck source=configs/default_hooks/sxmo_hook_icons.sh . sxmo_hook_icons.sh . sxmo_common.sh diff --git a/overlay/rootfs/usr/bin/sxmo_modemaudio.sh b/overlay/rootfs/usr/bin/sxmo_modemaudio.sh index 80d4bdc..9f75421 100755 --- a/overlay/rootfs/usr/bin/sxmo_modemaudio.sh +++ b/overlay/rootfs/usr/bin/sxmo_modemaudio.sh @@ -1,31 +1,59 @@ #!/bin/sh +export PATH="${XDG_CONFIG_HOME:-$HOME/.config}/sxmo/hooks:/usr/share/sxmo/default_hooks:$PATH" + # shellcheck source=scripts/core/sxmo_common.sh . sxmo_common.sh enable_call_audio_mode() { if command -v amixer >/dev/null 2>&1; then + amixer -c 0 sset 'SPK DAC Switch' off >/dev/null 2>&1 || true + amixer -c 0 sset 'RX3 MIX1 INP1' 'ZERO' >/dev/null 2>&1 || true amixer -c 0 sset 'PRI_MI2S_RX Voice Mixer VoiceMMode1' on >/dev/null 2>&1 || true amixer -c 0 sset 'VoiceMMode1 Capture Mixer TERT_MI2S_TX' on >/dev/null 2>&1 || true + amixer -c 0 sset 'MultiMedia2 Mixer TERT_MI2S_TX' on >/dev/null 2>&1 || true amixer -c 0 sset 'RX1 MIX1 INP1' 'RX1' >/dev/null 2>&1 || true amixer -c 0 sset 'RDAC2 MUX' 'RX1' >/dev/null 2>&1 || true amixer -c 0 sset 'EAR_S' 'Switch' >/dev/null 2>&1 || true + amixer -c 0 sset 'CIC1 MUX' 'AMIC' >/dev/null 2>&1 || true amixer -c 0 sset 'DEC1 MUX' 'ADC1' >/dev/null 2>&1 || true - amixer -c 0 sset 'ADC1 Volume' 100% >/dev/null 2>&1 || true + amixer -c 0 sset 'ADC1 Volume' 6 >/dev/null 2>&1 || true amixer -c 0 sset 'RX1 Digital Volume' 84 >/dev/null 2>&1 || true fi - sxmo_audio.sh device set Earpiece >/dev/null 2>&1 || true sxmo_log "Enabled ALSA voice call audio mode (Earpiece)." sxmo_hook_statusbar.sh volume >/dev/null 2>&1 || true return 0 } disable_call_audio_mode() { + spk_vol=85 + if [ -r "${XDG_CONFIG_HOME:-$HOME/.config}/sxmo/speaker_vol" ]; then + read -r spk_vol < "${XDG_CONFIG_HOME:-$HOME/.config}/sxmo/speaker_vol" 2>/dev/null || spk_vol=85 + fi + case "$spk_vol" in ''|*[!0-9]*) spk_vol=85 ;; esac + [ "$spk_vol" -gt 100 ] && spk_vol=100 + + pactl set-card-profile alsa_card.comprC0 off >/dev/null 2>&1 || true + if ! pactl list sinks short 2>/dev/null | grep -q "Speaker"; then + pactl set-card-profile alsa_card.platform-7702000.sound "HiFi (Mic1, Speaker)" >/dev/null 2>&1 || true + fi if command -v amixer >/dev/null 2>&1; then amixer -c 0 sset 'PRI_MI2S_RX Voice Mixer VoiceMMode1' off >/dev/null 2>&1 || true amixer -c 0 sset 'VoiceMMode1 Capture Mixer TERT_MI2S_TX' off >/dev/null 2>&1 || true + amixer -c 0 sset 'EAR_S' 'ZERO' >/dev/null 2>&1 || true + amixer -c 0 sset 'RX1 MIX1 INP1' 'ZERO' >/dev/null 2>&1 || true + amixer -c 0 sset 'RX3 MIX1 INP1' 'RX1' >/dev/null 2>&1 || true + if [ "$spk_vol" -gt 0 ]; then + rx3_hw=$(( 60 + spk_vol * 32 / 100 )) + amixer -c 0 sset 'SPK DAC Switch' on >/dev/null 2>&1 || true + amixer -c 0 sset 'RX3 Digital Volume' "$rx3_hw" >/dev/null 2>&1 || true + else + amixer -c 0 sset 'SPK DAC Switch' off >/dev/null 2>&1 || true + amixer -c 0 sset 'RX3 Digital Volume' 0 >/dev/null 2>&1 || true + fi fi - sxmo_audio.sh device set Speaker >/dev/null 2>&1 || true + wpctl set-mute @DEFAULT_AUDIO_SINK@ "$([ "$spk_vol" -le 0 ] && echo 1 || echo 0)" >/dev/null 2>&1 || true + wpctl set-volume @DEFAULT_AUDIO_SINK@ "${spk_vol}%" >/dev/null 2>&1 || true sxmo_log "Disabled ALSA voice call audio mode (Speaker)." sxmo_hook_statusbar.sh volume >/dev/null 2>&1 || true return 0 diff --git a/overlay/rootfs/usr/lib/dwm/plugins/callscreen.so b/overlay/rootfs/usr/lib/dwm/plugins/callscreen.so index be4416f..116ba58 100755 Binary files a/overlay/rootfs/usr/lib/dwm/plugins/callscreen.so and b/overlay/rootfs/usr/lib/dwm/plugins/callscreen.so differ diff --git a/overlay/rootfs/usr/lib/dwm/plugins/music.so b/overlay/rootfs/usr/lib/dwm/plugins/music.so index fcedc18..9835ba5 100755 Binary files a/overlay/rootfs/usr/lib/dwm/plugins/music.so and b/overlay/rootfs/usr/lib/dwm/plugins/music.so differ diff --git a/overlay/rootfs/usr/lib/dwm/plugins/ringtones.so b/overlay/rootfs/usr/lib/dwm/plugins/ringtones.so index 98c633b..6c0fe6c 100755 Binary files a/overlay/rootfs/usr/lib/dwm/plugins/ringtones.so and b/overlay/rootfs/usr/lib/dwm/plugins/ringtones.so differ diff --git a/overlay/rootfs/usr/lib/dwm/plugins/xdrawer.so b/overlay/rootfs/usr/lib/dwm/plugins/xdrawer.so index 9031a32..674cf68 100755 Binary files a/overlay/rootfs/usr/lib/dwm/plugins/xdrawer.so and b/overlay/rootfs/usr/lib/dwm/plugins/xdrawer.so differ diff --git a/overlay/rootfs/usr/share/sxmo/default_hooks/sxmo_hook_ring.sh b/overlay/rootfs/usr/share/sxmo/default_hooks/sxmo_hook_ring.sh index 72bf0b6..1d95248 100755 --- a/overlay/rootfs/usr/share/sxmo/default_hooks/sxmo_hook_ring.sh +++ b/overlay/rootfs/usr/share/sxmo/default_hooks/sxmo_hook_ring.sh @@ -84,10 +84,12 @@ ring() { ring_rx3=$(( 60 + SXMO_RINGVOL * 34 / 100 )) mpv_vol=$(( SXMO_RINGVOL * 130 / 100 )) amixer -c 0 cset "name='SPK DAC Switch'" 1 >/dev/null 2>&1 || true + amixer -c 0 cset "name='RX3 MIX1 INP1'" 3 >/dev/null 2>&1 || true amixer -c 0 cset "name='RX3 Digital Volume'" "$ring_rx3" >/dev/null 2>&1 || true wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1 || true wpctl set-volume @DEFAULT_AUDIO_SINK@ 100% >/dev/null 2>&1 || true mpv --no-resume-playback --quiet --no-video \ + --ao=pipewire,pulse,alsa --audio-channels=stereo --audio-samplerate=48000 \ --volume-max=150 --volume="$mpv_vol" \ --loop="${SXMO_RINGNUMBER:-10}" "$SXMO_RINGTONE" >/dev/null & } diff --git a/overlay/rootfs/usr/share/wireplumber/wireplumber.conf.d/msm8916-s16le.conf b/overlay/rootfs/usr/share/wireplumber/wireplumber.conf.d/msm8916-s16le.conf new file mode 100644 index 0000000..fe07420 --- /dev/null +++ b/overlay/rootfs/usr/share/wireplumber/wireplumber.conf.d/msm8916-s16le.conf @@ -0,0 +1,35 @@ +monitor.alsa.rules = [ + { + matches = [ + { + device.name = "alsa_card.comprC0" + } + ] + actions = { + update-props = { + device.disabled = true + } + } + }, + { + matches = [ + { + node.name = "~alsa_output.platform-7702000.*" + }, + { + node.name = "~alsa_output.platform-sound.*" + }, + { + node.name = "~alsa_input.platform-7702000.*" + }, + { + node.name = "~alsa_input.platform-sound.*" + } + ] + actions = { + update-props = { + audio.format = "S16LE" + } + } + } +] diff --git a/prebuilt/sxmo-x11/usr/lib/dwm/plugins/callscreen.so b/prebuilt/sxmo-x11/usr/lib/dwm/plugins/callscreen.so index be4416f..116ba58 100755 Binary files a/prebuilt/sxmo-x11/usr/lib/dwm/plugins/callscreen.so and b/prebuilt/sxmo-x11/usr/lib/dwm/plugins/callscreen.so differ diff --git a/prebuilt/sxmo-x11/usr/lib/dwm/plugins/music.so b/prebuilt/sxmo-x11/usr/lib/dwm/plugins/music.so index fcedc18..9835ba5 100755 Binary files a/prebuilt/sxmo-x11/usr/lib/dwm/plugins/music.so and b/prebuilt/sxmo-x11/usr/lib/dwm/plugins/music.so differ diff --git a/prebuilt/sxmo-x11/usr/lib/dwm/plugins/ringtones.so b/prebuilt/sxmo-x11/usr/lib/dwm/plugins/ringtones.so index 98c633b..6c0fe6c 100755 Binary files a/prebuilt/sxmo-x11/usr/lib/dwm/plugins/ringtones.so and b/prebuilt/sxmo-x11/usr/lib/dwm/plugins/ringtones.so differ diff --git a/prebuilt/sxmo-x11/usr/lib/dwm/plugins/xdrawer.so b/prebuilt/sxmo-x11/usr/lib/dwm/plugins/xdrawer.so index 9031a32..674cf68 100755 Binary files a/prebuilt/sxmo-x11/usr/lib/dwm/plugins/xdrawer.so and b/prebuilt/sxmo-x11/usr/lib/dwm/plugins/xdrawer.so differ diff --git a/scripts/configure-rootfs-from-scratch.sh b/scripts/configure-rootfs-from-scratch.sh index fde25f2..2be8a06 100755 --- a/scripts/configure-rootfs-from-scratch.sh +++ b/scripts/configure-rootfs-from-scratch.sh @@ -273,6 +273,18 @@ EOF cat > "$ROOTFS/usr/share/wireplumber/wireplumber.conf.d/msm8916-s16le.conf" <(st.st_size - 8); + uint32_t curRiff = static_cast(hdr[4]) | + (static_cast(hdr[5]) << 8) | + (static_cast(hdr[6]) << 16) | + (static_cast(hdr[7]) << 24); + if (curRiff == 0 || curRiff != riffLen) { + uint8_t b[4] = { + static_cast(riffLen & 0xff), + static_cast((riffLen >> 8) & 0xff), + static_cast((riffLen >> 16) & 0xff), + static_cast((riffLen >> 24) & 0xff), + }; + (void)::pwrite(fd, b, 4, 4); + } + for (ssize_t off = 12; off + 8 <= n;) { + uint32_t chunkLen = static_cast(hdr[off + 4]) | + (static_cast(hdr[off + 5]) << 8) | + (static_cast(hdr[off + 6]) << 16) | + (static_cast(hdr[off + 7]) << 24); + if (std::memcmp(hdr + off, "data", 4) == 0) { + uint32_t expectedData = static_cast(st.st_size - (off + 8)); + if (chunkLen == 0 || chunkLen != expectedData) { + uint8_t b[4] = { + static_cast(expectedData & 0xff), + static_cast((expectedData >> 8) & 0xff), + static_cast((expectedData >> 16) & 0xff), + static_cast((expectedData >> 24) & 0xff), + }; + (void)::pwrite(fd, b, 4, off + 4); + } + break; + } + if (chunkLen == 0) break; + off += 8 + static_cast((chunkLen + 1) & ~1u); + } + ::fsync(fd); + ::close(fd); +} + void wakeDisplayHardware(Display* dpy) { int saved = readIntFile("/tmp/sxmo_saved_brightness", -1); if (saved <= 1) { @@ -365,7 +420,7 @@ int callscreen_run_main(int argc, char** argv) { reloadContactsIfNeeded(); AlsaCallControl alsa; - unsigned int savedMicMux = 2; + unsigned int savedMicMux = 1; CallState callState = CallState::Idle; std::string callId; @@ -400,17 +455,17 @@ int callscreen_run_main(int argc, char** argv) { int64_t softkeyDeadlineMs = 0; auto refreshHardwareAudioState = [&]() { - unsigned int dec1 = alsa.getEnum("DEC1 MUX", 2); + unsigned int dec1 = alsa.getEnum("DEC1 MUX", 1); if (dec1 > 0) { savedMicMux = dec1; writeIntFile("/tmp/xdrawer_mic_mux", static_cast(savedMicMux)); micMuted = false; } else { - savedMicMux = static_cast(std::max(1, readIntFile("/tmp/xdrawer_mic_mux", 2))); + savedMicMux = static_cast(std::max(1, readIntFile("/tmp/xdrawer_mic_mux", 1))); micMuted = true; } bool spkSwitch = alsa.getBoolean("SPK DAC Switch", false); - bool earSwitch = alsa.getBoolean("EAR_S", true); + bool earSwitch = (alsa.getEnum("EAR_S", 1) != 0); speakerOn = (spkSwitch && !earSwitch); long rawVol = speakerOn ? alsa.getInteger("RX3 Digital Volume", 68) @@ -427,8 +482,9 @@ int callscreen_run_main(int argc, char** argv) { auto setMicMuted = [&](bool mute) { micMuted = mute; - alsa.setEnum("DEC1 MUX", mute ? 0 : savedMicMux); - alsa.setInteger("ADC3 Volume", mute ? 0 : 8); + alsa.setEnum("DEC1 MUX", mute ? 0 : (savedMicMux > 0 ? savedMicMux : 1)); + alsa.setInteger("ADC1 Volume", mute ? 0 : 6); + alsa.setInteger("ADC3 Volume", mute ? 0 : 6); alsa.setBoolean("VoiceMMode1 Capture Mixer TERT_MI2S_TX", !mute); runAsyncCmd(mute ? "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 1 >/dev/null 2>&1" : "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 0 >/dev/null 2>&1"); @@ -440,21 +496,19 @@ int callscreen_run_main(int argc, char** argv) { speakerOn = toSpeaker; long hwVol = std::lround((callVolumePct * 84.0) / 100.0); if (toSpeaker) { - alsa.setBoolean("EAR_S", false); + alsa.setEnum("EAR_S", 0); alsa.setEnum("RX1 MIX1 INP1", 0); alsa.setBoolean("SPK DAC Switch", true); - alsa.setEnum("RX3 MIX1 INP1", 1); + alsa.setEnum("RX3 MIX1 INP1", 3); alsa.setInteger("RX3 Digital Volume", hwVol); - runAsyncCmd("sxmo_audio.sh device set Speaker >/dev/null 2>&1"); statusNotice = "Audio routed to Speaker"; } else { alsa.setBoolean("SPK DAC Switch", false); alsa.setEnum("RX3 MIX1 INP1", 0); - alsa.setEnum("RX1 MIX1 INP1", 1); - alsa.setEnum("RDAC2 MUX", 1); - alsa.setBoolean("EAR_S", true); + alsa.setEnum("RX1 MIX1 INP1", 3); + alsa.setEnum("RDAC2 MUX", 0); + alsa.setEnum("EAR_S", 1); alsa.setInteger("RX1 Digital Volume", hwVol); - runAsyncCmd("sxmo_audio.sh device set Earpiece >/dev/null 2>&1"); statusNotice = "Audio routed to Earpiece"; } statusNoticeUntilMs = nowMs() + 2000; @@ -482,6 +536,7 @@ int callscreen_run_main(int argc, char** argv) { pid_t r = ::waitpid(recordPid, &status, WNOHANG); if (r == recordPid || (r < 0 && ::kill(recordPid, 0) != 0)) { recordPid = -1; + finalizeWavHeader(recordFilePath); return false; } return true; @@ -492,7 +547,7 @@ int callscreen_run_main(int argc, char** argv) { return; } ::kill(recordPid, SIGINT); - for (int i = 0; i < 15; ++i) { + for (int i = 0; i < 150; ++i) { int status = 0; if (::waitpid(recordPid, &status, WNOHANG) == recordPid) { recordPid = -1; @@ -502,9 +557,15 @@ int callscreen_run_main(int argc, char** argv) { } if (recordPid > 0) { ::kill(recordPid, SIGTERM); - (void)::waitpid(recordPid, nullptr, WNOHANG); + for (int i = 0; i < 20; ++i) { + if (::waitpid(recordPid, nullptr, WNOHANG) == recordPid) { + break; + } + usleep(10000); + } recordPid = -1; } + finalizeWavHeader(recordFilePath); if (!recordFileName.empty()) { statusNotice = "Saved: " + recordFileName; statusNoticeUntilMs = nowMs() + 3500; @@ -527,7 +588,12 @@ int callscreen_run_main(int argc, char** argv) { recordFileName = "call_" + tag + "_" + tsBuf + ".wav"; recordFilePath = dir + "/" + recordFileName; - // Ensure MultiMedia2 capture mixer is enabled for PipeWire recording + // Ensure MultiMedia2 capture mixer and primary mic path are active for PipeWire recording + alsa.setEnum("CIC1 MUX", 0); + if (!micMuted) { + alsa.setEnum("DEC1 MUX", savedMicMux > 0 ? savedMicMux : 1); + alsa.setInteger("ADC1 Volume", 6); + } alsa.setBoolean("MultiMedia2 Mixer TERT_MI2S_TX", true); pid_t pid = ::fork(); @@ -536,6 +602,12 @@ int callscreen_run_main(int argc, char** argv) { } if (pid == 0) { ::setsid(); + std::string runtimeDir = "/run/user/" + std::to_string(::getuid()); + const char* xdg = std::getenv("XDG_RUNTIME_DIR"); + struct stat stRuntime {}; + if (!xdg || !*xdg || ::stat(xdg, &stRuntime) != 0) { + ::setenv("XDG_RUNTIME_DIR", runtimeDir.c_str(), 1); + } int nullfd = ::open("/dev/null", O_RDWR); if (nullfd >= 0) { ::dup2(nullfd, STDIN_FILENO); @@ -545,8 +617,9 @@ int callscreen_run_main(int argc, char** argv) { ::close(nullfd); } } - execlp("pw-record", "pw-record", "--rate", "16000", "--channels", "1", - recordFilePath.c_str(), static_cast(nullptr)); + execlp("pw-record", "pw-record", "--format", "s16", "--rate", "16000", + "--channels", "1", recordFilePath.c_str(), + static_cast(nullptr)); execlp("parecord", "parecord", "--rate=16000", "--channels=1", "--file-format=wav", recordFilePath.c_str(), static_cast(nullptr)); execlp("arecord", "arecord", "-f", "S16_LE", "-r", "16000", "-c", "1", @@ -1055,6 +1128,12 @@ int callscreen_run_main(int argc, char** argv) { if (micMuted) { setMicMuted(false); } + alsa.setBoolean("PRI_MI2S_RX Voice Mixer VoiceMMode1", false); + alsa.setBoolean("VoiceMMode1 Capture Mixer TERT_MI2S_TX", false); + alsa.setEnum("EAR_S", 0); + alsa.setEnum("RX3 MIX1 INP1", 3); + alsa.setBoolean("SPK DAC Switch", true); + runAsyncCmd("sxmo_modemaudio.sh disable_call_audio_mode >/dev/null 2>&1"); if (callStartMs > 0 && (callState == CallState::Active || callState == CallState::Outgoing)) { lastFinalDurationSec = static_cast((nowMs() - callStartMs) / 1000); diff --git a/src/dwm-plugins/music_plugin.cpp b/src/dwm-plugins/music_plugin.cpp index 98b51dd..6166a58 100644 --- a/src/dwm-plugins/music_plugin.cpp +++ b/src/dwm-plugins/music_plugin.cpp @@ -275,6 +275,7 @@ void applySystemSpeakerVol(int pct) { int rx3 = (pct <= 0) ? 0 : (54 + (pct * 38) / 100); runAsyncCmd("amixer -c 0 cset \"name='SPK DAC Switch'\" " + std::string(pct > 0 ? "1" : "0") + " >/dev/null 2>&1; " + "amixer -c 0 cset \"name='RX3 MIX1 INP1'\" 3 >/dev/null 2>&1; " "amixer -c 0 cset \"name='RX3 Digital Volume'\" " + std::to_string(rx3) + " >/dev/null 2>&1; " "wpctl set-mute @DEFAULT_AUDIO_SINK@ " + @@ -433,7 +434,12 @@ int runMusicApp(int argc, char** argv) { ipcReadBuf.clear(); if (mpvPid > 0) { ::kill(mpvPid, SIGTERM); - ::waitpid(mpvPid, nullptr, WNOHANG); + for (int i = 0; i < 15; ++i) { + if (::waitpid(mpvPid, nullptr, WNOHANG) == mpvPid) { + break; + } + usleep(10000); + } mpvPid = -1; } ::unlink(kMpvSockPath); @@ -456,6 +462,12 @@ int runMusicApp(int argc, char** argv) { pid_t pid = ::fork(); if (pid == 0) { ::setsid(); + std::string runtimeDir = "/run/user/" + std::to_string(::getuid()); + const char* xdg = std::getenv("XDG_RUNTIME_DIR"); + struct stat stRuntime {}; + if (!xdg || !*xdg || ::stat(xdg, &stRuntime) != 0) { + ::setenv("XDG_RUNTIME_DIR", runtimeDir.c_str(), 1); + } int devNull = ::open("/dev/null", O_RDWR); if (devNull >= 0) { ::dup2(devNull, STDIN_FILENO); @@ -466,8 +478,9 @@ int runMusicApp(int argc, char** argv) { std::string ipcArg = std::string("--input-ipc-server=") + kMpvSockPath; ::execlp("mpv", "mpv", "--no-video", "--no-terminal", "--quiet", - "--idle=yes", "--keep-open=no", "--volume-max=150", - "--volume=100", ipcArg.c_str(), + "--ao=pipewire,pulse,alsa", "--audio-channels=stereo", + "--audio-samplerate=48000", "--idle=yes", "--keep-open=no", + "--volume-max=150", "--volume=100", ipcArg.c_str(), static_cast(nullptr)); ::_exit(127); } else if (pid > 0) { @@ -519,7 +532,9 @@ int runMusicApp(int argc, char** argv) { isPaused = false; ignoreEofUntilMs = nowMs() + 450; - runAsyncCmd("amixer -c 0 cset \"name='SPK DAC Switch'\" 1 >/dev/null 2>&1; " + runAsyncCmd("amixer -c 0 cset \"name='EAR_S'\" 0 >/dev/null 2>&1; " + "amixer -c 0 cset \"name='SPK DAC Switch'\" 1 >/dev/null 2>&1; " + "amixer -c 0 cset \"name='RX3 MIX1 INP1'\" 3 >/dev/null 2>&1; " "wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1"); ensureMpvRunning(); diff --git a/src/dwm-plugins/ringtones_plugin.cpp b/src/dwm-plugins/ringtones_plugin.cpp index 13a25c9..322db70 100644 --- a/src/dwm-plugins/ringtones_plugin.cpp +++ b/src/dwm-plugins/ringtones_plugin.cpp @@ -70,7 +70,13 @@ bool isAudioExtension(const std::string& name, std::string* extUpperOut = nullpt if (dot == std::string::npos || dot + 1 >= name.size()) return false; std::string ext = toLowerStr(name.substr(dot + 1)); if (ext == "wav" || ext == "ogg" || ext == "mp3" || - ext == "flac" || ext == "opus" || ext == "m4a") { + ext == "flac" || ext == "opus" || ext == "m4a" || + ext == "aac" || ext == "wma" || ext == "aiff" || ext == "aif" || + ext == "mod" || ext == "xm" || ext == "it" || ext == "s3m" || + ext == "mptm" || ext == "stm" || ext == "med" || ext == "okt" || + ext == "669" || ext == "ult" || ext == "far" || ext == "mtm" || + ext == "ptm" || ext == "amf" || ext == "dsm" || ext == "imf" || + ext == "gdm" || ext == "stx" || ext == "umx" || ext == "mo3") { if (extUpperOut) { *extUpperOut = ext; for (char& c : *extUpperOut) { @@ -139,6 +145,7 @@ std::vector discoverRingtones() { scanDirectoryForRingtones(home + "/Ringtones", "User", list); scanDirectoryForRingtones(home + "/Music/Ringtones", "User", list); scanDirectoryForRingtones(home + "/Music", "Music", list); + scanDirectoryForRingtones("/usr/share/sxmo/music", "Tracker", list); return list; } @@ -381,13 +388,21 @@ int runRingtonesApp(int argc, char** argv) { int rx3 = (rvol <= 0) ? 0 : (60 + rvol * 34 / 100); int mpvVol = rvol * 130 / 100; std::string volArg = "--volume=" + std::to_string(mpvVol); - runAsyncCmd("amixer -c 0 cset \"name='SPK DAC Switch'\" 1 >/dev/null 2>&1; " + runAsyncCmd("amixer -c 0 cset \"name='EAR_S'\" 0 >/dev/null 2>&1; " + "amixer -c 0 cset \"name='SPK DAC Switch'\" 1 >/dev/null 2>&1; " + "amixer -c 0 cset \"name='RX3 MIX1 INP1'\" 3 >/dev/null 2>&1; " "amixer -c 0 cset \"name='RX3 Digital Volume'\" " + std::to_string(rx3) + " >/dev/null 2>&1; " "wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1; " "wpctl set-volume @DEFAULT_AUDIO_SINK@ 100% >/dev/null 2>&1"); pid_t pid = ::fork(); if (pid == 0) { + std::string runtimeDir = "/run/user/" + std::to_string(::getuid()); + const char* xdg = std::getenv("XDG_RUNTIME_DIR"); + struct stat stRuntime {}; + if (!xdg || !*xdg || ::stat(xdg, &stRuntime) != 0) { + ::setenv("XDG_RUNTIME_DIR", runtimeDir.c_str(), 1); + } int devNull = ::open("/dev/null", O_RDWR); if (devNull >= 0) { ::dup2(devNull, STDIN_FILENO); @@ -396,7 +411,9 @@ int runRingtonesApp(int argc, char** argv) { if (devNull > 2) ::close(devNull); } ::execlp("mpv", "mpv", "--no-resume-playback", "--quiet", - "--no-video", "--volume-max=150", volArg.c_str(), + "--no-video", "--ao=pipewire,pulse,alsa", + "--audio-channels=stereo", "--audio-samplerate=48000", + "--volume-max=150", volArg.c_str(), "--loop=2", path.c_str(), nullptr); ::_exit(127); } else if (pid > 0) { diff --git a/src/xdrawer/xdrawer.cpp b/src/xdrawer/xdrawer.cpp index a9d8c7f..daa80d6 100644 --- a/src/xdrawer/xdrawer.cpp +++ b/src/xdrawer/xdrawer.cpp @@ -333,12 +333,12 @@ class PhoneController { "Sound", false, 0, 1, 1, spkOn ? 1 : 0}); // 7. Microphone on/off (toggle) - unsigned int dec1 = alsa_.getEnum("DEC1 MUX", 2); + unsigned int dec1 = alsa_.getEnum("DEC1 MUX", 1); if (dec1 > 0) { savedMicMux_ = dec1; writeIntFile("/tmp/xdrawer_mic_mux", static_cast(savedMicMux_)); } else { - savedMicMux_ = static_cast(std::max(1, readIntFile("/tmp/xdrawer_mic_mux", 2))); + savedMicMux_ = static_cast(std::max(1, readIntFile("/tmp/xdrawer_mic_mux", 1))); } items_.push_back({ControlId::MicMute, kIconMicOn, kIconMicOff, "Microphone", false, 0, 1, 1, (dec1 > 0) ? 1 : 0}); @@ -470,6 +470,7 @@ class PhoneController { long hw = (it.value <= 0) ? 0 : (60 + std::lround((it.value * 32.0) / 100.0)); if (it.value > 0) { alsa_.setBoolean("SPK DAC Switch", true); + alsa_.setEnum("RX3 MIX1 INP1", 3); } alsa_.setInteger("RX3 Digital Volume", hw); char cmd[160]; @@ -501,6 +502,9 @@ class PhoneController { } case ControlId::SoundMute: { alsa_.setBoolean("SPK DAC Switch", it.value != 0); + if (it.value != 0) { + alsa_.setEnum("RX3 MIX1 INP1", 3); + } runAsyncCmd(it.value ? "wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1; " "sxmo_hook_statusbar.sh audio >/dev/null 2>&1" @@ -509,8 +513,9 @@ class PhoneController { break; } case ControlId::MicMute: { - alsa_.setEnum("DEC1 MUX", it.value ? savedMicMux_ : 0); - alsa_.setInteger("ADC3 Volume", it.value ? 8 : 0); + alsa_.setEnum("DEC1 MUX", it.value ? (savedMicMux_ > 0 ? savedMicMux_ : 1) : 0); + alsa_.setInteger("ADC1 Volume", it.value ? 6 : 0); + alsa_.setInteger("ADC3 Volume", it.value ? 6 : 0); runAsyncCmd(it.value ? "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 0 >/dev/null 2>&1" : "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 1 >/dev/null 2>&1");