如何在xfce4桌面播放音频,音乐,声音

提示未连接到pulseaudio服务

1 个赞

有没有高手解答一下,我也遇到了这个问题

1.可以提供一下硬件设备信息和软件版本信息方便排查
2.检查 PulseAudio 进程是否存在,检查是否端口占用

暂时音频在受权限受限的设备上还不支持

  1. 安装 pulseaudiopulseaudio-enable-autospawnlibpulse0 等与 pulseaudio 相关的软件包。
  2. 使用命令编辑配置文件:
nano /data/data/com.aidlux/files/usr/etc/pulse/default.pa

default.pa 文件中插入以下内容(或去掉前面的 # 注释符号),然后保存文件:

load-module module-aaudio-sink
load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1 auth-anonymous=1 --exit-idle-time=-1
  1. 在终端中执行以下命令,取消 PULSE_SERVER 的设置:
unset PULSE_SERVER
  1. 在你的 .zshrc.bashrc 等 shell 配置文件中加入以下内容:
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export TMPDIR=/tmp
bash ~/.startaudio
  1. 创建 ~/.startaudio 文件,并写入以下脚本内容。保存后使用 chmod +x ~/.startaudio 使其可执行。
    每次登录 Aidlux 时,PulseAudio 将自动启动。
#!/bin/bash
# 防止重复运行
if pgrep -x "pulseaudio" > /dev/null; then
    exit 0
fi

# 静默结束已存在的 PulseAudio 实例
pkill -f pulseaudio >/dev/null 2>&1

# 检查 DBus 是否正在运行且未退出
if ! pgrep -x "dbus-daemon" > /dev/null || ! busctl --list | grep -q "org.freedesktop.DBus"; then
    service dbus restart >/dev/null 2>&1
    dbus-launch --exit-with-session >/dev/null 2>&1
fi

# 后台启动 PulseAudio(静默)
service pulseaudio-enable-autospawn start >/dev/null 2>&1
nohup /data/data/com.aidlux/files/usr/bin/pulseaudio \
    --start --load="module-native-protocol-tcp auth-ip-acl=127.0.0.1 auth-anonymous=1" --exit-idle-time=-1 \
    >/dev/null 2>&1 &

exit 0
  1. 确保你已经删除/卸载所有 ALSA 相关的软件包,并删除 ALSA 的配置文件(alsa_conf),因为它们会与 PulseAudio 冲突。
    你也可以使用如下命令阻止 ALSA 软件包被重新安装:
sudo apt-mark hold <alsa软件包名称>
  1. 可使用以下命令将 module-aaudio-sink 设置为默认的输出设备(sink):
bash /data/data/com.aidlux/files/usr/bin/pactl set-default-sink module-aaudio-sink

如果没有任何反应,尝试重启系统后再尝试。

  1. 第一次设置完成后,记得执行以下命令重启设备:
aid-reboot
  1. 重启之后,可以通过以下命令测试 PulseAudio 是否正常启动:
pulseaudio -vvv
  1. PulseAudio 无法启动的常见原因是 DBus 和 ALSA 配置问题。如果遇到问题,请解决后重启设备。

另一种跟简单的方法如下:

AidLux 音频播放器与 SL4A 集成版

这是一个功能丰富的音频播放器,专为运行 AidLux 的 Android 设备设计,利用 Android 脚本层(SL4A)实现媒体控制。该脚本提供基于终端的音频播放控制,支持播放列表、随机播放、循环播放和音量调节等功能。

功能特点:

  • 可直接播放音频文件或文件夹中的音频
  • 支持多种音频格式(MP3、WAV、OGG、M4A、FLAC、AAC)
  • 带键盘控制的交互式终端界面
  • 播放控制(播放/暂停、上一曲/下一曲)
  • 音量调节
  • 支持随机播放与循环播放模式
  • 显示播放进度
  • 优雅地处理系统信号中断

键盘控制:

  • p:暂停播放
  • r:恢复播放
  • n:下一首
  • b:上一首
  • l:切换循环模式
  • h:切换随机播放模式
  • +:增加音量
  • -:降低音量
  • q:退出播放器

请安装:

  • FFmpeg(用于检测音频时长)

使用方法:

python3.8 aidlux-sl4a.py <文件或目录路径>

示例:

python3 aidluxsl4a.py /sdcard/Music
python3 aidluxsl4a.py /sdcard/Music/song.mp3

代码如下:

#!/usr/bin/env python3
import os
import random
import select
import signal
import subprocess
import sys
import termios
import threading
import time
import tty
import android
# Initialize AidLux API
droid = android.Android()
class PlayerState:
    """Manages the state of the audio player."""
    def __init__(self):
        self.playlist = []
        self.current_index = 0
        self.looping = False
        self.shuffle_mode = False
        self.progress_stop_flag = threading.Event()
        self.progress_thread = None
        self.paused = False
        self.running = True
        self.current_start_time = 0
        self.current_duration = 0
state = PlayerState()
def load_audio(path):
    """Load and play a file or folder."""
    if os.path.isfile(path):
        state.playlist = [path]
    elif os.path.isdir(path):
        valid_ext = (".mp3", ".wav", ".ogg", ".m4a", ".flac", ".aac")
        state.playlist = sorted(
            os.path.join(path, f) for f in os.listdir(path) if f.endswith(valid_ext)
        )
        if not state.playlist:
            print("\n🚫 No audio files found.")
            return
    else:
        print("\n🚫 Invalid path.")
        sys.exit(1)
    print(f"\n🎶 Playing {len(state.playlist)} files from {path}")
    state.current_index = 0
    play_audio()
def get_audio_duration(file_path):
    """Get the duration of the audio file using ffmpeg."""
    try:
        result = subprocess.run(
            ["ffmpeg", "-i", file_path],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
        )
        for line in result.stderr.splitlines():
            if "Duration" in line:
                duration_str = line.split("Duration:")[1].split(",")[0].strip()
                hours, minutes, seconds = duration_str.split(":")
                return int(hours) * 3600 + int(minutes) * 60 + float(seconds)
        return 0
    except Exception:
        return 0
def format_time(seconds):
    """Convert seconds to HH:MM:SS format."""
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    seconds = int(seconds % 60)
    return f"{hours:02}:{minutes:02}:{seconds:02}"
def show_playback_progress(start_time, total_duration):
    """Display playback progress."""
    last_position = -1
    try:
        while not state.progress_stop_flag.is_set() and total_duration > 0:
            if state.paused:
                time.sleep(0.5)
                continue
                
            current_position = time.time() - start_time
            
            # Only update display if position changed by at least 1 second
            if int(current_position) != last_position:
                last_position = int(current_position)
                sys.stdout.write(
                    f"\r⏱️ {format_time(current_position)} / {format_time(total_duration)}   "
                )
                sys.stdout.flush()
            
            if current_position >= total_duration:
                if not state.looping:
                    print("\n🎵 Finished.")
                    next_song()
                break
                
            time.sleep(0.5)  # More responsive updates
            
    except Exception as e:
        print(f"\n⚠️ Error in progress tracking: {str(e)}")
    finally:
        # Clear the line when done
        sys.stdout.write("\r" + " " * 50 + "\r")
        sys.stdout.flush()
def stop_playback():
    """Stop any ongoing playback and clean up resources."""
    if state.progress_thread and state.progress_thread.is_alive():
        state.progress_stop_flag.set()
        state.progress_thread.join(timeout=0.5)
        state.progress_thread = None
    droid.mediaPlayClose()
    time.sleep(0.2)  # Small delay to ensure clean state

def play_audio():
    """Play the current song."""
    if not state.playlist:
        print("\n🚫 Playlist is empty.")
        return
    
    # Stop any existing playback and clean up
    stop_playback()
    
    file_path = state.playlist[state.current_index]
    print(f"\n🎵 Now playing: {os.path.basename(file_path)}")
    
    # Reset the stop flag for the new playback
    state.progress_stop_flag.clear()
    
    # Start new playback
    droid.mediaPlay(file_path)
    
    # Get duration and set up progress tracking
    state.current_duration = get_audio_duration(file_path)
    state.current_start_time = time.time()
    
    # Start new progress thread
    state.progress_thread = threading.Thread(
        target=show_playback_progress, 
        args=(state.current_start_time, state.current_duration), 
        daemon=True
    )
    state.progress_thread.start()
def pause_audio():
    """Pause playback."""
    if not state.paused:
        droid.mediaPlayPause()
        print("\n⏸️ Paused playback.")
        state.paused = True
def resume_audio():
    """Resume playback."""
    if state.paused:
        droid.mediaPlayStart()
        print("\n▶️ Resumed playback.")
        state.paused = False
        # Update start time to account for pause duration
        state.current_start_time = time.time() - (time.time() - state.current_start_time - (time.time() % 1))
def next_song():
    """Play the next song."""
    if state.current_index < len(state.playlist) - 1:
        state.current_index += 1
        play_audio()
    else:
        print("\n⏹️ End of playlist reached.")
def previous_song():
    """Play the previous song."""
    if state.current_index > 0:
        state.current_index -= 1
    play_audio()
def toggle_loop():
    """Toggle looping mode."""
    state.looping = not state.looping
    droid.mediaPlaySetLooping(state.looping)
    print(f"\n🔁 Looping {'on' if state.looping else 'off'}")
def toggle_shuffle():
    """Toggle shuffle mode."""
    state.shuffle_mode = not state.shuffle_mode
    if state.shuffle_mode:
        random.shuffle(state.playlist)
    else:
        state.playlist.sort()
    print(f"\n🔀 Shuffle {'on' if state.shuffle_mode else 'off'}")
def adjust_volume(change):
    """Adjust media volume."""
    current_vol = droid.getMediaVolume().result
    new_vol = max(0, min(15, current_vol + change))
    droid.setMediaVolume(new_vol)
    print(f"\n🔊 Volume set to {new_vol}")
def handle_exit_signal(sig, frame):
    """Handle exit signal."""
    print("\n👋 Exiting...")
    stop_playback()
    sys.exit(0)
def process_key(key):
    """Process key commands."""
    key = key.lower()
    if key == "p":
        pause_audio()
    elif key == "r":
        resume_audio()
    elif key == "n":
        next_song()
    elif key == "b":
        previous_song()
    elif key == "l":
        toggle_loop()
    elif key == "h":
        toggle_shuffle()
    elif key == "+":
        adjust_volume(1)
    elif key == "-":
        adjust_volume(-1)
    elif key == "q":
        droid.mediaPlayClose()
        state.running = False
        return False
    return True
    
if __name__ == "__main__":
    signal.signal(signal.SIGINT, handle_exit_signal)  # Handle Ctrl+C gracefully
    if len(sys.argv) < 2:
        print("Usage: python3 audio.py <file or folder>")
        sys.exit(1)
    load_audio(sys.argv[1])  # Load and start playing audio
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        tty.setcbreak(fd)
        running = True
        while running:
            try:
                if select.select([sys.stdin], [], [], 0)[0]:
                    key = sys.stdin.read(1)
                    running = process_key(key)
                time.sleep(0.1)
            except KeyboardInterrupt:
                handle_exit_signal(None, None)  # Handle Ctrl+C
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)