258 lines
8.7 KiB
Java
258 lines
8.7 KiB
Java
package Audio;
|
|
|
|
import lombok.Getter;
|
|
import org.tinylog.Logger;
|
|
|
|
import java.io.File;
|
|
|
|
|
|
public class AudioPlayer {
|
|
private final Bass bass;
|
|
private int deviceid = -1;
|
|
@Getter private boolean inited = false;
|
|
|
|
int playbackhandle = 0;
|
|
float playbackvolume = 1.0f;
|
|
|
|
public AudioPlayer(){
|
|
bass = Bass.Instance;
|
|
Logger.info("Bass Version = {}", Integer.toHexString(bass.BASS_GetVersion()));
|
|
}
|
|
|
|
/**
|
|
* Unload Bass
|
|
*/
|
|
public void Unload(){
|
|
if (inited){
|
|
Logger.info("Freeing Device {}", deviceid);
|
|
bass.BASS_SetDevice(deviceid);
|
|
bass.BASS_Free();
|
|
}
|
|
deviceid = -1;
|
|
inited = false;
|
|
}
|
|
|
|
/**
|
|
* Detect Output Devices
|
|
*/
|
|
public void DetectOutputDevices(){
|
|
Logger.info("Detecting Output Devices...");
|
|
int ii = 1;
|
|
while (true){
|
|
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
|
|
if (bass.BASS_GetDeviceInfo(ii, info)){
|
|
Logger.info("Device {} = {}, flags = {}", ii, info.name, Integer.toHexString(info.flags));
|
|
ii++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get BASS_DEVICEINFO for a device
|
|
* @param device device id
|
|
* @return BASS_DEVICEINFO object or null if failed
|
|
*/
|
|
public Bass.BASS_DEVICEINFO GetDeviceInfo(int device){
|
|
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
|
|
if (bass.BASS_GetDeviceInfo(device, info)){
|
|
return info;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Find Device ID with Name
|
|
* @param name device name
|
|
* @return device id, or -1 if not found
|
|
*/
|
|
public int FindDeviceIDWithName(String name){
|
|
int result = -1;
|
|
int ii = 1;
|
|
while(true){
|
|
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
|
|
if (bass.BASS_GetDeviceInfo(ii, info)){
|
|
if (info.name.contains(name)){
|
|
result = ii;
|
|
break;
|
|
}
|
|
ii++;
|
|
} else {
|
|
// gak ada lagi
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Open Output Device
|
|
* @param device device id, starts from 1
|
|
* @param freq output frequency
|
|
* @return true if success
|
|
*/
|
|
public boolean OpenDevice(int device, int freq){
|
|
int flag = Bass.BASS_DEVICE_REINIT | Bass.BASS_DEVICE_16BITS | Bass.BASS_DEVICE_MONO | Bass.BASS_DEVICE_FREQ;
|
|
boolean success = bass.BASS_Init(device, freq, flag);
|
|
if (success){
|
|
Logger.info("Device {} opened successfully", device);
|
|
deviceid = device;
|
|
inited = true;
|
|
} else {
|
|
Logger.error("Failed to open device {}, Error Code {}", device, bass.BASS_ErrorGetCode());
|
|
}
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* Set Master Output Volume
|
|
* Master Output Volume related to the system volume (Alsamixer on Linux, Volume Mixer on Windows)
|
|
* @param value volume value, 0-100
|
|
*/
|
|
public void setMasterVolume(int value){
|
|
if (value<0) value = 0;
|
|
if (value>100) value = 100;
|
|
if (inited){
|
|
bass.BASS_SetDevice(deviceid);
|
|
if (!bass.BASS_SetVolume(value/100.0f)){
|
|
Logger.error("Failed to set Master Volume to {}, Error Code {}", value, bass.BASS_ErrorGetCode());
|
|
} else Logger.info("Master Volume set to {}", value);
|
|
} else Logger.info("AudioPlayer not initialized");
|
|
}
|
|
|
|
/**
|
|
* Get Master Output Volume
|
|
* Master Output Volume related to the system volume (Alsamixer on Linux, Volume Mixer on Windows)
|
|
* @return 0 - 100, or -1 if failed
|
|
*/
|
|
@SuppressWarnings("unused")
|
|
public int getMasterVolume(){
|
|
if (inited){
|
|
bass.BASS_SetDevice(deviceid);
|
|
float vol = bass.BASS_GetVolume();
|
|
if (vol>=0 && vol<=1){
|
|
return (int)(vol*100);
|
|
}
|
|
} else Logger.info("AudioPlayer not initialized");
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Get Playback Volume
|
|
* is the volume of the currently playing audio file
|
|
* @return 0 - 100
|
|
*/
|
|
public int getPlaybackvolume(){
|
|
if (playbackvolume<0)
|
|
return 0;
|
|
else if (playbackvolume>1.0f)
|
|
return 100;
|
|
else
|
|
return (int)(playbackvolume*100);
|
|
}
|
|
|
|
/**
|
|
* Set Playback Volume
|
|
* is the volume of the currently playing audio file
|
|
* @param value 0 - 100
|
|
*/
|
|
public void setPlaybackvolume(int value){
|
|
if (value<0) value = 0;
|
|
if (value>100) value = 100;
|
|
playbackvolume = value/100.0f;
|
|
if (playbackhandle!=0){
|
|
bass.BASS_ChannelSetAttribute(playbackhandle, Bass.BASS_ATTRIB_VOL, playbackvolume);
|
|
}
|
|
}
|
|
|
|
private float lastplaybackvolume = 1.0f;
|
|
|
|
/**
|
|
* Set Playback Volume to 0
|
|
*/
|
|
public void Mute(){
|
|
lastplaybackvolume = playbackvolume;
|
|
setPlaybackvolume(0);
|
|
}
|
|
|
|
/**
|
|
* Set Playback Volume to last volume before Mute
|
|
*/
|
|
public void Unmute(){
|
|
setPlaybackvolume((int)lastplaybackvolume*100);
|
|
}
|
|
|
|
/**
|
|
* Open Audio File
|
|
* @param ff audio file name to open
|
|
* @return AudioFileProperties object or null if failed
|
|
*/
|
|
public AudioFileProperties OpenAudioFile(File ff){
|
|
if (inited){
|
|
int handle = bass.BASS_StreamCreateFile(false, ff.getAbsolutePath(), 0, 0,0);
|
|
if (handle!=0){
|
|
Logger.info("Audio file {} opened successfully", ff.getName());
|
|
AudioFileProperties prop = new AudioFileProperties(handle, ff.getName());
|
|
prop.Length = bass.BASS_ChannelGetLength(handle, Bass.BASS_POS_BYTE);
|
|
prop.duration = bass.BASS_ChannelBytes2Seconds(handle, prop.Length);
|
|
return prop;
|
|
} else Logger.error("Failed to open audio file {}, Error Code {}", ff.getAbsolutePath(), bass.BASS_ErrorGetCode());
|
|
} else Logger.info("AudioPlayer not initialized");
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Close Audio File
|
|
* @param prop AudioFileProperties object to close
|
|
*/
|
|
public void CloseAudioFile(AudioFileProperties prop) {
|
|
playbackhandle = 0;
|
|
if (inited) {
|
|
if (prop != null) {
|
|
if (!bass.BASS_StreamFree(prop.handle)) {
|
|
{
|
|
Logger.error("Failed to close audio file {}, Error Code {}", prop.filename, bass.BASS_ErrorGetCode());
|
|
}
|
|
} else Logger.info("Audio file {} closed successfully", prop.filename);
|
|
} else Logger.info("AudioPlayer not initialized");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Play Audio File
|
|
* @param prop AudioFileProperties object to play
|
|
* @param looping set true to loop the audio file
|
|
* @param event PlaybackEvent object to handle playback event
|
|
*/
|
|
public void PlayAudioFile(AudioFileProperties prop, boolean looping, PlaybackEvent event){
|
|
if (inited){
|
|
if (prop!=null){
|
|
if (bass.BASS_ChannelStart(prop.handle)){
|
|
playbackhandle = prop.handle;
|
|
bass.BASS_ChannelSetAttribute(prop.handle, Bass.BASS_ATTRIB_VOL, playbackvolume);
|
|
if (looping) bass.BASS_ChannelFlags(prop.handle, Bass.BASS_SAMPLE_LOOP, Bass.BASS_SAMPLE_LOOP);
|
|
if (event!=null) event.onPlaybackStart(prop);
|
|
int devfailsync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_DEV_FAIL,0, (handle, channel, data, user)->{
|
|
if (event!=null) event.onPlaybackFailure(prop, "Device Failure");
|
|
}, null);
|
|
if (devfailsync==0) Logger.error("Failed to set Device Failure Sync, Error Code {}", bass.BASS_ErrorGetCode());
|
|
int endsync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_END, 0, (handle, channel, data, user)->{
|
|
if (looping) {
|
|
if (event!=null) event.onPlaybackLooped(prop);
|
|
} else if (event!=null) event.onPlaybackFinished(prop);
|
|
}, null);
|
|
if (endsync==0) Logger.error("Failed to set End Sync, Error Code {}", bass.BASS_ErrorGetCode());
|
|
} else {
|
|
if (event!=null) event.onPlaybackFailure(prop, String.format("Failed to play audio file %s, Error Code %d", prop.filename, bass.BASS_ErrorGetCode()));
|
|
}
|
|
} else {
|
|
if (event!=null) event.onPlaybackFailure(null, "AudioFileProperties is null");
|
|
}
|
|
} else {
|
|
if (event!=null) event.onPlaybackFailure(prop, "AudioPlayer not initialized");
|
|
}
|
|
}
|
|
|
|
}
|