- Fixed audio streaming issue with Termux.
This commit is contained in:
An Bui 2025-11-28 00:35:26 +07:00
parent 371bfc4c71
commit abb602925a
10 changed files with 36 additions and 151 deletions

View file

@ -4,10 +4,10 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-11-26T16:43:52.667289300Z">
<DropdownSelection timestamp="2025-11-27T17:28:19.603048800Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="Default" identifier="serial=192.168.1.99:40033;connection=76197848" />
<DeviceId pluginId="LocalEmulator" identifier="path=E:\Android\AVD\avd\Pixel_9_Pro_Fold.avd" />
</handle>
</Target>
</DropdownSelection>

View file

@ -10,8 +10,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
versionCode 41
versionName "3.3.7"
versionCode 42
versionName "3.3.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}

View file

@ -51,10 +51,6 @@
<activity
android:name=".settings.ImportExportSettingsActivity"
android:exported="false" />
<activity
android:name=".settings.MainActivity"
android:exported="false"
android:theme="@style/AppTheme" />
<activity
android:name=".settings.ThemeActivity"
android:configChanges="uiMode|orientation|screenSize|keyboardHidden|smallestScreenSize|screenLayout"
@ -205,10 +201,6 @@
<service
android:name=".MainService"
android:exported="false" />
<service
android:name=".AudioStreamService"
android:enabled="true"
android:foregroundServiceType="mediaPlayback" />
<activity
android:name="com.termux.app.TermuxActivity"

View file

@ -76,12 +76,12 @@ public class AppConfig {
public static String neededPkgs = "bash aria2 tar dwm xfce4-terminal xterm libslirp libslirp-dev pulseaudio-dev glib-dev pixman-dev zlib-dev spice-dev" +
" libusbredirparser usbredir-dev libiscsi-dev sdl2 sdl2-dev sdl2_image-dev libepoxy-dev virglrenderer-dev rdma-core" +
" libusb libaio ncurses-libs curl libnfs gtk+3.0 gtk+3.0-dev fuse libpulse libseccomp jack pipewire liburing" +
" mesa-dri-gallium mesa-vulkan-swrast vulkan-loader mesa-utils mesa-egl mesa-gbm mesa-vulkan-ati mesa-vulkan-broadcom mesa-vulkan-freedreno mesa-vulkan-panfrost";
" libusb libaio ncurses-libs curl libnfs gtk+3.0 gtk+3.0-dev fuse libpulse libseccomp jack pipewire liburing pulseaudio pulseaudio-alsa alsa-plugins-pulse" +
" mesa-dri-gallium mesa-vulkan-swrast vulkan-loader mesa-utils mesa-egl mesa-gbm mesa-vulkan-ati mesa-vulkan-broadcom mesa-vulkan-freedreno mesa-vulkan-panfrost qemu-audio-sdl";
public static String neededPkgs32bit = "bash aria2 tar dwm xterm libslirp libslirp-dev pulseaudio-dev glib-dev pixman-dev zlib-dev spice-dev" +
" libusbredirparser usbredir-dev libiscsi-dev sdl2 sdl2-dev libepoxy-dev virglrenderer-dev rdma-core" +
" libusb ncurses-libs curl libnfs gtk+3.0 fuse libpulse libseccomp jack pipewire liburing tigervnc";
" libusbredirparser usbredir-dev libiscsi-dev sdl2 sdl2-dev libepoxy-dev virglrenderer-dev rdma-core pulseaudio pulseaudio-alsa alsa-plugins-pulse" +
" libusb ncurses-libs curl libnfs gtk+3.0 fuse libpulse libseccomp jack pipewire liburing tigervnc qemu-audio-sdl";
public static boolean needreinstallsystem = false;

View file

@ -1,125 +0,0 @@
package com.vectras.vm;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.termux.app.TermuxService;
import com.vectras.qemu.Config;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Timer;
import java.util.TimerTask;
public class AudioStreamService extends Service {
private static final String CHANNEL_ID = "AudioStreamServiceChannel";
private static final int SAMPLE_RATE = 44100;
private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_STEREO;
private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
private static final int PORT = 4713;
private AudioTrack audioTrack;
private boolean isPlaying = true;
@Override
public void onCreate() {
super.onCreate();
// createNotificationChannel();
// Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
// .setContentTitle("Audio Streaming")
// .setContentText("Receiving audio stream...")
// .setSmallIcon(R.drawable.volume_up_24px)
// .build();
// startForeground(1, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
int minBufSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT);
audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
SAMPLE_RATE,
CHANNEL_CONFIG,
AUDIO_FORMAT,
minBufSize,
AudioTrack.MODE_STREAM
);
new Thread(() -> {
try {
LocalSocket socket = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress(TermuxService.FILES_PATH + "run/pulse/native",
LocalSocketAddress.Namespace.FILESYSTEM);
socket.connect(address);
InputStream in = socket.getInputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
// If raw PCM data
audioTrack.write(buffer, 0, bytesRead);
if (audioTrack != null)
audioTrack.play();
}
in.close();
socket.close();
Log.e("LocalSocket", "Running...");
} catch (Exception e) {
Log.e("LocalSocket", "Connection error", e);
}
}).start();
}
}).start();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
isPlaying = false;
assert audioTrack != null;
audioTrack.stop();
audioTrack.release();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Audio Stream Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}

View file

