mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-17 22:07:20 +00:00
- ModuleManager: Central tracking for installed marketplace modules - DataAnalyzerWidget: Real-time CPU/RAM/Battery/Storage widget (unlocked by Data Analyzer module) - BottomNavBar: Navigation bar for Projects/Chat/Marketplace/Settings - RootShell: Real root command execution utility - TerminalActivity: Full root shell with neofetch, sysinfo, real Linux commands - Terminal Pro module: Adds aliases (ll, la, h), command history - ArcadeActivity + SnakeGame: Pixel Arcade module unlocks retro games - fade_in/fade_out animations for smooth transitions
162 lines
5.2 KiB
Java
162 lines
5.2 KiB
Java
package com.aethex.os;
|
|
|
|
import android.media.AudioAttributes;
|
|
import android.media.AudioFormat;
|
|
import android.media.AudioTrack;
|
|
|
|
import java.util.EnumMap;
|
|
import java.util.Map;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
|
|
/**
|
|
* Singleton sound manager for AeThexOS Android.
|
|
* Generates tones programmatically using AudioTrack - no sound asset files needed.
|
|
*/
|
|
public class SoundManager {
|
|
|
|
private static final int SAMPLE_RATE = 44100;
|
|
private static final float VOLUME = 0.3f;
|
|
|
|
public enum Sound {
|
|
OPEN(523, 0.1, WaveType.SINE),
|
|
CLOSE(392, 0.1, WaveType.SINE),
|
|
CLICK(800, 0.03, WaveType.SQUARE),
|
|
NOTIFICATION(880, 0.15, WaveType.SINE),
|
|
SWITCH(440, 0.2, WaveType.SAWTOOTH),
|
|
BOOT_BEEP(660, 0.05, WaveType.SINE);
|
|
|
|
final int frequency;
|
|
final double duration;
|
|
final WaveType waveType;
|
|
|
|
Sound(int frequency, double duration, WaveType waveType) {
|
|
this.frequency = frequency;
|
|
this.duration = duration;
|
|
this.waveType = waveType;
|
|
}
|
|
}
|
|
|
|
private enum WaveType {
|
|
SINE, SQUARE, SAWTOOTH
|
|
}
|
|
|
|
private static volatile SoundManager instance;
|
|
|
|
private final Map<Sound, byte[]> bufferCache = new EnumMap<>(Sound.class);
|
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
private volatile boolean enabled = true;
|
|
|
|
private SoundManager() {
|
|
}
|
|
|
|
public static SoundManager getInstance() {
|
|
if (instance == null) {
|
|
synchronized (SoundManager.class) {
|
|
if (instance == null) {
|
|
instance = new SoundManager();
|
|
}
|
|
}
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
public void setEnabled(boolean enabled) {
|
|
this.enabled = enabled;
|
|
}
|
|
|
|
public boolean isEnabled() {
|
|
return enabled;
|
|
}
|
|
|
|
/**
|
|
* Play a sound on a background thread. Non-blocking.
|
|
*/
|
|
public void play(Sound sound) {
|
|
if (!enabled) return;
|
|
|
|
executor.execute(() -> {
|
|
try {
|
|
byte[] pcm = getOrGenerateBuffer(sound);
|
|
int bufferSize = pcm.length;
|
|
|
|
AudioAttributes attributes = new AudioAttributes.Builder()
|
|
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
|
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
|
.build();
|
|
|
|
AudioFormat format = new AudioFormat.Builder()
|
|
.setSampleRate(SAMPLE_RATE)
|
|
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
|
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
|
|
.build();
|
|
|
|
AudioTrack track = new AudioTrack.Builder()
|
|
.setAudioAttributes(attributes)
|
|
.setAudioFormat(format)
|
|
.setBufferSizeInBytes(bufferSize)
|
|
.setTransferMode(AudioTrack.MODE_STATIC)
|
|
.build();
|
|
|
|
track.write(pcm, 0, pcm.length);
|
|
track.setVolume(VOLUME);
|
|
track.play();
|
|
|
|
// Wait for playback to complete, then release
|
|
long durationMs = (long) (sound.duration * 1000) + 50;
|
|
Thread.sleep(durationMs);
|
|
|
|
track.stop();
|
|
track.release();
|
|
} catch (Exception e) {
|
|
// Silently ignore sound playback failures
|
|
}
|
|
});
|
|
}
|
|
|
|
private synchronized byte[] getOrGenerateBuffer(Sound sound) {
|
|
byte[] cached = bufferCache.get(sound);
|
|
if (cached != null) return cached;
|
|
|
|
byte[] buffer = generatePcmBuffer(sound.frequency, sound.duration, sound.waveType);
|
|
bufferCache.put(sound, buffer);
|
|
return buffer;
|
|
}
|
|
|
|
private byte[] generatePcmBuffer(int frequency, double duration, WaveType waveType) {
|
|
int numSamples = (int) (SAMPLE_RATE * duration);
|
|
byte[] pcm = new byte[numSamples * 2]; // 16-bit mono = 2 bytes per sample
|
|
|
|
for (int i = 0; i < numSamples; i++) {
|
|
double t = (double) i / SAMPLE_RATE;
|
|
double sample;
|
|
|
|
switch (waveType) {
|
|
case SQUARE:
|
|
sample = Math.signum(Math.sin(2.0 * Math.PI * frequency * t));
|
|
break;
|
|
case SAWTOOTH:
|
|
sample = 2.0 * (t * frequency - Math.floor(0.5 + t * frequency));
|
|
break;
|
|
case SINE:
|
|
default:
|
|
sample = Math.sin(2.0 * Math.PI * frequency * t);
|
|
break;
|
|
}
|
|
|
|
// Apply a short fade-in/fade-out envelope to avoid clicks
|
|
int fadeLength = Math.min(numSamples / 10, SAMPLE_RATE / 200);
|
|
if (i < fadeLength) {
|
|
sample *= (double) i / fadeLength;
|
|
} else if (i > numSamples - fadeLength) {
|
|
sample *= (double) (numSamples - i) / fadeLength;
|
|
}
|
|
|
|
short pcmValue = (short) (sample * Short.MAX_VALUE);
|
|
pcm[i * 2] = (byte) (pcmValue & 0xFF);
|
|
pcm[i * 2 + 1] = (byte) ((pcmValue >> 8) & 0xFF);
|
|
}
|
|
|
|
return pcm;
|
|
}
|
|
}
|