Fix WAV call recording/playback, ALSA call audio routing, and tracker ringtones

This commit is contained in:
Luxferre
2026-10-25 18:15:36 +02:00
parent b706865ee8
commit f4fe5ce8e9
21 changed files with 339 additions and 33 deletions
+1 -1
View File
@@ -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`.
@@ -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"
}
}
}
]
@@ -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"
}
}
}
]
@@ -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"
}
}
}
]
+2
View File
@@ -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
+31 -3
View File
@@ -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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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 &
}
@@ -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"
}
}
}
]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+18
View File
@@ -273,6 +273,18 @@ EOF
cat > "$ROOTFS/usr/share/wireplumber/wireplumber.conf.d/msm8916-s16le.conf" <<EOF
monitor.alsa.rules = [
{
matches = [
{
device.name = "alsa_card.comprC0"
}
]
actions = {
update-props = {
device.disabled = true
}
}
},
{
matches = [
{
@@ -280,6 +292,12 @@ monitor.alsa.rules = [
},
{
node.name = "~alsa_output.platform-sound.*"
},
{
node.name = "~alsa_input.platform-7702000.*"
},
{
node.name = "~alsa_input.platform-sound.*"
}
]
actions = {
+97 -18
View File
@@ -207,6 +207,61 @@ std::string getRecordingsDir() {
return dir;
}
void finalizeWavHeader(const std::string& path) {
if (path.empty()) return;
int fd = ::open(path.c_str(), O_RDWR);
if (fd < 0) return;
struct stat st {};
if (::fstat(fd, &st) != 0 || st.st_size <= 44) {
::close(fd);
return;
}
uint8_t hdr[64] = {0};
ssize_t n = :: pread(fd, hdr, sizeof(hdr), 0);
if (n < 44 || std::memcmp(hdr, "RIFF", 4) != 0 ||
std::memcmp(hdr + 8, "WAVE", 4) != 0) {
::close(fd);
return;
}
uint32_t riffLen = static_cast<uint32_t>(st.st_size - 8);
uint32_t curRiff = static_cast<uint32_t>(hdr[4]) |
(static_cast<uint32_t>(hdr[5]) << 8) |
(static_cast<uint32_t>(hdr[6]) << 16) |
(static_cast<uint32_t>(hdr[7]) << 24);
if (curRiff == 0 || curRiff != riffLen) {
uint8_t b[4] = {
static_cast<uint8_t>(riffLen & 0xff),
static_cast<uint8_t>((riffLen >> 8) & 0xff),
static_cast<uint8_t>((riffLen >> 16) & 0xff),
static_cast<uint8_t>((riffLen >> 24) & 0xff),
};
(void)::pwrite(fd, b, 4, 4);
}
for (ssize_t off = 12; off + 8 <= n;) {
uint32_t chunkLen = static_cast<uint32_t>(hdr[off + 4]) |
(static_cast<uint32_t>(hdr[off + 5]) << 8) |
(static_cast<uint32_t>(hdr[off + 6]) << 16) |
(static_cast<uint32_t>(hdr[off + 7]) << 24);
if (std::memcmp(hdr + off, "data", 4) == 0) {
uint32_t expectedData = static_cast<uint32_t>(st.st_size - (off + 8));
if (chunkLen == 0 || chunkLen != expectedData) {
uint8_t b[4] = {
static_cast<uint8_t>(expectedData & 0xff),
static_cast<uint8_t>((expectedData >> 8) & 0xff),
static_cast<uint8_t>((expectedData >> 16) & 0xff),
static_cast<uint8_t>((expectedData >> 24) & 0xff),
};
(void)::pwrite(fd, b, 4, off + 4);
}
break;
}
if (chunkLen == 0) break;
off += 8 + static_cast<ssize_t>((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<int>(savedMicMux));
micMuted = false;
} else {
savedMicMux = static_cast<unsigned int>(std::max(1, readIntFile("/tmp/xdrawer_mic_mux", 2)));
savedMicMux = static_cast<unsigned int>(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<char*>(nullptr));
execlp("pw-record", "pw-record", "--format", "s16", "--rate", "16000",
"--channels", "1", recordFilePath.c_str(),
static_cast<char*>(nullptr));
execlp("parecord", "parecord", "--rate=16000", "--channels=1",
"--file-format=wav", recordFilePath.c_str(), static_cast<char*>(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<int>((nowMs() - callStartMs) / 1000);
+19 -4
View File
@@ -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<char*>(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();
+20 -3
View File
@@ -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<RingtoneEntry> 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) {
+9 -4
View File
@@ -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<int>(savedMicMux_));
} else {
savedMicMux_ = static_cast<unsigned int>(std::max(1, readIntFile("/tmp/xdrawer_mic_mux", 2)));
savedMicMux_ = static_cast<unsigned int>(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");