@ -1214,4 +1214,23 @@ public class VMManager {
|| _qemuCommand.contains("-M pc-q35")
|| _qemuCommand.contains("-machine pc-q35");
}
public static String addAudioDevSdl(String env) {
final String audioDevParam = ",audiodev=defaultaudiodev -audiodev sdl,id=defaultaudiodev ";
String result = env;
if (env.startsWith("-device hda-duplex ") || env.contains(" -device hda-duplex ") || env.endsWith(" -device hda-duplex")) {
result = result.replaceFirst(" -device hda-duplex", " -device hda-duplex" + audioDevParam);
} else if (env.startsWith("-device cs4231a ") || env.contains(" -device cs4231a ") || env.endsWith(" -device cs4231a")) {
result = result.replaceFirst(" -device cs4231a", " -device cs4231a" + audioDevParam);
} else if (env.startsWith("-device ac97 ") || env.contains(" -device ac97 ") || env.endsWith(" -device ac97")) {
result = result.replaceFirst(" -device ac97", " -device ac97" + audioDevParam);
} else if (env.startsWith("-device es1370 ") || env.contains(" -device es1370 ") || env.endsWith(" -device es1370")) {
result = result.replaceFirst(" -device es1370", " -device es1370" + audioDevParam);
} else if (env.startsWith("-device sb16 ") || env.contains(" -device sb16 ") || env.endsWith(" -device sb16")) {
result = result.replaceFirst(" -device sb16", " -device sb16" + audioDevParam);
} else if (env.startsWith("-device adlib ") || env.contains(" -device adlib ") || env.endsWith(" -device adlib")) {
result = result.replaceFirst(" -device adlib", " -device adlib" + audioDevParam);
}
return result;
}
}

View file

@ -46,7 +46,7 @@ public class HomeStartVM {
public static String pendingVMID = "";
public static String pendingThumbnailFile = "";
public static boolean isLaunchFromPending = false;
public static String runCommandFormat = "export TMPDIR=/tmp && mkdir -p $TMPDIR/pulse && export XDG_RUNTIME_DIR=/tmp && pulseaudio --start --exit-idle-time=-1 > /dev/null 2>&1 && %s";
public static String runCommandFormat = "export TMPDIR=/tmp && mkdir -p $TMPDIR/pulse && export XDG_RUNTIME_DIR=/tmp && chmod -R 775 $TMPDIR/pulse && pulseaudio --start --exit-idle-time=-1 > /dev/null 2>&1 && %s";
public static void startNow(
Activity activity,
@ -166,7 +166,7 @@ public class HomeStartVM {
VMManager.isQemuStopedWithError = false;
String finalCommand = String.format(runCommandFormat, env);
String finalCommand = VMManager.addAudioDevSdl(String.format(runCommandFormat, env));
Log.i(TAG, finalCommand);
if (ServiceUtils.isServiceRunning(activity, MainService.class)) {
@ -274,6 +274,6 @@ public class HomeStartVM {
}
public static void setDefault() {
runCommandFormat = "export TMPDIR=/tmp && mkdir -p $TMPDIR/pulse && export XDG_RUNTIME_DIR=/tmp && pulseaudio --start --exit-idle-time=-1 > /dev/null 2>&1 && %s";
runCommandFormat = "export TMPDIR=/tmp && mkdir -p $TMPDIR/pulse && export XDG_RUNTIME_DIR=/tmp && chmod -R 775 $TMPDIR/pulse && pulseaudio --start --exit-idle-time=-1 > /dev/null 2>&1 && %s";
}
}

View file

@ -275,11 +275,10 @@ public class SetupWizardActivity extends AppCompatActivity {
" chmod 775 /usr/local/bin/*;";
} else {
cmd += " apk add qemu-system-x86_64 qemu-system-ppc qemu-system-i386 qemu-system-aarch64" +
" qemu-pr-helper qemu-img qemu-audio-sdl pulseaudio mesa-dri-gallium;";
" qemu-pr-helper qemu-img mesa-dri-gallium;";
}
cmd += " echo \"Just a sec...\";" +
" apk add qemu-audio-sdl pulseaudio;" +
" echo export TMPDIR=/tmp >> /etc/profile;" +
" mkdir -p $TMPDIR/pulse;" +
" echo export PULSE_SERVER=127.0.0.1 >> /etc/profile;" +

View file

@ -3,7 +3,7 @@
<string name="app_name">Vectras VM</string>
<!--======================VECTRAS STRINGS====================-->
<string name="app_version" translatable="false">3.3.7</string>
<string name="app_version" translatable="false">3.3.8</string>
<string name="qemu_version" translatable="false">Stable</string>
<!-- logger -->
<string name="startvm">VM STARTED!</string>

View file

@ -5,11 +5,11 @@
"url": "https://github.com/xoureldeen/Vectras-VM-Android/releases",
"Message": "<h2>3.3.0</h2>New Qemu 9.2.2 with 3dfx! And bugs fixed.",
"cancellable": true,
"versionCodeBeta":"41",
"versionNameBeta":"3.3.7",
"versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7",
"versionCodeBeta":"42",
"versionNameBeta":"3.3.8",
"versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8",
"sizeBeta": "55 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
"MessageBeta": "<h2>3.3.7</h2>Bugs fixed.",
"MessageBeta": "<h2>3.3.8</h2>Bugs fixed.",
"cancellableBeta": true
}