first commit

This commit is contained in:
2024-11-28 13:06:18 +07:00
commit 2b1fe05f49
285 changed files with 24966 additions and 0 deletions

BIN
lib/arm64-v8a/libbass.so Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/arm64-v8a/libbass_fx.so Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/arm64-v8a/libbassape.so Normal file

Binary file not shown.

BIN
lib/arm64-v8a/libbassdsd.so Normal file

Binary file not shown.

BIN
lib/arm64-v8a/libbassenc.so Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/arm64-v8a/libbasshls.so Normal file

Binary file not shown.

Binary file not shown.

BIN
lib/arm64-v8a/libbassmix.so Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/arm64-v8a/libbasswv.so Normal file

Binary file not shown.

BIN
lib/arm64-v8a/libtags.so Normal file

Binary file not shown.

BIN
lib/armeabi-v7a/libbass.so Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/armeabi-v7a/libtags.so Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,354 @@
package AndroidRelated;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASS.BASS_DEVICEINFO;
import com.un4seen.bass.BASS.bassconstant;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.objects.collections.List;
import anywheresoftware.b4a.objects.collections.Map;
import jbass.Bass_DeviceInfo;
@BA.ShortName("AndroidNetworkReceiver")
@BA.Events(values= {
"log(msg as string)",
"udpserver(isopened as boolean)",
"playbackstarted(sourceip as string)",
"playbackfinished(sourceip as string)",
"playbackfailed(sourceip as string)"
})
public class AndroidNetworkReceiver implements NetworkReceiverUDPClientEvent {
private BA bax;
private Object caller;
private String event;
private boolean need_log_event = false;
private boolean need_udpserver_event = false;
private boolean need_playbackstarted_event = false;
private boolean need_playbackfinished_event = false;
private boolean need_playbackfailed_event = false;
private boolean inited = false;
private BASS bass;
private int playback_dev_id = -1;
private Boolean udpisworking = false;
private Map streamingmap ;
/**
* Initialize AndroidNetworkReceiver
* @param callerobject : caller object
* @param eventname : event name
*/
public void Initialize(BA ba, Object callerobject, String eventname) {
if (ba instanceof BA) {
bax = ba;
if (callerobject!=null) {
caller = callerobject;
if ( eventname instanceof String) {
if (!eventname.isEmpty()) {
event = eventname;
need_log_event = bax.subExists(event+"_log");
need_udpserver_event = bax.subExists(event+"_udpserver");
need_playbackstarted_event = bax.subExists(event+"_playbackstarted");
need_playbackfinished_event = bax.subExists(event+"_playbackfinished");
need_playbackfailed_event = bax.subExists(event+"_playbackfailed");
bass = new BASS();
if (bass.BASS_GetVersion()!=0) {
inited = true;
}
}
}
}
}
streamingmap = new Map();
streamingmap.Initialize();
}
/**
* Check if AndroidNetworkReceiver is initialized or not
* @return true if inited
*/
public boolean IsInitialized() {
return inited;
}
//////////////////// AUDIO SECTION /////////////////////////
/**
* Get a List containing all Bass_DeviceInfo from Playback
* @return List
*/
public List GetAllPlaybackDevice() {
List result = new List();
result.Initialize();
if (inited) {
for(int ii=0;ii<10;ii++) {
BASS_DEVICEINFO devinfo = new BASS_DEVICEINFO();
if (bass.BASS_GetDeviceInfo(ii, devinfo)) {
Bass_DeviceInfo dev = new Bass_DeviceInfo(ii, devinfo);
if (dev.isvalid) {
result.Add(dev);
}
}
}
}
return result;
}
/**
* Close Playback Device and free Playback resources
*/
public void ClosePlaybackDevice() {
if (playback_dev_id>-1) {
if (inited) {
if (bass.BASS_SetDevice(playback_dev_id)) {
if (bass.BASS_Stop()) {
if (bass.BASS_Free()) {
raise_log_event("ClosePlaybackDevice success");
} else raise_log_event("ClosePlaybackDevice Free failed, Msg : "+bass.GetBassErrorString());
} else raise_log_event("ClosePlaybackDevice Stop failed, Msg : "+bass.GetBassErrorString());
} else raise_log_event("ClosePlaybackDevice SetDevice failed, Msg : "+bass.GetBassErrorString());
}
playback_dev_id = -1;
}
}
/**
* Open Playback Device
* @param deviceindex : device index
* @param samplingrate : sampling rate
* @return true if can be opened
*/
public boolean OpenPlaybackDevice(int deviceindex, int samplingrate) {
if (inited) {
ClosePlaybackDevice();
int initflag = bassconstant.BASS_DEVICE_16BITS | bassconstant.BASS_DEVICE_MONO | bassconstant.BASS_DEVICE_FREQ;
if (bass.BASS_Init(deviceindex, samplingrate, initflag)) {
// sampe sini dah init, dan udah automatically setdevice
if (bass.BASS_Start()) {
playback_dev_id = deviceindex;
if (playback_dev_id>0) {
// NO SOUND (device 0) tidak bisa SetVolume
if (!bass.BASS_SetVolume(1.0f)) raise_log_event("OpenPlaybackDevice SetVolume failed, Msg : "+bass.GetBassErrorString());
}
return true;
} else raise_log_event("OpenPlaybackDevice Start failed, Msg : "+bass.GetBassErrorString());
} else raise_log_event("OpenPlaybackDevice Init failed, Msg : "+bass.GetBassErrorString());
}
return false;
}
////////////////// NETWORKING SECTION ///////////////////////////////////////////////
/**
* start UDP Server for data streaming
* @param listenport : listening port
* @param maxbytesperpacket : maximum bytes per UDP packet. Exceed this value, will be cropped
* @return true if can be opened
*/
public boolean Start_UDP_Server(int listenport, int maxbytesperpacket) {
Stop_UDP_Server();
if (maxbytesperpacket<1) maxbytesperpacket = 1000;
try {
DatagramSocket newudp = new DatagramSocket(listenport);
Thread tx = new Thread(new UDPThread(newudp, listenport, maxbytesperpacket));
tx.start();
return true;
} catch (SocketException e) {
raise_log_event("Failed Start_UDP_Server at port "+listenport+", Msg : "+e.getMessage());
return false;
}
}
/**
* Stop UDP Server
*/
public void Stop_UDP_Server() {
synchronized(udpisworking) {
udpisworking = false;
}
}
/**
* Add an IP Address to be valid to stream with UDP Server
* @param ipaddress : Ip address
* @return newly added NetworkReceiverUDPClient , or null if failed
*/
public NetworkReceiverUDPClient Add_UDP_Client(String ipaddress) {
if (streamingmap instanceof Map) {
if (streamingmap.IsInitialized()) {
NetworkReceiverUDPClient newclient = new NetworkReceiverUDPClient(ipaddress, bass, this);
streamingmap.Put(ipaddress, newclient);
return newclient;
}
}
return null;
}
/**
* Remove an IP address from map. after removed, data from this IP will be ignored
* @param ipaddress : Ip address
* @return NetworkReceiverUDPClient that being removed, or null if not exist
*/
public NetworkReceiverUDPClient Remove_UDP_Client(String ipaddress) {
if (streamingmap instanceof Map) {
if (streamingmap.IsInitialized()) {
if (streamingmap.ContainsKey(ipaddress)) {
Object obj = streamingmap.Remove(ipaddress);
if (obj instanceof NetworkReceiverUDPClient) {
NetworkReceiverUDPClient client = (NetworkReceiverUDPClient) obj;
client.StopBuffering();
return client;
}
}
}
}
return null;
}
private class UDPThread implements Runnable{
private final int udpport;
private final DatagramSocket udpsock;
private final int maxpackagesize;
public UDPThread(DatagramSocket udp, int listenport, int maxsize) {
udpsock = udp;
udpport = listenport;
maxpackagesize = maxsize;
synchronized(udpisworking) {
if (udpsock instanceof DatagramSocket) {
udpisworking = true;
} else {
udpisworking = false;
}
}
}
@Override
public void run() {
if (udpisworking) {
raise_udpserver(true, udpport);
// muter muter di sini
while(true) {
if (udpisworking) {
if (udpsock instanceof DatagramSocket) {
DatagramPacket pkg = new DatagramPacket(new byte[maxpackagesize],maxpackagesize);
try {
udpsock.receive(pkg);
} catch (IOException e) {
raise_log_event("UDP receive exception, Msg : "+e.getMessage());
break; // exit while
}
// kalau sampe sini, packet data sudah diterima
String key = pkg.getAddress().getHostAddress();
if (streamingmap instanceof Map) {
if (streamingmap.IsInitialized()) {
Object obj = streamingmap.Get(key);
if (obj instanceof NetworkReceiverUDPClient) {
NetworkReceiverUDPClient client = (NetworkReceiverUDPClient) obj;
if (client.getUDPPort()!=pkg.getPort()) {
raise_log_event("UDP Port for Streaming from "+client.getRemoteIP()+" is changed from "+client.getUDPPort()+" to "+pkg.getPort());
client.setUDPPort(pkg.getPort());
}
client.StoreBytes(pkg.getData(), pkg.getLength());
}
}
}
continue;
}
}
break;
}
}
synchronized(udpisworking) {
udpisworking = false;
}
raise_udpserver(false, udpport);
}
}
private void raise_log_event(String msg) {
if (need_log_event) bax.raiseEventFromDifferentThread(caller, null, 0, event+"_log", false, new Object[] {msg});
}
private void raise_udpserver(boolean value, int portnumber) {
if (need_udpserver_event) bax.raiseEventFromDifferentThread(caller, null, 0, event+"_udpserver", false, new Object[] {value, portnumber});
}
//TODO: lanjutin ini
@SuppressWarnings("unused")
private void raise_playbackstarted_event(String sourceip) {
if (need_playbackstarted_event) bax.raiseEventFromDifferentThread(caller, null, 0, event+"_playbackstarted", false, new Object[] {sourceip});
}
//TODO: lanjutin ini
@SuppressWarnings("unused")
private void raise_playbackfinished_event(String sourceip) {
if (need_playbackfinished_event) bax.raiseEventFromDifferentThread(caller, null, 0, event+"_playbackfinished", false, new Object[] {sourceip});
}
//TODO: lanjutin ini
@SuppressWarnings("unused")
private void raise_playbackfailed_event(String sourceip) {
if (need_playbackfailed_event) bax.raiseEventFromDifferentThread(caller, null, 0, event+"_playbackfailed", false, new Object[] {sourceip});
}
//////////// Event dari NetworkReceiverUDPClient /////////////////////
@Override
@BA.Hide
public void log(String senderip, String msg) {
raise_log_event("Log from "+senderip+" : "+msg);
}
//TODO: lanjutin ini
@Override
@BA.Hide
public void handleisready(String senderip, int handle) {
// TODO Auto-generated method stub
}
//TODO: lanjutin ini
@Override
@BA.Hide
public void handleisinvalid(String senderip) {
// TODO Auto-generated method stub
}
//TODO: lanjutin ini
@Override
@BA.Hide
public void handleisfreed(String senderip, int handle) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,170 @@
package AndroidRelated;
import java.io.IOException;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASS.bassconstant;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.objects.streams.File;
@BA.ShortName("AudioFileInformationV2")
public class AudioFileInformationV2 {
private final String filename;
private int handle=0;
private int size=-1;
private double seconds=-1;
private boolean found = false;
private boolean valid=false;
private final int _samplingrate;
private final int _initflag = bassconstant.BASS_DEVICE_16BITS | bassconstant.BASS_DEVICE_MONO;
private final int _loadflag = bassconstant.BASS_STREAM_DECODE | bassconstant.BASS_SAMPLE_MONO;
private final int KB_threshold = 1024;
private final int MB_threshold = 1024 * 1024;
private final int GB_threshold = 1024 * 1024 * 1024;
private final int minute_threshold = 60;
private final int hour_threshold = 60 * 60;
private BASS bass;
public AudioFileInformationV2() {
filename ="";
_samplingrate=44100;
}
public AudioFileInformationV2(BASS _bass,String path, int samplingrate) {
bass = _bass;
filename = path;
this._samplingrate = samplingrate;
try {
found = File.Exists("", path);
} catch (IOException e) {
BA.Log("Unable to check existanceo of "+path+", Msg : "+e.getMessage());
return;
}
if (bass instanceof BASS) {
if (bass.BASS_GetVersion()!=0) {
bass.BASS_Init(0, _samplingrate, _initflag);
handle = bass.BASS_StreamCreateFile(false, filename, 0, 0, _loadflag);
if (handle!=0) {
size = (int)bass.BASS_ChannelGetLength(handle, bassconstant.BASS_POS_BYTE);
if (size>-1) {
seconds = bass.BASS_ChannelBytes2Seconds(handle, size);
if (seconds>-1) {
valid = true;
}
}
bass.BASS_StreamFree(handle);
}
}
}
}
/***
* Check if file is found
* @return true if found
*/
public boolean isfound() {
return found;
}
/**
* Check if file is valid
* @return true if valid
*/
public boolean isvalid() {
return valid;
}
/**
* Size in bytes
* @return value in bytes
*/
public int size_in_bytes() {
return size;
}
/**
* Return size in String
* if invalid, return N/A
* if < Kilobytes, return [X] B
* if < Megabytes, return [X] KB
* if < Gigabytes, return [X] MB
* else return [X] GB
* @return value in string
*/
public String size_in_String() {
if (size<0) {
return "N/A";
} else if (size<KB_threshold) {
return String.format("%d B", size);
} else if (size < MB_threshold) {
return String.format("%.1f KB" , (float)size / KB_threshold);
} else if (size < GB_threshold) {
return String.format("%.1f MB" , (float)size / MB_threshold);
} else {
return String.format("%.1f GB" , (float)size / GB_threshold);
}
}
/**
* Return length in seconds
* if invalid, return less than 0
* @return value in seconds
*/
public double length_in_seconds() {
return seconds;
}
/**
* Return length in string
* if invalid, return N/A
* if < 60 second, return [X]
* if < 3600 seconds, return [X] M [Y] S
* else return [X] H [Y] M [Z] S
* @return String
*/
public String length_in_String() {
if (seconds<0) {
return "N/A";
} else if (seconds < minute_threshold) {
return String.format("%d", seconds);
} else if (seconds < hour_threshold) {
int _minute = (int)(seconds / minute_threshold);
int _second = (int)(seconds % minute_threshold);
return String.format("%d M %d S", _minute, _second);
} else {
int _hour = (int) (seconds / hour_threshold);
int remainder = (int)(seconds - (_hour * hour_threshold));
int _minute = (int) (remainder / minute_threshold);
int _second = (int)(remainder % minute_threshold);
return String.format("%d H %d M %d S", _hour, _minute, _second);
}
}
/**
* Return array of bytes for PCM data
* @return null if invalid
*/
public byte[] GetBytes() {
if (valid) {
if (size>0) {
handle = bass.BASS_StreamCreateFile(false, filename, 0, 0, _loadflag);
if (handle!=0) {
Pointer pp = new Memory(size);
int readsize = bass.BASS_ChannelGetData(handle, pp, size);
if (readsize>0) {
return pp.getByteArray(0, readsize);
}
}
}
}
return null;
}
}

View File

@@ -0,0 +1,254 @@
package AndroidRelated;
import java.nio.ByteBuffer;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASS.BASS_DEVICEINFO;
import com.un4seen.bass.BASS.BASS_FILEPROCS;
import com.un4seen.bass.BASS.FILECLOSEPROC;
import com.un4seen.bass.BASS.FILELENPROC;
import com.un4seen.bass.BASS.FILEREADPROC;
import com.un4seen.bass.BASS.FILESEEKPROC;
import com.un4seen.bass.BASS.bassconstant;
import anywheresoftware.b4a.BA;
@BA.ShortName("NetworkReceiverUDPClient")
public class NetworkReceiverUDPClient {
private final String targetip;
private int targetport;
private final int sizeinterval = 1024 * 1024; // penambahan size tiap 1MB
private int capacity = 5 * sizeinterval; // capacity awal
private ByteBuffer buf;
private int totalbytesreceived = 0; // total sudah ketulis
private boolean inited = false; // indicator for NetworkReceiverUDPClient inited or not
private BASS mybass;
private final int deviceid = 0; // use NO SOUND
private final int samplingrate = 48000; // samplingrate = 48K
private final int initflag = bassconstant.BASS_DEVICE_16BITS | bassconstant.BASS_DEVICE_FREQ | bassconstant.BASS_DEVICE_MONO;
private boolean dev_enabled = false;
private boolean dev_inited = false;
private int handle = 0;
private NetworkReceiverUDPClientEvent myevent;
private Thread tx;
public NetworkReceiverUDPClient(String ipaddress, BASS bass, NetworkReceiverUDPClientEvent event) {
targetip = ipaddress;
mybass = bass;
myevent = event;
inited = false;
if (!(mybass instanceof BASS)) {
myevent.log(targetip,"NetworkReceiverUDPClient failed, mybass is null");
return;
}
if (mybass.BASS_GetVersion()==0) {
myevent.log(targetip,"NetworkReceiverUDPClient failed, mybass version = 0");
return;
}
BASS_DEVICEINFO dev = new BASS_DEVICEINFO();
if (!mybass.BASS_GetDeviceInfo(deviceid, dev)) {
myevent.log(targetip,"NetworkReceiverUDPClient failed, unable to GetDeviceInfo device="+deviceid);
return;
}
// sampai sini, GetDeviceInfo bisa
dev.read();
dev_enabled = (dev.flags & bassconstant.BASS_DEVICE_ENABLED) > 0 ? true : false;
if (! dev_enabled) {
myevent.log(targetip,"NetworkReceiverUDPClient failed, device="+deviceid+" is disabled");
return;
}
dev_inited = (dev.flags & bassconstant.BASS_DEVICE_INIT) > 0 ? true : false;
if (! dev_inited) {
if (!bass.BASS_Init(deviceid, samplingrate, initflag)) {
// gagal init
myevent.log(targetip,"NetworkReceiverUDPClient failed, device="+deviceid+" can't be inited");
return;
}
}
// sampai sini, dev_inited sudah true
inited = true;
buf = ByteBuffer.allocate(capacity);
tx = new Thread(new BufferingThread());
tx.start();
}
public boolean IsInitialized() {
return inited;
}
public void StopBuffering() {
if (tx instanceof Thread) {
if (tx.isAlive()) {
tx.interrupt();
}
}
}
@BA.Hide
public void setUDPPort(int value) {
targetport = value;
}
public int getUDPPort() {
return targetport;
}
public String getRemoteIP() {
return targetip;
}
/**
* How many bytes that stored
* @return value in integer
*/
public int TotalSizeReceived() {
return totalbytesreceived;
}
/**
* Store data bytes
* @param buffer : data bytes
* @param length : length to store
* @return how many bytes actually stored
*/
public int StoreBytes(byte[] buffer, int length) {
if (buf instanceof ByteBuffer) {
if (buffer != null) {
if (length>0) {
if (length > buffer.length) length = buffer.length;
synchronized(buf) {
if (buf.remaining() < length) {
// buffer gak cukup, gedein capacity
capacity = capacity + sizeinterval;
ByteBuffer newbuf = ByteBuffer.allocate(capacity); // bikin newbuffer dengan kapasitas + ditambah length
newbuf.put(buf); // isi newbuf dengan buf
buf = newbuf; // replace buf dengan newbuf
myevent.log(targetip, "Buffer increased by 1M");
}
buf.put(buffer, 0, length); // masukin data ke buf
buf.notifyAll();
}
totalbytesreceived+=length;
return length;
}
}
}
return 0;
}
private class BufferingThread implements Runnable{
private FILEREADPROC fr;
private FILELENPROC fl;
private FILECLOSEPROC fc;
private FILESEEKPROC fs;
private BASS_FILEPROCS fileproc;
public BufferingThread() {
/// create callbacks
fr = new FILEREADPROC() {
@Override
public int FILEREADPROC(Pointer buffer, int length, Pointer user) {
synchronized(buf) {
if (buf.position()==0) {
try {
buf.wait(5000);
} catch (InterruptedException e) {
myevent.log(targetip, "buf wait timeout, no more bytes");
return 0; // kalau return 0, selesai
}
}
buf.flip(); // change to read mode
int readcount = buf.remaining();
if (readcount>length) readcount = length;
byte[] bufread = new byte[readcount];
buf.get(bufread);
buf.compact(); // back to write mode
buffer.write(0, bufread, 0, readcount);
return readcount; // ada bacaan
}
}
};
fl = new FILELENPROC() {
@Override
public long FILELENPROC(Pointer user) {
return 0; // size unknown
}
};
fs = new FILESEEKPROC() {
@Override
public boolean FILESEEKPROC(long offset, Pointer user) {
return false; // can't seek streaming
}
};
fc = new FILECLOSEPROC() {
@Override
public void FILECLOSEPROC(Pointer user) {
// do nothing here
}
};
fileproc = new BASS_FILEPROCS(fc, fl, fr, fs);
}
@Override
public void run() {
mybass.BASS_SetDevice(deviceid);
handle = mybass.BASS_StreamCreateFileUser(bassconstant.STREAMFILE_BUFFER, 0, fileproc, null);
// tunggu di sini sampe handle keluar
if (handle!=0) {
BASS.BASS_ChannelPause(handle);
myevent.handleisready(targetip, handle);
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
mybass.BASS_StreamFree(handle);
myevent.handleisfreed(targetip, handle);
handle = 0;
} else myevent.handleisinvalid(targetip);
}
};
}

View File

@@ -0,0 +1,8 @@
package AndroidRelated;
public interface NetworkReceiverUDPClientEvent {
void log(String senderip, String msg);
void handleisready(String senderip, int handle);
void handleisinvalid(String senderip);
void handleisfreed(String senderip, int handle);
}

View File

@@ -0,0 +1,624 @@
package DatareceiverRelated;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import anywheresoftware.b4a.BA;
import jbass.commoncodes;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
@BA.Events(values= {
"log(msg as string)",
"tcpconnected(success as boolean)",
"newdata(functionname as string, bb() as byte, length as int)",
"connected(ipaddress as string, portnumber as int)",
"disconnected(ipaddress as string, portnumber as int)",
"receivedbytescounter(value as long, value_in_string as string)"
})
@BA.ShortName("NetworkDataReceiver")
public class DataReceiver {
private BA ba;
private boolean inited = false;
private String event;
private DataReceiverEvent dre;
private boolean need_log_event = false;
private boolean need_tcpconnected_event = false;
private boolean need_newdata_event = false;
private boolean need_connected_event = false;
private boolean need_disconnected_event = false;
private boolean need_receivedbytescounter_event = false;
private DatagramSocket udp;
private InetSocketAddress udp_sa;
private MulticastSocket mulsock;
private InetSocketAddress multicastaddress;
private NetworkInterface mulsockinterface = null;
private Socket tcpsock;
private InetSocketAddress tcp_sa;
private Thread rx;
private long rxbytes = 0;
private Object myobject;
/**
* How many bytes already received by Network Receiver Unit
* @return value in long
*/
public long getNetworkReceivedBytes() {
return rxbytes;
}
/**
* How many bytes already received by Network Receiver Unit, stated in readable String
* @return value in string
*/
public String getNetworkReceivedBytes_in_String() {
return commoncodes.ByteCounter_to_String(rxbytes);
}
@BA.Hide
public void SetDataReceiverEvent(DataReceiverEvent xx) {
dre = xx;
}
public void Initialize(BA bax, Object callerobject, String eventname) {
ba = bax;
event = eventname;
myobject = callerobject;
if (ba!=null) {
if (myobject!=null) {
if (!event.isEmpty()) {
need_log_event = ba.subExists(event+"_log");
need_tcpconnected_event = ba.subExists(event+"_tcpconnected");
need_newdata_event = ba.subExists(event+"_newdata");
need_connected_event = ba.subExists(event+"_connected");
need_disconnected_event = ba.subExists(event+"_disconnected");
need_receivedbytescounter_event = ba.subExists(event+"_receivedbytescounter");
inited = true;
}
}
}
}
public boolean IsInitialized() {
return inited;
}
/**
* on UDP Client, return UDP Server IP
* on TCP Client, return TCP server IP
* on Join Multicast, return UDP sender IP in Multicast, that has been filtered
* @return IP address on string
*/
public String GetCurrentNetworkSource() {
if (udp_sa!=null) {
return udp_sa.getAddress().getHostAddress();
} else if (tcp_sa!=null) {
return tcp_sa.getAddress().getHostAddress();
}else return "N/A";
}
/**
* on UDP Client, return UDP Server Port
* on TCP Client , return TCP server port
* on JOin Multicast, return UDP Sender Port in Multicast, that has been filtered
* @return -1 if not joining
*/
public int GetCurrentNetworkPort() {
if (udp_sa!=null) {
return udp_sa.getPort();
} else if (tcp_sa!=null) {
return tcp_sa.getPort();
} else return -1;
}
/**
* Return Multicast Address joined, or empty string if not joining
* @return Multicast address in string
*/
public String GetMulticastAddress() {
if (multicastaddress!=null) {
return multicastaddress.getAddress().getHostAddress();
} else {
return "";
}
}
/**
* Return Multicast Port joined, or -1 if not joining
* @return -1 if not joining
*/
public int GetMulticastPort() {
if (multicastaddress!=null) {
return multicastaddress.getPort();
} else {
return -1;
}
}
/***
* Connect to a UDP Client
* @param targetip : UDP Target IP
* @param targetport : UDP Target Port
* @param maxpackagesize : package read size
* @return true if connected
*/
public boolean Start_UDP_Connecting(String targetip, int targetport, int maxpackagesize) {
if (maxpackagesize<1) maxpackagesize = 1000; // default size is 1000 bytes
try {
udp = new DatagramSocket();
} catch (SocketException e) {
raise_log_exception("Start_UDP_Connecting","DatagramSocket()",e);
return false;
}
udp_sa = new InetSocketAddress(targetip, targetport);
try {
udp.connect(udp_sa);
raise_connected(udp_sa);
raise_log("Start UDP Connecting to "+udp_sa.getAddress().getHostAddress()+":"+udp_sa.getPort());
} catch (SocketException e) {
raise_disconnect(udp_sa);
raise_log_exception("Start_UDP_Connecting","Connect",e);
return false;
}
if (udp.isConnected()) {
rx = new Thread(new UDP_Receive_Thread(maxpackagesize, targetip));
rx.start();
return true;
} else {
return false;
}
}
/**
* Start UDP Listening
* @param listenport : listen port
* @param ipsource : IP address of source stream wanted to receive, or fill with empty string for all address
* @param maxpackagesize : package read size
* @return true if success
*/
public boolean Start_UDP_Listening(int listenport, String ipsource, int maxpackagesize) {
if (maxpackagesize<1) maxpackagesize = 1000; // default size is 1000 bytes
try {
udp = new DatagramSocket(listenport);
raise_log("Start UDP Listening at "+udp.getLocalAddress().getHostAddress()+":"+udp.getLocalPort());
} catch (SocketException e) {
raise_log_exception("Start_UDP_Listening","DatagramSocket("+listenport+")", e);
return false;
}
rx = new Thread(new UDP_Receive_Thread(maxpackagesize, ipsource)) ;
rx.start();
return true;
}
/**
* Start Multicast Joining
* @param localip : Ethernet IP used for this Multicast Joining
* @param ipsource : IP address of source stream wanted to receive, or fill with empty string for all address
* @param multicastip : Multicast IP address
* @param multicastport : Multicast Port
* @param maxpackagesize : package read size
* @return true if success
*/
public boolean Start_Multicast_Join(String localip, String ipsource, String multicastip, int multicastport, int maxpackagesize) {
mulsockinterface = null;
multicastaddress = null;
try {
mulsock = new MulticastSocket();
} catch (IOException e) {
raise_log_exception("Start_Multicast_Join","create MulticastSocket",e);
return false;
}
try {
NetworkInterface nif = NetworkInterface.getByInetAddress(InetAddress.getByName(localip));
mulsockinterface = nif;
} catch (SocketException | UnknownHostException e) {
raise_log_exception("Start_Multicast_Join","GetNetworkInterface from LocalIP",e);
return false;
}
try {
InetSocketAddress xx = new InetSocketAddress(multicastip, multicastport); // alamat ketemuan di multicast address
mulsock.joinGroup(multicastaddress, mulsockinterface);
multicastaddress = xx;
} catch (IOException e) {
raise_log_exception("Start_Multicast_Join","JoinGroup",e);
return false;
}
rx = new Thread(new Multicast_Receive_Thread(maxpackagesize, ipsource));
rx.start();
return true;
}
/**
* Connect to TCP Server
* Use event tcpconnected(success as boolean) to wait
* @param targetip : Server IP
* @param targetport : Server Port
* @param timeout : timeout to wait, in milisecond
* @param maxpackagesize : package read size
*/
public void Start_TCP_Connecting(String targetip, int targetport, int timeout, int maxpackagesize) {
tcpsock = new Socket();
tcp_sa = new InetSocketAddress(targetip, targetport);
Thread ct = new Thread() {
public void run() {
try {
tcpsock.connect(tcp_sa, timeout);
raise_tcpconnectresult(tcpsock.isConnected());
if (tcpsock.isConnected()) {
raise_connected(tcp_sa);
rx = new Thread(new TCP_Receive_Thread(maxpackagesize));
rx.start();
}
} catch (IOException e) {
raise_log("Exception on TCP Connect to "+targetip+":"+targetport+", Msg="+e.getMessage()+", Caused="+e.getCause());
raise_tcpconnectresult(false);
}
}
};
ct.start();
}
private class Multicast_Receive_Thread implements Runnable{
private int packetsize = 1000;
private String ipfilter = "";
public Multicast_Receive_Thread(int maxpkgsize, String targetip) {
packetsize = maxpkgsize;
ipfilter = targetip;
}
private boolean StillValid() {
if (mulsock!=null) {
if (multicastaddress!=null) {
return true;
}
}
return false;
}
@Override
public void run() {
boolean has_receive = false;
rxbytes = 0;
raise_receivedbytescounter(rxbytes);
raise_log("Multicast_Receive_Thread started");
while(true) {
if (!StillValid()) break;
byte[] BX = new byte[packetsize];
DatagramPacket newdata = new DatagramPacket(BX, packetsize);
try {
mulsock.receive(newdata); // block di sini , sampai data baru sampe
// cek dengan IP filter
if (!ipfilter.isEmpty()) {
if (newdata.getAddress().getHostAddress().compareTo(ipfilter)!=0) continue; // kalau tidak sama dengan ip filter, buang saja
}
if (newdata.getLength()>0) {
// ada datanya
if (!has_receive) {
has_receive = true;
udp_sa = new InetSocketAddress(newdata.getAddress().getHostAddress(), newdata.getPort());
raise_connected(udp_sa);
}
rxbytes+= newdata.getLength();
raise_receivedbytescounter(rxbytes);
raise_newdata("Multicast_Receive_Thread",newdata.getData(), newdata.getLength());
}
} catch (IOException e) {
break;
}
}
//if (udp_sa!=null) raise_disconnect(udp_sa);
close_communications();
raise_log("Multicast_Receive_Thread stopped");
}
}
private class TCP_Receive_Thread implements Runnable {
private InputStream fis;
private OutputStream fos;
private boolean validsocket = false;
private int packetsize = 0;
public TCP_Receive_Thread(int pkgsize) {
if (pkgsize<1) pkgsize = 1000;
packetsize = pkgsize;
if (tcpsock instanceof Socket) {
if (tcpsock.isConnected()) {
try {
fis = tcpsock.getInputStream();
fos = tcpsock.getOutputStream();
validsocket = true;
} catch (IOException e) {
validsocket = false;
raise_log_exception("TCP_Thread","Input/OutputStream",e);
}
}
}
raise_log("TCP_Receive_Thread validsocket = "+validsocket);
}
private boolean StillValid() {
if (tcpsock instanceof Socket) {
if (tcpsock.isConnected()) {
if (fis instanceof InputStream) {
if (fos instanceof OutputStream) {
validsocket = true;
return true;
}
}
}
}
validsocket = false;
return false;
}
@Override
public void run() {
boolean has_receive = false;
rxbytes = 0;
raise_receivedbytescounter(rxbytes);
int rxcounter = 0;
raise_log("TCP_Receive_Thread started");
while(true) {
if (!StillValid()) {
//BA.Log("TCP Receive Thread StillValid() = false");
break;
}
if (!(tcp_sa instanceof InetSocketAddress)) break;
byte[] BX = new byte[packetsize];
try {
// revisi 12/10/2020, pancingan di sini
fos.write(String.valueOf(packetsize).getBytes());
rxcounter = fis.read(BX); // nge-block di sini sampe ada data
} catch (IOException e) {
raise_log_exception("TCP_Thread","Read",e);
break;
}
if (rxcounter>0) {
if (!has_receive) {
// pertama terima data
has_receive = true;
if (tcp_sa instanceof InetSocketAddress) raise_connected(tcp_sa);
}
rxbytes += rxcounter;
raise_receivedbytescounter(rxbytes);
raise_newdata("TCP_Receive_Thread",BX,rxcounter);
//raise_log("TCP_Receive_Thread got "+rxcounter+" bytes");
}
}
//if (tcp_sa!=null) raise_disconnect(tcp_sa);
close_communications();
//BA.Log("Close Communications dari TCP Receive Thread");
raise_log("TCP_Receive_Thread stopped");
}
}
private class UDP_Receive_Thread implements Runnable {
private int udpsize;
private String ipfilter="";
UDP_Receive_Thread(int maxpackagesize, String sourceIP){
udpsize = maxpackagesize;
ipfilter = sourceIP;
}
@Override
public void run() {
rxbytes = 0;
raise_receivedbytescounter(rxbytes);
boolean has_receive = false;
raise_log("UDP_Receive_Thread started");
while(true) {
if (udp==null) {
break;
}
byte[] bb = new byte[udpsize];
DatagramPacket newdata = new DatagramPacket(bb,udpsize);
try {
udp.receive(newdata); // nge-block di sini sampai dapat data
// ada data baru, cek dengan sourceip
// kalau tidak ada ipfilter, process semua data
if (!ipfilter.isEmpty()) {
// ada filter
if (newdata.getAddress().toString().compareTo(ipfilter)!=0) {
continue; // tidak sama dengan ipfilter, buang aja
}
}
if (newdata.getLength()>0) {
// ada isinya
if (!has_receive) {
has_receive = true;
if (udp_sa==null) { // pada UDP listening , UDP_SA awalnya null, jadi ini menandakan paket pertama yang diterima
// pada UDP connecting, UDP_SA sudah diset ke UDP Master
udp_sa = new InetSocketAddress(newdata.getAddress(), newdata.getPort());
}
raise_connected(udp_sa);
}
raise_newdata("UDP_Receive_Thread",newdata.getData(),newdata.getLength());
rxbytes+= newdata.getLength();
raise_receivedbytescounter(rxbytes);
}
} catch (IOException e) {
if (e.getMessage().compareToIgnoreCase("socket closed")!=0) {
// kalau socket closed , itu sudah biasa..
// kalau yang lainnya, raise_log_exception
raise_log_exception("RX_Thread","udp.receive(newdata)",e);
}
break;
}
}
//if (udp_sa!=null) raise_disconnect(udp_sa);
close_communications();
raise_log("UDP_Receive_Thread stopped");
}
}
/**
* Close UDP / TCP connections.
* This function is also called when calling Start_xxxxx command
*/
public void close_communications() {
boolean isclosingsomething = false;
if (udp!=null) {
if (udp.isConnected()) {
udp.disconnect();
if (udp_sa!=null) raise_disconnect(udp_sa);
}
udp.close();
isclosingsomething = true;
udp = null;
}
if (tcpsock instanceof Socket) {
try {
tcpsock.close();
isclosingsomething = true;
if (tcp_sa!=null) raise_disconnect(tcp_sa);
} catch (IOException e) {
raise_log_exception("close_communication","tcpsock.close",e);
}
tcpsock = null;
}
if (mulsock!=null) {
if (multicastaddress!=null) {
try {
if (multicastaddress!=null) {
mulsock.leaveGroup(multicastaddress, mulsockinterface);
raise_disconnect(multicastaddress);
}
} catch (IOException e) {
raise_log_exception("close_communication","mulsock.leavegroup",e);
}
multicastaddress = null;
}
isclosingsomething = true;
mulsock.disconnect();
mulsock.close();
mulsock = null;
}
if (rx!=null) {
rx.interrupt();
}
if (isclosingsomething) raise_log("DataReceiver communications closed");
}
private void raise_log_exception(String functionname, String command, Exception e) {
raise_log("Exception on Function="+functionname+", Command="+command+", Msg="+e.getMessage()+", Caused="+e.getCause());
}
private void raise_tcpconnectresult(boolean value) {
if (dre!=null) dre.tcpconnectresult(value);
if (need_tcpconnected_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_tcpconnected", false, new Object[] {value});
}
private void raise_log(String msg) {
if (dre!=null) dre.log(msg);
if (need_log_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
}
private void raise_disconnect(InetSocketAddress sa) {
if (dre!=null) dre.disconnected(sa);
if (need_disconnected_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_disconnected", false, new Object[] {sa.getAddress().getHostAddress(), sa.getPort()});
}
private void raise_connected(InetSocketAddress sa) {
if (dre!=null) dre.connected(sa);
if (need_connected_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_connected", false, new Object[] {sa.getAddress().getHostAddress(), sa.getPort()});
}
private void raise_newdata(String fromfunction,byte[] bb, int length) {
if (dre!=null) {
Pointer pp = new Memory(length);
pp.write(0, bb, 0, length);
dre.data_received(pp, length);
}
if (need_newdata_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_newdata", false, new Object[] {fromfunction, bb, length});
}
private void raise_receivedbytescounter(long value) {
if (need_receivedbytescounter_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_receivedbytescounter", false, new Object[] {value,commoncodes.ByteCounter_to_String(value)});
}
}

View File

@@ -0,0 +1,13 @@
package DatareceiverRelated;
import java.net.InetSocketAddress;
import com.sun.jna.Pointer;
public interface DataReceiverEvent {
public void data_received(Pointer datanya, int length);
public void log(String msg);
public void connected(InetSocketAddress sa);
public void disconnected(InetSocketAddress sa);
public void tcpconnectresult(boolean success);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
package MastersenderRelated;
public interface CustomSocketEvent {
public void log(Object sender, String msg);
public void socketclosed(Object sender);
}

View File

@@ -0,0 +1,145 @@
package MastersenderRelated;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.DateTime;
@BA.ShortName("MasterSender_DatagramSocket")
public class DatagramSocketForMasterSender {
private final String _ipkey;
private final int _portkey;
private boolean _isvalid=false;
private InetSocketAddress _sock = null;
private long create_time=0;
private long _txcount=0;
private CustomSocketEvent javaobject;
@BA.Hide
public DatagramSocketForMasterSender(final String ipaddress, final int port, final CustomSocketEvent event) {
_ipkey = ipaddress;
_portkey = port;
javaobject = event;
try {
_sock = new InetSocketAddress(_ipkey, _portkey);
_isvalid = true;
create_time = DateTime.getNow();
} catch(IllegalArgumentException | SecurityException e) {
log("Error Initializing DatagramSocketForMasterSender, Msg : "+e.getMessage() );
}
}
private void log(String msg) {
if (javaobject instanceof CustomSocketEvent) {
javaobject.log(this,msg);
}
}
/***
* Key String is [ipaddress]:[port]
* @return Key string for this class
*/
public String getKey() {
return _isvalid ? _ipkey+":"+_portkey : "N/A";
}
public String getIP() {
return _isvalid ? _ipkey : "";
}
public int getPort() {
return _isvalid ? _portkey : 0;
}
/**
* Check if Datagram Socket is valid
* @return true if valid
*/
public boolean getIsValid() {
return _isvalid;
}
/**
* Get TX counter
* @return TX counter
*/
public long getTXCounter() {
return _txcount;
}
/**
* Get Duration between creation and now
* @return duration in tick
*/
public long getDuration() {
return _isvalid ? DateTime.getNow() - create_time : 0;
}
/**
* Send Data
* @param udpsock : datagram socket to use for sending data
* @param bb : bytes to send
* @return 0 if failed, bb.length if success
*/
public int SendData(DatagramSocket udpsock, byte[] bb) {
if (_isvalid) {
if (udpsock instanceof DatagramSocket) {
DatagramPacket pkg = new DatagramPacket(bb,bb.length, _sock);
try {
udpsock.send(pkg);
_txcount+=bb.length;
return bb.length;
} catch (IOException e) {
log("SendData failed, Msg : "+e.getMessage());
if (javaobject instanceof CustomSocketEvent) {
javaobject.socketclosed(this);
}
}
}
}
return 0;
}
public int SendData_withLengthLimit(DatagramSocket udpsock , byte[] bb, final int datalength, final int maxlengthperpacket) {
if (_isvalid) {
if (udpsock instanceof DatagramSocket) {
ByteBuffer bx = ByteBuffer.wrap(bb,0,datalength);
bx.rewind();
int sentcounter = 0;
int xx = bx.capacity() / maxlengthperpacket;
int yy = bx.capacity() % maxlengthperpacket;
if (xx>0) {
for(int ii=0;ii<xx;ii++) {
byte[] mm = new byte[maxlengthperpacket];
bx.get(mm);
int sent = SendData(udpsock, mm);
if (sent>0) {
sentcounter+=sent;
}
}
}
if (yy>0) {
byte[] nn = new byte[yy];
bx.get(nn);
int sent = SendData(udpsock, nn);
if (sent>0) {
sentcounter+=sent;
}
}
return sentcounter;
}
}
return 0;
}
}

View File

@@ -0,0 +1,938 @@
package MastersenderRelated;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.objects.collections.Map;
@BA.ShortName("MasterNetworkSender")
@BA.Events(values= {
"mastersenderlog(msg as string)",
"udpmaster(isactive as boolean)",
"tcpmaster(isactive as boolean)",
"tcpsentcount(value as long)",
"udpsentcount(value as long)",
"tcpsentlog(targetip as string, targetport as int, bytecount as int)",
"udpsentlog(targetip as string, targetport as int, bytecount as int)",
"socketisclosed(socket_address as string, socket_port as int)"
})
public class MasterSender implements CustomSocketEvent {
private BA ba;
private String event;
private boolean need_log_event =false;
private boolean need_udpmaster_event = false;
private boolean need_tcpmaster_event = false;
private boolean need_tcpsentcount_event = false;
private boolean need_udpsentcount_event = false;
private boolean need_tcpsentlog_event = false;
private boolean need_udpsentlog_event = false;
private boolean need_socketisclosed_event = false;
private boolean inited = false;
private DatagramSocket udpmaster;
private boolean udpmaster_is_starting = false;
private final int udp_packet_size = 1000;
private Map udp_clients_map;
private long udp_bytesent = 0;
private ServerSocket tcpmaster;
private boolean tcpmaster_is_starting = false;
private InetSocketAddress tcp_master_sa;
private Map tcp_clients_map;
private long tcp_bytesent = 0;
private MasterSenderEvent javaevent;
private Object myobject;
public void Initialize(BA bax, Object caller, String eventname) {
ba = bax;
event = eventname;
myobject = caller;
if (ba instanceof BA) {
if (myobject != null) {
if (!event.isEmpty()) {
need_log_event = ba.subExists(event+"_mastersenderlog");
need_udpmaster_event = ba.subExists(event+"_udpmaster");
need_tcpmaster_event = ba.subExists(event+"_tcpmaster");
need_tcpsentcount_event = ba.subExists(event+"_tcpsentcount");
need_udpsentcount_event = ba.subExists(event+"_udpsentcount");
need_tcpsentlog_event = ba.subExists(event+"_tcpsentlog");
need_udpsentlog_event = ba.subExists(event+"_udpsentlog");
need_socketisclosed_event = ba.subExists(event+"_socketisclosed");
}
}
}
udp_clients_map = new Map();
udp_clients_map.Initialize();
tcp_clients_map = new Map();
tcp_clients_map.Initialize();
inited = true;
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
Stop_TCP_Master();
Stop_UDP_Master();
}
});
}
public boolean IsInitialized() {
return inited;
}
public boolean TCP_Clients_exists() {
if (tcp_clients_map instanceof Map) {
if (tcp_clients_map.IsInitialized()) {
if (tcp_clients_map.getSize()>0) {
return true;
}
}
}
return false;
}
public boolean UDP_Clients_exists() {
if (udp_clients_map instanceof Map) {
if (udp_clients_map.IsInitialized()) {
if (udp_clients_map.getSize()>0) {
return true;
}
}
}
return false;
}
@BA.Hide
public void setJavaEvent(MasterSenderEvent value) {
javaevent = value;
}
/**
* Get how much bytes already sent via TCP Master
* @return positive numbers on sending, or 0 if idle
*/
public long Get_TCP_SentBytes() {
return tcp_bytesent;
}
/**
* Get TCP Server Listening Port
* @return positive number : listening port. 0 / -1 = not listening
*/
public int Get_TCP_Master_ListeningPort() {
if (tcpmaster instanceof ServerSocket) {
return tcpmaster.getLocalPort();
} else return -1;
}
/**
* Get TCP Server Ethernet IP
* @return empty string if not listening
*/
public String Get_TCP_Master_EthernetIP() {
if (tcpmaster instanceof ServerSocket) {
if (tcpmaster.isClosed()) {
return "";
} else {
InetAddress xx = tcpmaster.getInetAddress();
if (xx==null) {
return "";
} else return xx.getHostAddress();
}
} else return "";
}
/**
* Send data bytes to targetlist
* @param bb : data to send
* @param length : length of bytes to send
* @return 0 = not sent, length = sent
*/
public int SendData_to_TCP_Clients(byte[] bb, int length) {
if (bb==null) return 0;
if (length<1) return 0;
if (bb.length<length) return 0; // data yang mau dikirim lebih kecil daripada jumlah kirim, error !
if (!TCP_Clients_exists()) {
return 0;
}
boolean sendingsomething = false;
int tcpclientsize = tcp_clients_map.getSize();
for(int ii=0;ii<tcpclientsize;ii++) {
Object sk = tcp_clients_map.GetValueAt(ii);
//revisi dari coding di bawah (15/1/2021)
if (sk instanceof TCPSocketForMasterSender) {
TCPSocketForMasterSender tcp = (TCPSocketForMasterSender) sk;
if (!tcp.getIsValid()) {
raise_log_event("Unable to TCP Send "+length+" bytes to "+tcp.getKey()+" , socket is invalid");
continue;
}
if (!tcp.getIsConnected()) {
raise_log_event("Unable to TCP Send "+length+" bytes to "+tcp.getKey()+" , socket is closed");
continue;
}
if (tcp.SendData(bb)>0) {
sendingsomething =true;
raise_tcpsentlog_event(tcp.getIP(), tcp.getPort(), length);
}
}
// ganti ke class TCPSocketForMasterSender
// if (sk instanceof Socket) {
// @SuppressWarnings("resource")
// Socket sock = (Socket) sk;
// String sock_address = sock.getInetAddress().getHostAddress();
// int sock_port = sock.getPort();
// if (sock.isClosed()) {
// raise_log_event("Unable to TCP Send "+length+" bytes to "+sock_address+":"+sock_port+" , socket is closed");
// raise_socketisclosed_event(sock_address, sock_port);
// if (tcp_clients_map.Remove(sock_address)!=null) {
// raise_log_event("Removed "+sock_address+" from tcp_clients_map");
// }
// continue;
// }
//
//
// try {
// OutputStream fos = sock.getOutputStream();
// if (fos instanceof OutputStream) {
// fos.write(bb,0, length);
// raise_tcpsentlog_event(sock_address, sock_port, length);
// sendingsomething = true;
// } else {
// raise_log_event("Unable to TCP Send "+length+" bytes to "+sock_address+":"+sock_port+", OutputStream is null");
// raise_socketisclosed_event(sock_address, sock_port);
// if (tcp_clients_map.Remove(sock_address)!=null) {
// raise_log_event("Removed "+sock_address+" from tcp_clients_map");
// }
// }
//
//
//
// } catch (IOException e) {
// raise_log_exception("SendData_to_TCP_Clients","Socket.OutputStream.Write",e);
//
// }
//
// }
//
} // selesai for(int ii=0;ii<tcpclientsize;ii++)
if (sendingsomething) {
tcp_bytesent+=length;
raise_tcpsentcount_event(tcp_bytesent);
return length;
} else return 0;
}
/**
* Stop TCP Master, and close all sockets accepted
*/
public void Stop_TCP_Master() {
tcpmaster_is_starting = false;
if (TCP_Clients_exists()) {
int mapsize = tcp_clients_map.getSize();
for(int ii=0;ii<mapsize;ii++) {
Object cl = tcp_clients_map.GetValueAt(ii);
// revisi 15/1/2021
if (cl instanceof TCPSocketForMasterSender) {
TCPSocketForMasterSender tcp = (TCPSocketForMasterSender) cl;
if (tcp.getIsValid()) {
tcp.Close();
raise_socketisclosed_event(tcp.getIP(),tcp.getPort());
}
}
// revisi pakai TCPSocketForMasterSender
// if (cl instanceof Socket) {
// Socket client = (Socket) cl;
// try {
// client.close();
// } catch (IOException e) {
// raise_log_exception("Stop_TCP_Master","Close Socket",e);
// }
// }
}
tcp_clients_map.Clear();
}
if (tcpmaster instanceof ServerSocket) {
try {
tcpmaster.close();
} catch (IOException e) {
raise_log_exception("Stop_TCP_Master","Close",e);
}
tcpmaster = null;
}
}
public boolean getTCPMasterRunning() {
return tcpmaster_is_starting;
}
/**
* Get Local IP Address used by default network interface used by system
* @return IP address , or empty string if failed
*/
public String GetDefaultNetwork_IPAddress() {
try {
DatagramSocket dgs = new DatagramSocket();
dgs.connect(InetAddress.getByName("8.8.8.8"),53);
String result = dgs.getLocalAddress().getHostAddress();
dgs.close();
return result;
} catch (SocketException | UnknownHostException e) {
raise_log_exception("GetDefaultNetwork_IPAddress","",e);
return "";
}
}
/**
* Add new TCP Clients for streaming
* @param iptarget : ip address
* @param targetport : port number
* @return true if success
*/
public boolean Add_TCP_Target(String iptarget, int targetport) {
if (iptarget.isEmpty()) {
raise_log_event("Add_TCP_Target failed, iptarget is empty");
return false;
}
if (targetport<1) {
raise_log_event("Add_TCP_Target failed , targetport less than 1");
return false;
}
if (targetport>65535) {
raise_log_event("Add_TCP_Target failed, targetport more than 65535");
return false;
}
if (!(tcp_clients_map instanceof Map)) {
raise_log_event("Add_TCP_Target failed, tcp_clients_map is null");
return false;
}
if (!(tcp_clients_map.IsInitialized())) {
raise_log_event("Add_TCP_Target failed, tcp_clients_map is not initialized");
return false;
}
try {
Socket trysock = new Socket(iptarget, targetport);
if (trysock.isConnected()) {
TCPSocketForMasterSender tcpx = new TCPSocketForMasterSender(iptarget, targetport, trysock, this);
Object old = tcp_clients_map.Put(iptarget, tcpx);
if (old instanceof TCPSocketForMasterSender) {
TCPSocketForMasterSender tcpold = (TCPSocketForMasterSender) old;
tcpold.Close();
raise_log_event("Replacing Old Socket "+tcpold.getKey()+" with New Socket "+tcpx.getKey());
} else {
raise_log_event("Add new TCP Client "+tcpx.getKey());
}
return true;
} else {
raise_log_event("Add_TCP_Target failed, Can't connect with "+iptarget+":"+targetport);
trysock.close();
return false;
}
} catch (UnknownHostException e) {
raise_log_event("Add_TCP_target failed, Msg : "+e.getMessage());
return false;
} catch (IOException e) {
raise_log_event("Add_TCP_target failed, Msg : "+e.getMessage());
return false;
}
}
/**
* Start TCP Master (Listening server)
* @param ethernetip : ethernet IP to bind with ServerSocket. Put empty string for all ip
* @param listeningport : listening port. put 0 = random port number
* @return true if tcp master started
*/
public boolean Start_TCP_Master(String ethernetip, int listeningport) {
tcp_bytesent = 0;
if (listeningport<0) listeningport = 0;
if (tcpmaster instanceof ServerSocket) {
try {
tcpmaster.close();
} catch (IOException e) {
raise_log_exception("Start_TCP_Master","Initial tcpmaster close",e);
}
tcpmaster = null;
}
if (ethernetip.isEmpty()) {
// cari default outbond
ethernetip = GetDefaultNetwork_IPAddress();
if (ethernetip.isEmpty()) {
return false;
}
}
try {
tcpmaster = new ServerSocket();
tcp_master_sa = new InetSocketAddress(ethernetip,listeningport);
tcpmaster.bind(tcp_master_sa);
} catch(IOException e) {
raise_log_exception("Start_TCP_Master","New ServerSocket",e);
return false;
}
tcpmasterthread tmt = new tcpmasterthread(this);
tmt.start();
return true;
}
// tcp client harus socket.conect dulu, baru dimasukkan ke tcp_clients_map
private class tcpmasterthread extends Thread{
private final CustomSocketEvent event;
public tcpmasterthread(CustomSocketEvent mainevent) {
event = mainevent;
}
public void run() {
raise_tcpmaster_event(true);
tcpmaster_is_starting = true;
while(true) {
if (!tcpmaster_is_starting) break;
if (!(tcpmaster instanceof ServerSocket)) break;
if (tcpmaster.isClosed()) break;
Socket xx;
try {
xx = tcpmaster.accept();
} catch (IOException e) {
if (e.getMessage().equalsIgnoreCase("socket closed")) {
// socket closed, exit thread
break;
}
raise_log_exception("TCP Master Thread","Accept",e);
continue;
}
if (xx instanceof Socket) {
InetAddress cc = xx.getInetAddress();
int port = xx.getPort();
if (cc!=null) {
String senderip = cc.getHostAddress();
// revisi pakai TCPSocketForMasterSender (15/1/2021)
TCPSocketForMasterSender newsock = new TCPSocketForMasterSender(senderip, port, xx, event);
Object old = tcp_clients_map.Put(senderip, newsock);
if (old instanceof TCPSocketForMasterSender) {
TCPSocketForMasterSender oldsock = (TCPSocketForMasterSender) old;
oldsock.Close();
raise_log_event("TCP Client IP="+senderip+":"+port+" replacing IP="+oldsock.getKey());
} else {
raise_log_event("Add New TCP Client IP="+senderip+":"+port);
}
// revisi (15/1/2021)
// if (old instanceof Socket) {
// Socket oldsock = (Socket) old;
// raise_log_event("TCP Client IP="+senderip+":"+port+" replacing IP="+oldsock.getInetAddress().getHostAddress()+":"+oldsock.getPort());
// try {
// oldsock.close();
// } catch (IOException e) {
// raise_log_exception("TCP Master Thread","Close old Socket",e);
// }
// oldsock = null;
// } else {
// raise_log_event("Add New TCP Client IP="+senderip+":"+port);
// }
}
}
}
raise_tcpmaster_event(false);
tcpmaster_is_starting = false;
}
}
/**
* Send Data to target list via UDP
* @param bb : data to send, size must be same or larger than length
* @param length : length of bytes to send
* @return 0 if failed, or length if data sent
*/
public int SendData_to_UDP_Clients(byte[] bb, int length) {
if (!(udpmaster instanceof DatagramSocket)) {
raise_log_event("SendData_to_UDP_Clients failed, udpmaster is null");
return 0;
}
if (length<1) {
raise_log_event("SendData_to_UDP_Clients failed, length invalid");
return 0;
}
if (bb==null) {
raise_log_event("SendData_to_UDP_Clients failed, data isn null");
return 0;
}
if (bb.length<length) {
raise_log_event("SendData_to_UDP_Clients failed, data length not same as length info");
return 0; // data yang mau dikirim , lebih kecil dari jumlah yang mau dikirim, fault !
}
if (!UDP_Clients_exists()) {
raise_log_event("SendData_to_UDP_Clients failed, UDP_Clients is not exists" );
return 0;
}
boolean sendingsomething = false; // indikator pernah ngirim sesuatu lewat UDP
int mapsize = udp_clients_map.getSize();
for(int ii=0;ii<mapsize;ii++) {
Object cl = udp_clients_map.GetValueAt(ii);
if (cl instanceof DatagramSocketForMasterSender) {
DatagramSocketForMasterSender udp = (DatagramSocketForMasterSender) cl;
if (udp.getIsValid()) {
int sentcounter = udp.SendData_withLengthLimit(udpmaster, bb, length, 1000);
if (sentcounter>0) {
sendingsomething = true;
raise_udpsentlog_event(udp.getIP(), udp.getPort(), sentcounter);
}
}
}
// revisi 15/1/2021, pakai DatagramSocketForMasterSender
// if (cl instanceof InetSocketAddress) {
// InetSocketAddress client = (InetSocketAddress) cl;
// InetAddress ipx = client.getAddress();
// int port = client.getPort();
//
// ByteBuffer bx = ByteBuffer.wrap(bb, 0, length);
// bx.rewind();
//
// boolean sendinghere = false; // indikator ngirim sesuatu di ipx : port
// int xx = length / 1000;
// int yy = length % 1000;
//
// if (xx>0) {
// for(int jj=0;jj<xx;jj++) {
// byte[] mm = new byte[1000];
// bx.get(mm);
// if (sending_bytes_to_udp(mm, ipx, port)) {
// sendingsomething = true;
// sendinghere = true;
// }
// }
// }
//
// if (yy>0) {
// byte[] nn = new byte[yy];
// bx.get(nn);
// if (sending_bytes_to_udp(nn, ipx, port)) {
// sendingsomething = true;
// sendinghere = true;
// }
// }
//
// if (sendinghere) {
// raise_udpsentlog_event(ipx.getHostAddress(), port, length);
// }
//
//
// } else {
// raise_log_event("Object at udp_clients_map index "+ii+" is not InetSocketAddress");
//
// }
}
if (sendingsomething) {
udp_bytesent+=length;
raise_udpsentcount_event(udp_bytesent);
return length;
}else return 0;
}
// private boolean sending_bytes_to_udp(byte[] xx, InetAddress target, int port) {
// if (udpmaster==null) return false;
// DatagramPacket pkg = new DatagramPacket(xx, xx.length, target, port);
// try {
// udpmaster.send(pkg);
// return true;
// } catch(IOException e) {
// return false;
// }
// }
public long Get_UDP_SentBytes() {
return udp_bytesent;
}
/**
* Get UDP Master Listening Port
* @return -1 if invalid , or positive numbers
*/
public int Get_UDP_Master_ListeningPort() {
if (udpmaster instanceof DatagramSocket) {
return udpmaster.getLocalPort();
} else return -1;
}
/**
* Get Ethernet IP used by UDP Master
* @return empty string if invalid
*/
public String Get_UDP_Master_EthernetIP() {
if (udpmaster instanceof DatagramSocket) {
InetAddress loc = udpmaster.getLocalAddress();
if (loc!=null) {
return loc.getHostAddress();
} else {
return "";
}
} else return "";
}
/**
* Stop UDP Master
*/
public void Stop_UDP_Master() {
if (udpmaster instanceof DatagramSocket) {
udpmaster.close();
udpmaster = null;
}
udpmaster_is_starting = false;
}
/**
* Add an UDP target to udp_clients_map
* @param iptarget : Ip address
* @param targetport : port
* @return true if can be add to UDP List
*/
public boolean Add_UDP_Target(String iptarget, int targetport) {
if (iptarget.isEmpty()) {
raise_log_event("Add_UDP_Target failed, targetip is empty");
return false;
}
if (targetport<1) {
raise_log_event("Add_UDP_Target failed, targetport less than 1");
return false;
}
if (targetport>65535) {
raise_log_event("Add_UDP_Target failed, targetport more than 65535");
return false;
}
if (!(udpmaster instanceof DatagramSocket)) {
raise_log_event("Add_UDP_Target failed, udpmaster is null");
return false;
}
if (!(udp_clients_map instanceof Map)) {
raise_log_event("Add_UDP_Target failed, udp_clients_map is null");
return false;
}
if (!udp_clients_map.IsInitialized()) {
raise_log_event("Add_UDP_Target failed, udp_clients_map is not initialized");
return false;
}
InetSocketAddress sa;
try {
sa = new InetSocketAddress(iptarget,targetport);
} catch(IllegalArgumentException e) {
raise_log_event("Add_UDP_Target failed, new InetSocketAddress exception, Msg="+e.getMessage());
return false;
}
if (sa.isUnresolved()) {
raise_log_event("Add_UDP_Target failed, InetSocketAddress is not resolved");
return false;
}
DatagramSocketForMasterSender udp = new DatagramSocketForMasterSender(iptarget, targetport, this);
Object old = udp_clients_map.Put(iptarget, udp);
if (old instanceof DatagramSocketForMasterSender) {
DatagramSocketForMasterSender oldudp = (DatagramSocketForMasterSender) old;
raise_log_event(udp.getKey()+" is replacing "+oldudp.getKey()+" in udp_clients_map");
} else {
raise_log_event(udp.getKey()+" is added to udp_clients_map");
}
// revisi 15/1/2021
//udp_clients_map.Put(iptarget, sa);
//raise_log_event("InetSocketAddress "+iptarget+":"+targetport+" is added to udp_clients_map");
return true;
}
/**
* Open UDP Master (Listening Server)
* UDP Master hanya untuk kirim data ke UDP clients, tidak terima data balik
* UDP Clients harus kirim sesuatu dulu ke sini, supaya dimasukkan dalam udp_clients_map
* @param ethernetip : ethernet ip untuk set listening di ethernet mana. Kalau semua, kasih string kosong
* @param listenport : listening port. kalau 0, udp port random
* @return true if success
*/
public boolean Start_UDP_Master(String ethernetip, int listenport) {
udp_bytesent = 0;
if (udp_clients_map==null) {
udp_clients_map = new Map();
udp_clients_map.Initialize();
}
if (udpmaster!=null) {
udpmaster.disconnect();
udpmaster.close();
udpmaster = null;
}
if (listenport<1) {
listenport = 0;
}
if (ethernetip.isEmpty()) {
ethernetip = GetDefaultNetwork_IPAddress();
}
try {
InetAddress local = InetAddress.getByName(ethernetip);
udpmaster = new DatagramSocket(listenport, local);
} catch(UnknownHostException e) {
raise_log_event("Unable to determine InetAddress from "+ethernetip);
return false;
} catch(SocketException e) {
raise_log_event("Unable to create DatagramSocket at port "+listenport);
return false;
} catch(SecurityException e) {
raise_log_event("Unable to create DatagramSocket at port "+listenport+" because of Security reason");
return false;
}
raise_log_event("Start_UDP_Master success, DatagramSocket created at port "+udpmaster.getLocalPort());
udpmasterthread umt = new udpmasterthread(this);
umt.start();
return true;
}
// UDP Master hanya untuk kirim data streaming, tidak terima data balik
// client harus kirim sesuatu supaya dimasukkan ke udp_clients_map
private class udpmasterthread extends Thread {
private final CustomSocketEvent event;
public udpmasterthread(CustomSocketEvent mainevent) {
event = mainevent;
}
public void run() {
raise_udpmaster_event(true);
udpmaster_is_starting = true;
while(true) {
if (udpmaster==null) break;
if (udpmaster.isClosed()) break;
if (!udpmaster_is_starting) break;
DatagramPacket pkg = new DatagramPacket(new byte[udp_packet_size],udp_packet_size);
try {
udpmaster.receive(pkg);
} catch (IOException e) {
if (e.getMessage().compareToIgnoreCase("socket closed")!=0) {
// kalau socket closed, itu biasa ..
// kalau bukan socket closed, raise_log_exception
raise_log_exception("udpmasterthread","receive",e);
}
pkg = null;
continue;
}
if (pkg instanceof DatagramPacket) {
String senderip = pkg.getAddress().getHostAddress();
int senderport = pkg.getPort();
DatagramSocketForMasterSender newudp = new DatagramSocketForMasterSender(senderip,senderport, event);
if (newudp.getIsValid()) {
Object old = udp_clients_map.Put(senderip, newudp);
if (old instanceof DatagramSocketForMasterSender) {
DatagramSocketForMasterSender oldudp = (DatagramSocketForMasterSender) old;
raise_log_event("New UDP Client IP="+newudp.getKey()+" is replacing Old UDP Client IP="+oldudp.getKey());
} else {
raise_log_event("Add new UDP Client IP="+newudp.getKey());
}
} else raise_log_event("Unable to Add UDP Client IP="+senderip+":"+senderport+", Invalid DatagramSocketForMasterSender");
// revisi 15/1/2021
// String senderip = pkg.getAddress().getHostAddress();
// InetSocketAddress sender_sa = (InetSocketAddress) pkg.getSocketAddress();
// Object oldobject = udp_clients_map.Put(senderip, sender_sa);
// if (oldobject instanceof InetSocketAddress) {
// InetSocketAddress old = (InetSocketAddress) oldobject;
// raise_log_event("UDP Client IP="+sender_sa.getAddress().getHostAddress()+":"+sender_sa.getPort()+" replacing IP="+old.getAddress().getHostAddress()+":"+old.getPort());
// } else {
// raise_log_event("Add new UDP client IP="+sender_sa.getAddress().getHostAddress()+":"+sender_sa.getPort());
// }
}
}
raise_udpmaster_event(false);
udpmaster_is_starting = false;
}
}
public boolean getUDPMasterRunning() {
return udpmaster_is_starting;
}
private void raise_log_exception(String function, String command, Exception e) {
raise_log_event("Exception on Function="+function+", Command="+command+", Msg="+e.getMessage()+", Caused="+e.getCause());
}
private void raise_log_event(String msg) {
if (need_log_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_mastersenderlog", false, new Object[] {msg});
if (javaevent!=null) javaevent.mastersenderlog(msg);
}
private void raise_udpmaster_event(boolean isactive) {
if (need_udpmaster_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_udpmaster", false, new Object[] {isactive});
if (javaevent!=null) javaevent.udpmaster(isactive);
}
private void raise_tcpmaster_event(boolean isactive) {
if (need_tcpmaster_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_tcpmaster", false, new Object[] {isactive});
if (javaevent!=null) javaevent.tcpmaster(isactive);
}
private void raise_udpsentcount_event(long value) {
if (need_udpsentcount_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_udpsentcount", false, new Object[] {value});
if (javaevent!=null) javaevent.udpsentcount(value);
}
private void raise_tcpsentcount_event(long value) {
if (need_tcpsentcount_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_tcpsentcount", false, new Object[] {value});
if (javaevent!=null) javaevent.tcpsentcount(value);
}
private void raise_tcpsentlog_event(String targetip, int targetport, int bytecount) {
if (need_tcpsentlog_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_tcpsentlog", false, new Object[] {targetip, targetport, bytecount});
if (javaevent!=null) javaevent.tcpsentlog(targetip, targetport, bytecount);
}
private void raise_udpsentlog_event(String targetip, int targetport, int bytecount) {
if (need_udpsentlog_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_udpsentlog", false, new Object[] {targetip, targetport, bytecount});
if (javaevent!=null) javaevent.udpsentlog(targetip, targetport, bytecount);
}
private void raise_socketisclosed_event(String socket_address, int socket_port) {
if (need_socketisclosed_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_socketisclosed", false, new Object[] {socket_address, socket_port});
if (javaevent!=null) javaevent.socketisclosed(socket_address, socket_port);
}
/**
* Event dari TCPSocketForMasterSender dan DatagramSocketForMasterSender
*/
@Override
@BA.Hide
public void log(Object sender, String msg) {
if (sender instanceof TCPSocketForMasterSender) {
TCPSocketForMasterSender tcpx = (TCPSocketForMasterSender) sender;
raise_log_event("Log from "+tcpx.getKey()+" = "+msg);
} else if (sender instanceof DatagramSocketForMasterSender) {
DatagramSocketForMasterSender udpx = (DatagramSocketForMasterSender) sender;
raise_log_event("Log from "+udpx.getKey()+" = "+msg);
}
}
@Override
@BA.Hide
public void socketclosed(Object sender) {
if (sender instanceof TCPSocketForMasterSender) {
TCPSocketForMasterSender tcpx = (TCPSocketForMasterSender) sender;
raise_socketisclosed_event(tcpx.getIP(), tcpx.getPort());
if (tcp_clients_map instanceof Map) {
if (tcp_clients_map.IsInitialized()) {
Object old = tcp_clients_map.Remove(tcpx.getIP());
if (old instanceof TCPSocketForMasterSender) {
TCPSocketForMasterSender tcpold = (TCPSocketForMasterSender) old;
raise_log_event(tcpold.getKey()+" is removed from tcp_clients_map");
}
}
}
} else if (sender instanceof DatagramSocketForMasterSender) {
DatagramSocketForMasterSender udpx = (DatagramSocketForMasterSender) sender;
raise_socketisclosed_event(udpx.getIP(), udpx.getPort());
if (udp_clients_map instanceof Map) {
if (udp_clients_map.IsInitialized()) {
Object old = udp_clients_map.Remove(udpx.getIP());
if (old instanceof DatagramSocketForMasterSender) {
DatagramSocketForMasterSender udpold = (DatagramSocketForMasterSender) old;
raise_log_event(udpold.getKey()+" is removed from udp_clients_map");
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
package MastersenderRelated;
public interface MasterSenderEvent {
void mastersenderlog(String log);
void udpmaster(boolean isactive);
void tcpmaster(boolean isactive);
void tcpsentcount(long value);
void udpsentcount(long value);
void tcpsentlog(String targetip, int targetport, int bytecount);
void udpsentlog(String targetip, int targetport, int bytecount);
void socketisclosed(String socket_address, int socket_port);
}

View File

@@ -0,0 +1,168 @@
package MastersenderRelated;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.DateTime;
@BA.ShortName("MasterSender_TCPSocket")
public class TCPSocketForMasterSender {
private final String _ipkey;
private final int _portkey;
private boolean _isvalid=false;
private OutputStream _outputstream = null;
private Socket _sock;
private long create_time=0;
private long _txcount=0;
private CustomSocketEvent javaobject;
@BA.Hide
public TCPSocketForMasterSender(final String ipaddress, final int port, final Socket ss, final CustomSocketEvent event) {
_ipkey = ipaddress;
_portkey = port;
_sock = ss;
javaobject = event;
if (!_ipkey.isEmpty()) {
if (_portkey>0) {
if (_portkey<65535) {
if (_sock instanceof Socket) {
if (_sock.isConnected()) {
try {
_outputstream = _sock.getOutputStream();
_isvalid = true;
create_time = DateTime.getNow();
} catch (IOException e) {
log("Failed to Initialize TCPSocketForMasterSender, Msg : "+e.getMessage());
}
return;
}
}
}
}
}
}
private void log(String msg) {
if (javaobject instanceof CustomSocketEvent) {
javaobject.log(this, msg);
}
}
/***
* Key String is [ipaddress]:[port]
* @return Key string for this class, or return N/A if invalid
*/
public String getKey() {
return _isvalid ? _ipkey+":"+_portkey : "N/A";
}
/**
* Get IP Adddres
* @return empty string if invalid
*/
public String getIP() {
return _isvalid ? _ipkey : "";
}
/**
* Get TCP Port
* @return zero if invalid
*/
public int getPort() {
return _isvalid ? _portkey : 0 ;
}
/**
* Check if TCP Socket is valid
* @return true if valid
*/
public boolean getIsValid() {
return _isvalid;
}
/**
* Check if TCP Socket is connected
* @return true if connected
*/
public boolean getIsConnected() {
if (_sock instanceof Socket) {
if (_sock.isClosed()==false) {
return _sock.isConnected();
}
}
return false;
}
/**
* Close TCP Socket
*/
public void Close() {
if (_sock instanceof Socket) {
try {
_sock.close();
} catch (IOException e) {
log("Error closing "+getKey()+", Msg : "+e.getMessage());
}
}
_sock = null;
if (javaobject instanceof CustomSocketEvent) {
javaobject.socketclosed(this);
}
}
/**
* Get Duration between creation and now
* @return duration in tick
*/
public long getDuration() {
return _isvalid ? DateTime.getNow() - create_time : 0;
}
/**
* Get TX counter
* @return TX counter
*/
public long getTXCounter() {
return _txcount;
}
/**
* Send Data to TCP Socket
* @param bb : bytes to send
* @return 0 if failed, else return bb.length
*/
public int SendData(byte[] bb) {
if (getIsValid()) {
if (getIsConnected()) {
if (_outputstream instanceof OutputStream) {
if (bb != null) {
if (bb.length>0) {
try {
_outputstream.write(bb);
_txcount+= bb.length;
return bb.length;
} catch (IOException e) {
log("Failed to SendData to "+getKey()+", Msg : "+e.getMessage());
Close();
}
}
}
}
}
}
return 0;
}
}

View File

@@ -0,0 +1,401 @@
package QZARelated;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.objects.collections.List;
@BA.ShortName("NetworkDataSender")
public class DataSender {
private DataSenderEvent dse;
private MulticastSocket mulsock = null;
private InetAddress multicastip = null;
private InetSocketAddress multicastsockaddress = null;
private InetAddress multicast_ethernet = null;
private int multicastport = 0;
private boolean multicastready = false;
private DatagramSocket unisock = null;
private List unicasttarget = null;
private int unicastport = 0;
private InetAddress unicast_ethernet = null;
private boolean unicastready = false;
protected long bytesent_multicast = 0;
protected long bytesent_unicast = 0;
protected long bytesent = 0;
private boolean is_unicasting = false;
private boolean is_multicasting = false;
protected void SetDataSenderEvent(DataSenderEvent value) {
dse = value;
}
/**
* Close Unicast Sending
*/
public void CloseUnicast() {
if (unicasttarget!=null) {
if (unicasttarget.getSize()>0) {
StringBuilder str = new StringBuilder();
for(int ii=0;ii<unicasttarget.getSize();ii++) {
InetSocketAddress ipx = (InetSocketAddress) unicasttarget.Get(ii);
if (str.length()>0) str.append(";");
str.append(ipx.getAddress().getHostAddress());
}
if (str.length()>0) {
if (dse != null) {
dse.stopstreaming(str.toString());
}
}
}
unicasttarget.Clear();
unicasttarget = null;
}
if (unisock!=null) {
unisock.disconnect();
unisock.close();
unisock = null;
}
unicastready = false;
}
/**
* Initialize Multicast Sending
* @param MulticastIp : Multicast Destination IP
* @param Port : Multicast Port
* @param EthernetIP : Local Ethernet IP, for use with multiple ethernet cards
* @return true if success
*/
public boolean SetMulticastTarget(String MulticastIp, int Port, String EthernetIP) {
if (this.mulsock!=null) {
this.mulsock.disconnect();
this.mulsock.close();
this.mulsock = null;
}
if (Port<1) {
raise_log_event("Invalid Target Multicast Port");
return false;
}
MulticastIp = MulticastIp.trim();
if (MulticastIp.isEmpty()) {
raise_log_event("Invalid Target Multicast IP");
return false;
}
this.multicastport=Port;
this.multicastready = false;
try {
this.multicastip = InetAddress.getByName(MulticastIp);
this.multicastsockaddress = new InetSocketAddress(this.multicastip, this.multicastport);
} catch (UnknownHostException | SecurityException | IllegalArgumentException e) {
raise_log_event("Invalid MulticastIP="+MulticastIp+", Exception="+e.getMessage()+", Caused="+e.getCause());
return false;
}
try {
this.mulsock = new MulticastSocket();
} catch (IOException e) {
raise_log_event("Unable to create MulticastSocket, Msg : "+e.getMessage()+", Caused : "+e.getCause());
this.multicastip = null;
this.multicastsockaddress = null;
return false;
}
if (!EthernetIP.isEmpty()) {
try {
this.multicast_ethernet = InetAddress.getByName(EthernetIP);
} catch (UnknownHostException e1) {
raise_log_event("Invalid Local EthernetIP="+EthernetIP);
return false;
}
try {
mulsock.setInterface(this.multicast_ethernet);
} catch (SocketException e) {
raise_log_event("Unable to bind MulticastSocket with EthernetIP="+EthernetIP);
return false;
}
}
try {
mulsock.joinGroup(this.multicastip);
} catch (IOException | SecurityException e) {
raise_log_event("MulticastSocket unable to JoinGroup to "+MulticastIp+", Exception="+e.getMessage()+", Caused="+e.getCause());
return false;
}
multicastready = true;
return true;
}
/**
* Close Multicast Sending
*/
public void CloseMulticast() {
if (mulsock!=null) {
if (multicastip!=null) {
try {
mulsock.leaveGroup(multicastip);
if (dse!=null) {
dse.stopstreaming(multicastip.getHostAddress());
}
} catch (IOException e) {
raise_log_event("MulticastSocket unable to LeaveGroup from "+multicastip.getHostAddress());
}
multicastip = null;
}
this.multicastsockaddress = null;
mulsock.disconnect();
mulsock.close();
mulsock = null;
}
multicastready = false;
}
/**
* Get Ethernet IP to bind with Multicast Socket
* @return IP Address in String
*/
public String Multicast_Ethernet_IP() {
if (multicast_ethernet==null)
return "";
else
return multicast_ethernet.getHostAddress();
}
/**
* Get Ethernet IP to bind with Unicast Socket
* @return IP Address in String
*/
public String Unicast_Ethernet_IP() {
if (unicast_ethernet==null)
return "";
else
return unicast_ethernet.getHostAddress();
}
/**
* Initialize Unicast Sending
* @param TargetIP : List of IP Address in string
* @param Port : unicast port
* @param EthernetIP : Local Ethernet IP, for use with multiple ethernet cards
* @return true if success
*/
public boolean SetUnicastTarget(List TargetIP, int Port, String EthernetIP) {
if (this.unisock!=null) {
this.unisock.disconnect();
this.unisock.close();
this.unisock = null;
}
if (Port<1) {
raise_log_event("Invalid Target Unicast Port");
return false;
}
this.unicastport = Port;
this.unicastready = false;
if (unicasttarget==null) {
unicasttarget = new List();
}
if (!unicasttarget.IsInitialized()) {
unicasttarget.Initialize();
} else unicasttarget.Clear();
if (TargetIP==null) {
raise_log_event("Unicast TargetIP is null");
return false;
}
if (TargetIP.getSize()<1) {
raise_log_event("Unicast TargetIP is empty");
return false;
}
for(int ii=0;ii<TargetIP.getSize();ii++) {
Object xx = TargetIP.Get(ii);
if (xx instanceof String) {
String ip = (String) xx;
try {
InetAddress ipx = InetAddress.getByName(ip);
InetSocketAddress ipsock = new InetSocketAddress(ipx, Port);
unicasttarget.Add(ipsock);
} catch (IOException | SecurityException | IllegalArgumentException e) {
raise_log_event("Unicast Target "+ip+" is invalid, will be excluded from Streaming, Exception="+e.getMessage()+", Caused="+e.getCause());
continue;
}
}
}
int unicount = unicasttarget.getSize();
if (unicount<1) {
raise_log_event("Final Unicast Destination is empty");
return false;
} else {
raise_log_event("Will send Unicast to "+unicount+" targets");
}
if (!EthernetIP.isEmpty()) {
try {
this.unicast_ethernet = InetAddress.getByName(EthernetIP);
} catch (UnknownHostException e) {
this.unicast_ethernet = null;
raise_log_event("Invalid Local Ethernet IP="+EthernetIP+", Message="+e.getMessage()+", Caused="+e.getCause());
return false;
}
InetSocketAddress localsock = null;
try {
localsock = new InetSocketAddress(this.unicast_ethernet,0);
} catch(IllegalArgumentException e) {
localsock = null;
raise_log_event("Invalid Local Ethernet Socket Address, Message="+e.getMessage()+", Caused="+e.getCause());
return false;
}
try {
unisock = new DatagramSocket(localsock);
} catch (SocketException e) {
raise_log_event("Unable to create DatagramSocket, Msg : "+e.getMessage()+", Caused : "+e.getCause());
return false;
}
} else {
try {
unisock = new DatagramSocket();
} catch (SocketException e) {
raise_log_event("Unable to create DatagramSocket, Msg : "+e.getMessage()+", Caused : "+e.getCause());
return false;
}
}
raise_log_event("Unicast Socket created at "+unisock.getLocalAddress().getHostAddress()+":"+unisock.getLocalPort());
this.unicastready = true;
return true;
}
/**
* Check if MulticastSocket is created
* @return true if opened
*/
public boolean getMulticastIsOpened() {
if (mulsock != null) return true; else return false;
}
/**
* Check if UnicastSocket is created
* @return true if opened
*/
public boolean getUnicastIsOpened() {
if (unisock != null) return true; else return false;
}
protected void SendPCMData(byte[] xx, int length) {
if (xx==null) return;
if (xx.length<0) return;
if (length<0) return;
boolean is_sending = false;
if (multicastready) {
if (multicastsockaddress!=null) {
try {
DatagramPacket pkg = new DatagramPacket(xx, length, multicastsockaddress);
if (mulsock!=null) mulsock.send(pkg);
bytesent_multicast += xx.length;
is_sending = true;
if (!is_multicasting) {
is_multicasting = true;
if (dse!=null) {
dse.startstreaming(multicastsockaddress.getAddress().getHostAddress());
}
}
} catch (IOException | SecurityException | IllegalArgumentException e) {
raise_log_event("Unable to send Multicast Packet to "+multicastip.getHostAddress()+":"+multicastport+", Exception="+e.getMessage()+", Caused="+e.getCause());
}
}
}
if (unicastready) {
if (unicasttarget.getSize()>0) {
StringBuilder str = new StringBuilder();
for (int ii=0;ii<unicasttarget.getSize();ii++) {
InetSocketAddress ipx = (InetSocketAddress) unicasttarget.Get(ii);
try {
DatagramPacket pkg = new DatagramPacket(xx,length, ipx);
if (unisock!=null) unisock.send(pkg);
if (str.length()>0) str.append(";");
str.append(ipx.getAddress().getHostAddress());
} catch (IOException | SecurityException | IllegalArgumentException e) {
raise_log_event("Unable to send Unicast Packet to "+ipx.getAddress().getHostAddress()+":"+unicastport+", Exception="+e.getMessage()+", Caused="+e.getCause());
}
}
bytesent_unicast+=xx.length;
is_sending = true;
if (!is_unicasting) {
is_unicasting = true;
if (str.length()>0) {
if (dse != null) {
dse.startstreaming(str.toString());
}
}
}
}
}
if (is_sending) bytesent+= xx.length;
}
/**
* Get Total Sent bytes to Multicast Target
* To get in String format, use filesize_to_string function
* @return total bytes sent in long
*/
public long getPCMByteSent_Multicast() {
return bytesent_multicast;
}
/**
* Get Total Sent bytes to Unicast target
* To get in String format, use filesize_to_string function
* @return total bytes sent in long
*/
public long getPCMByteSent_Unicast() {
return bytesent_unicast;
}
/**
* Get Sent bytes, either in Multicast or In Unicast
* @return total bytes sent in long
*/
public long getPCMByteSent() {
return bytesent;
}
private void raise_log_event(String msg) {
if (dse!=null) dse.log(msg);
}
}

View File

@@ -0,0 +1,7 @@
package QZARelated;
public interface DataSenderEvent {
void log(String msg);
void startstreaming(String toip);
void stopstreaming(String toip);
}

View File

@@ -0,0 +1,754 @@
package QZARelated;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASS.IDSPPROC;
import com.un4seen.bass.BASS.bassconstant;
import com.un4seen.bass.BASSmix;
import anywheresoftware.b4a.BA;
@BA.ShortName("QZA_UDP_FileSender")
@BA.Events(values= {
"playbackstarted(filename as string,success as boolean)",
"playbackstopped(filename as string)",
"playbackpcm(filename as string, bb() as byte)",
"log(msg as string)",
"vulevel(value as int)",
"sendprogress(sent as long, totalsize as long)",
"startstreaming(toip as string)",
"stopstreaming(toip as string)"
})
public class QZA_UDP_FileSender extends DataSender implements DataSenderEvent{
private BASS bass;
private BASSmix bassmix;
private BA bax;
private String event = "";
private String namafile = "";
private boolean need_playbackstarted_event = false;
private boolean need_playbackstopped_event = false;
private boolean need_playbackpcm_event = false;
private boolean need_log_event = false;
private boolean need_vulevel_event = false;
private boolean need_sendprogress_event = false;
private boolean need_startstreaming_event = false;
private boolean need_stopstreaming_event = false;
@SuppressWarnings("unused")
private long dspnanotime = 0;
private int current_play_handle = 0;
private long audiofilesize = 0;
private double audioduration = 0;
private long resamplingsize = 0;
private int samplingrate = 0;
private int mixerhandle = 0;
private int dsphandle = 0;
private final long KBthreshold = 1024;
private final long MBthreshold = 1024 * 1024;
private final long GBthreshold = 1024 * 1024 * 1024;
private final int MINUTEthreshold = 60;
private final int HOURthreshold = 60 * 60;
private boolean issending = false;
private boolean inited = false;
private int maxpackagesize = 1000;
private Object myobject;
private float volfactor = 1.0f;
private int loopingcount = 0;
private long byteread = 0;
private int devid = -1;
private IDSPPROC theproc;
/**
* Initialize QZA UDP File Sender
* @param eventname : event prefix
* @param device id : 0 = no sound, 1 = first real hardware
*/
public void Initialize(BA ba, String eventname, int deviceid) {
inited = false;
bax = ba;
event = eventname;
devid = deviceid;
myobject = this;
if (bax!=null) {
if (!event.isEmpty()) {
need_playbackstarted_event = bax.subExists(event+"_playbackstarted");
need_playbackstopped_event = bax.subExists(event+"_playbackstopped");
need_playbackpcm_event = bax.subExists(event+"_playbackpcm");
need_log_event = bax.subExists(event+"_log");
need_vulevel_event = bax.subExists(event+"_vulevel");
need_sendprogress_event = bax.subExists(event+"_sendprogress");
need_startstreaming_event = bax.subExists(event+"_startstreaming");
need_stopstreaming_event = bax.subExists(event+"_stopstreaming");
}
}
SetDataSenderEvent(this); // buat set event DataSender
bass = new BASS();
bassmix = new BASSmix();
int bassversion = bass.BASS_GetVersion();
int bassmixversion = bassmix.BASS_Mixer_GetVersion();
if (bassversion!=0) {
if (bassmixversion!=0) {
inited = true;
}
}
// auto close kalau java program closed
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if (inited) {
StopPlayback();
if (devid != -1) {
if (BASS.BASS_SetDevice(devid)) {
BASS.BASS_Stop();
}
}
BASS.BASS_Free();
}
}
});
}
/**
* Check if QZA UDP FileSender is initialzed
* @return true if initialized
*/
public boolean getIsInitialized() {
return inited;
}
/**
* Stop current sending
*/
public void StopPlayback() {
issending = false;
close_all();
}
/**
* Get / Set Streaming Volume
* 0 = silent, 1.0f = normal, more than 1.0f = amplification
*/
public float getStreamVolume() {
return volfactor;
}
public void setStreamVolume(float value) {
if (value<0) value = 0;
volfactor = value;
}
/**
* Get Total bytes read from file (read progress)
* To get in String format, use filesize_to_string function
* @return total byte read, in long
*/
public long getPCMByteRead() {
return byteread;
}
/**
* Get Original Filesize in bytes
* @return filesize in bytes
*/
public long getAudio_FileSize() {
return audiofilesize;
}
public String getAudio_FileSize_String() {
return filesize_to_string(audiofilesize);
}
/**
* Get Audio duration in seconds
* @return duration in seconds
*/
public double getAudio_Duration() {
return audioduration;
}
public String getAudio_Duration_String() {
return duration_to_string(audioduration);
}
/**
* Get Resampled Filesize in bytes
* @return resampled size in bytes
*/
public long getResampling_FileSize() {
return resamplingsize;
}
public String getResampling_FileSize_String() {
return filesize_to_string(resamplingsize);
}
/**
* Check if there is opened audio file
* @return true if opened
*/
public boolean getFileIsOpened() {
if (current_play_handle==0) return false; else return true;
}
/**
* Ambil Mixer_handle, kalau mau direkam , atau di-encode
* @return 0 = mixer mati, lainnya = mixer aktif
*/
public int getMixer_Handle() {
return mixerhandle;
}
/**
* Open Audio file
* @param filenya : valid file path for audio file
* @param newsamplingrate : new sampling rate if required
* @param ismono : true for mono
* @return true if success
*/
public boolean OpenFile(String filenya, int newsamplingrate, boolean ismono) {
this.samplingrate = newsamplingrate;
if (!bass.BASS_SetDevice(devid)) { // sudah bisa SetDevice, berarti sudah Init
// kalau tidak bis SetDevice, cek error
int errcode = bass.BASS_ErrorGetCode();
if (errcode==bassconstant.BASS_ERROR_DEVICE) {
// device tidak ada
raise_bass_log_event("OpenFile","SetDevice "+devid);
return false;
} else if (errcode==bassconstant.BASS_ERROR_INIT) {
// belum di-init, maka sekarang init
int initflag = bassconstant.BASS_DEVICE_FREQ | bassconstant.BASS_DEVICE_16BITS ;
if (ismono) initflag |= bassconstant.BASS_DEVICE_MONO;
if (!bass.BASS_Init(devid, newsamplingrate, initflag)) {
// tidak bisa init, maka gagal
raise_bass_log_event("OpenFile","Init "+devid);
return false;
}
}
}
// sampai sini, sudah set device dan sudah init
// create mixer, untuk ubah samplingrate file original
int mixflag = 0;
mixerhandle= bassmix.BASS_Mixer_StreamCreate(samplingrate, 1, mixflag);
if (mixerhandle==0) {
raise_bass_log_event("OpenFile","Mixer_StreamCreate");
return false;
}
// buka file original
// sumber data untuk Mixer, harus selalu decode
int openflag = bassconstant.BASS_STREAM_DECODE;
current_play_handle = bass.BASS_StreamCreateFile(false,filenya, 0, 0, openflag);
if (current_play_handle==0) {
raise_bass_log_event("OpenFile","StreamCreateFile");
return false;
}
namafile = filenya;
audiofilesize = 0;
audioduration = 0;
resamplingsize = 0;
// ambil properties nya
audiofilesize = bass.BASS_ChannelGetLength(current_play_handle, bassconstant.BASS_POS_BYTE);
if (audiofilesize == -1) {
raise_bass_log_event("OpenFile","ChannelGetLength play_handle");
return false;
}
audioduration = bass.BASS_ChannelBytes2Seconds(current_play_handle, audiofilesize);
if (audioduration<0) {
raise_bass_log_event("OpenFile","ChannelBytes2Seconds play_handle");
return false;
}
resamplingsize = bass.BASS_ChannelSeconds2Bytes(mixerhandle, audioduration);
if (resamplingsize == -1) {
raise_bass_log_event("OpenFile","ChannelSeconds2Bytes mixerhandle");
return false;
}
return true;
}
public String filesize_to_string(long value) {
if (value < KBthreshold) {
return value+" B";
} else if (value < MBthreshold ) {
return String.format("%.2f", (double)value/KBthreshold)+" KB";
} else if (value < GBthreshold) {
return String.format("%.2f", (double)value/MBthreshold)+" MB";
} else {
return String.format("%.2f", (double)value/GBthreshold)+" GB";
}
}
private String duration_to_string(double value) {
if (value < MINUTEthreshold) {
return (int)value+" seconds";
} else if (value < HOURthreshold ) {
int mm = (int)value / MINUTEthreshold;
int ss = (int)value - (mm * MINUTEthreshold);
return mm+" minutes "+ss+" seconds";
} else {
int hh = (int)value / HOURthreshold;
value = value - (hh * HOURthreshold);
int mm = (int)value / MINUTEthreshold;
int ss = (int)value - (mm*MINUTEthreshold);
return hh+" hours "+mm+" minutes "+ss+" seconds ";
}
}
/**
* Check if in streaming mode
* @return true if streaming
*/
public boolean getIsStreaming() {
return issending;
}
/**
* Start Playing (streaming) audio file
* will raise event playbackstarted, playbackstopped
* also will raise event playbackpcm , and vulevel, if required
* @param loopcount : how many file is going to be played
* @param packagesize : UDP packagesize, default is 1000 bytes. value above 1500 wil be capped to 1500.
* @return true if playfile success
*/
public boolean PlayFile(int loopcount, int packagesize) {
issending = false;
if (packagesize<1) packagesize = 1000;
if (packagesize>1500) packagesize = 1500;
this.maxpackagesize = packagesize;
// audiofile belum diopen
if (current_play_handle==0) {
raise_log_event("Audio File not opened");
raise_playbackstarted_event(false);
return false;
}
//mixer belum diopen
if (mixerhandle==0) {
raise_log_event("Mixer not Opened");
raise_playbackstarted_event(false);
return false;
}
// "Plug In" file original ke Mixer
int addflag = 0;
if (!bassmix.BASS_Mixer_StreamAddChannel(mixerhandle, current_play_handle, addflag)) {
raise_bass_log_event("PlayFile","Mixer_StreamAddChannel");
raise_playbackstarted_event(false);
return false;
}
// Set DSPPROC untuk ambil PCM Bytes, lewat decodepcmproc
theproc = create_dspproc();
dsphandle = bass.BASS_ChannelSetDSP(mixerhandle,theproc , null, 1);
if (dsphandle==0) {
raise_bass_log_event("PlayFile","ChannelSetDSP");
raise_playbackstarted_event(false);
return false;
}
// Set No_Buffer dan Granule, supaya konstan 1000 bytes per packet
if (!bass.BASS_ChannelSetAttribute(mixerhandle, bassconstant.BASS_ATTRIB_NOBUFFER, 1)) {
raise_bass_log_event("PlayFile","ChannelSetAttribute ATTRIB_NOBUFFER failed");
raise_playbackstarted_event(false);
return false;
}
int granule = packagesize / 2; // granule = packagesize / 2, karena 16 bits = 2x 8bit
if (!bass.BASS_ChannelSetAttribute(mixerhandle,bassconstant.BASS_ATTRIB_GRANULE, granule)) {
raise_bass_log_event("PlayFile","ChannelSetAttribute ATTRIB_GRANULE "+granule+" failed");
raise_playbackstarted_event(false);
return false;
}
// Set Device untuk persiapan BASS_Start()
if (!bass.BASS_SetDevice(devid)) {
// tidak bisa set to device 0
raise_bass_log_event("PlayFile","SetDevice "+devid);
raise_playbackstarted_event(false);
return false;
}
// start output
if (!bass.BASS_Start()) {
raise_bass_log_event("PlayFile","Start");
raise_playbackstarted_event(false);
return false;
}
// play mixer stream
if (!bass.BASS_ChannelPlay(mixerhandle, true)) {
raise_bass_log_event("PlayFile","ChannelPlay");
raise_playbackstarted_event(false);
return false;
}
loopingcount = loopcount;
Thread tx = new Thread(new Play_Monitor());
tx.start();
return true;
}
private class Play_Monitor implements Runnable{
@Override
public void run() {
bytesent_multicast = 0;
bytesent_unicast = 0;
bytesent = 0;
byteread = 0;
dspnanotime = System.nanoTime();
issending = true;
raise_playbackstarted_event(true);
int playstatus = -1;
int lastplaystatus = -1;
while(true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
raise_log_event("PlayFile:Thread:InterruptedException");
break;
}
if (current_play_handle==0) {
raise_log_event("PlayFile Thread for "+namafile+" finished, current_play_handle=0");
break;
}
if (mixerhandle==0) {
raise_log_event("PlayFile Thread for "+namafile+" finished, mixer handle = 0");
break;
}
if (!issending) {
raise_log_event("PlayFile Thread for "+namafile+" finished, user stopped");
break;
}
// revisi 06-11-2019
// kadang playback 2 detik , stop sendiri, gak tau kenapa
// ini untuk cek playback status
playstatus = bass.BASS_ChannelIsActive(mixerhandle);
if (playstatus!=lastplaystatus) {
lastplaystatus = playstatus;
switch(playstatus) {
case bassconstant.BASS_ACTIVE_PLAYING :
raise_log_event("ChannelIsActive mixerhandle "+mixerhandle+" = ACTIVE_PLAYING");
continue; // normal
case bassconstant.BASS_ACTIVE_PAUSED :
raise_log_event("ChannelIsActive mixerhandle "+mixerhandle+" = ACTIVE_PAUSED");
break;
case bassconstant.BASS_ACTIVE_STALLED :
raise_log_event("ChannelIsActive mixerhandle "+mixerhandle+" = ACTIVE_STALLED");
break;
case bassconstant.BASS_ACTIVE_STOPPED :
raise_log_event("ChannelIsActive mixerhandle "+mixerhandle+" = ACTIVE_STOPPED");
break;
}
}
if (playstatus!=bassconstant.BASS_ACTIVE_PLAYING) {
if (loopingcount>0) {
loopingcount-=1;
if (loopingcount==0) {
raise_log_event("Looping finished");
break; // looping = 0, selesai
} else {
raise_log_event("Looping for "+loopingcount+" more");
}
}
/// ulang dari awal
bass.BASS_ChannelStop(mixerhandle);
if (issending) bass.BASS_ChannelPlay(mixerhandle, true); // kalau loopingcount masih positif, dan belum distop user, mainkan lagi
}
}
raise_playbackstopped_event();
close_all();
}
}
private void close_all() {
issending =false;
if (mixerhandle!=0) {
// kadang bikin error !
//http://www.un4seen.com/forum/?topic=18676.msg130824#msg130824
//The crash happened when deleting a reference to a Java DSPPROC callback function because the reference was no longer valid.
//It looks like that could possibly happen if BASS_ChannelRemoveDSP was called just before BASS_StreamFree was
if (!bass.BASS_ChannelStop(mixerhandle)) {
raise_bass_log_event("close_all","ChannelStop:mixerhandle");
}
if (dsphandle!=0) {
if (!bass.BASS_ChannelRemoveDSP(mixerhandle, dsphandle)) {
raise_bass_log_event("close_all","ChannelRemoveDSP");
}
dsphandle = 0;
}
if (current_play_handle!=0) {
if (!bassmix.BASS_Mixer_ChannelRemove(current_play_handle)) {
raise_bass_log_event("close_all","Mixer_ChannelRemove:current_play_handle");
}
}
if (!bass.BASS_StreamFree(mixerhandle)) {
raise_bass_log_event("close_all","StreamFree:mixerhandle");
}
mixerhandle = 0;
}
if (current_play_handle!=0) {
if (!bass.BASS_StreamFree(current_play_handle)) {
raise_bass_log_event("close_all","StreamFree:current_play_handle");
}
current_play_handle=0;
}
// revisi 29/09/2020 , BASS_Free dan BASS_Stop jangan di bagian close_all
// if (bass.BASS_SetDevice(devid)) {
// masih bisa set device
// if (!bass.BASS_Stop()) {
// raise_bass_log_event("close_all","Stop");
// }
// jangan di-free-in , siapa tau deviceid ini terpakai di tempat lain, malah jadi error
// if (!bass.BASS_Free()) {
// raise_bass_log_event("close_all","Free");
// }
// } else raise_bass_log_event("close_all","SetDevice");
}
private IDSPPROC create_dspproc() {
IDSPPROC newproc = new IDSPPROC() {
// Di sini dapat PCM bytes untuk dikirim
@Override
public void DSPPROC(int handle, int channel, Pointer bx, int length, Pointer user) {
if (handle==0) return;
if (channel==0) return;
if (bx==null) return;
if (length<2) return;
if (!issending) return;
// buat diagnostic, masuk ke DSPPROC setiap berapa ms
dspnanotime = System.nanoTime();
// convert to ByteBuffer
Calculate_VU_and_Volume(bx.getByteBuffer(0, length)); // di sini Vu dihitung, juga volume di adjust
// get bytes
byte[] bufbyte = bx.getByteArray(0, length);
if (length<= maxpackagesize)
// kalau ukuran dah pas, langsung aja kirim
SendPCMData(bufbyte,bufbyte.length);
else
// kalau kelebihan, pecah-pecah dulu, baru kirim
Split_and_Send(bx.getByteBuffer(0, length));
// raise raw PCM event
raise_playbackpcm_event(bufbyte);
byteread+= length;
raise_sendprogress_event();
}
};
return newproc;
}
private void Calculate_VU_and_Volume(ByteBuffer bufx) {
if (bufx==null) return;
ShortBuffer vubuf = bufx.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
// calculate volume and vu
short temp = 0;
short maxvalue = 0;
int pos = 0;
vubuf.rewind();
while(vubuf.hasRemaining()) {
pos = vubuf.position(); // ambil posisi sekarang
temp = (short)(vubuf.get() * volfactor); // ambil nilai, dikali volfactor
vubuf.position(pos); // balik posisi tadi
vubuf.put(temp); // taruh balik
if (temp > maxvalue) maxvalue = temp; // cari nilai maksimum untuk VU
}
int vuvalue = (int)((maxvalue/32767.0)*100);
raise_vulevel_event(vuvalue);
}
// Butuh ini kalau Buffer lebih besar dari MaxPackagesize
private void Split_and_Send(ByteBuffer bufx) {
if (bufx==null) return;
int length = bufx.capacity();
int X = length / maxpackagesize; // package per maxpackagesize
int Y = length % maxpackagesize; // remainder of
bufx.rewind();
while (X>0) {
// sending per maxpackagesize limit
byte[] xxx = new byte[maxpackagesize];
bufx.get(xxx);
SendPCMData(xxx,xxx.length); // Pakai DataSender
X-=1;
}
if (Y>0) {
// if has remainder, or length is less than maxpackagesize
byte[] xxx = new byte[Y];
bufx.get(xxx);
SendPCMData(xxx,xxx.length); // Pakai DataSender
}
}
@Override
public void log(String msg) { // event dari DataSender
raise_log_event("DataSender Log : "+ msg);
}
private void raise_playbackstarted_event(boolean success) {
if (need_playbackstarted_event) {
bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_playbackstarted", false, new Object[] {namafile, success});
}
}
private void raise_playbackstopped_event() {
if (need_playbackstopped_event) {
bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_playbackstopped", false, new Object[] {namafile});
}
}
private void raise_playbackpcm_event(byte[] bb) {
if (need_playbackpcm_event) {
bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_playbackpcm", false, new Object[] {namafile,bb});
}
}
private void raise_log_event(String msg) {
if (need_log_event) {
bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
}
}
private void raise_sendprogress_event() {
if (need_sendprogress_event) {
bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_sendprogress", false, new Object[] {byteread, resamplingsize});
}
}
private void raise_bass_log_event(String function, String basscommand) {
int errcode = bass.BASS_ErrorGetCode();
if (errcode==0) return;
String msg = "Error on Function="+function+" from Bass Command="+basscommand+", Code="+BASS.GetBassErrorString(errcode);
raise_log_event(msg);
}
private void raise_vulevel_event(int value) {
if (need_vulevel_event) {
bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_vulevel", false, new Object[] {value});
}
}
private void raise_startstreaming_event(String value) {
if (need_startstreaming_event) bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_startstreaming", false, new Object[] {value});
}
private void raise_stopstreaming_event(String value) {
if (need_stopstreaming_event) bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_stopstreaming", false, new Object[] {value});
}
@Override
public void startstreaming(String toip) {
raise_startstreaming_event(toip);
}
@Override
public void stopstreaming(String toip) {
raise_stopstreaming_event(toip);
}
}

View File

@@ -0,0 +1,461 @@
package QZARelated;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.DateTime;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASS.BASS_DEVICEINFO;
import com.un4seen.bass.BASS.RECORDPROC;
import com.un4seen.bass.BASS.bassconstant;
@BA.ShortName("QZA_UDP_MicrophoneSender")
@BA.Events(values= {
"recordstart(timetick as long)",
"recordstop(timetick as long)",
"recordpcm(bb() as byte)",
"recordvu(value as int)",
"startstreaming(toip as string)",
"stopstreaming(toip as string)",
"log(msg as string)"
})
public class QZA_UDP_MicrophoneSender extends DataSender implements DataSenderEvent {
private BASS bass;
private String event = "";
private BA ba;
private boolean inited = false;
private boolean recording = false;
private boolean need_recordstart_event = false;
private boolean need_recordstop_event = false;
private boolean need_recordpcm_event = false;
private boolean need_recordvu_event = false;
private boolean need_log_event = false;
private boolean need_startstreaming_event = false;
private boolean need_stopstreaming_event = false;
private int recorddev = 0;
private int recordhandle = 0;
private long byteread = 0;
private RECORDPROC recproc = null;
private Object myobject;
private float volfactor = 1.0f;
public void Initialize(BA bax, String eventname) {
this.ba = bax;
this.event = eventname;
myobject = this;
if (ba!=null) {
if (!event.isEmpty()) {
need_log_event = bax.subExists(event+"_log");
need_recordpcm_event = bax.subExists(event+"_recordpcm");
need_recordstart_event = bax.subExists(event+"_recordstart");
need_recordstop_event = bax.subExists(event+"_recordstop");
need_recordvu_event = bax.subExists(event+"_recordvu");
need_startstreaming_event = bax.subExists(event+"_startstreaming");
need_stopstreaming_event = bax.subExists(event+"_stopstreaming");
}
}
bass = new BASS();
if (bass.BASS_GetVersion()!=0) {
this.inited = true;
}
// auto close kalau java program closed
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if (inited) {
StopRecording();
BASS.BASS_Free();
}
}
});
}
/**
* Check if QZA_UDP_MicrophoneSender is initialized or not
* @return true if initialized
*/
public boolean IsInitialized() {
return inited;
}
/**
* Check if Recording Device with device-ID is exist
* @param devid : 0 = first recorder device, 1 = second device , ..
* @return true if device exist
*/
public boolean Check_Recording_Device_exists(int devid) {
if (inited) {
if (devid>=0) {
BASS_DEVICEINFO dev = new BASS_DEVICEINFO();
if (bass.BASS_RecordGetDeviceInfo(devid, dev)) {
dev.read();
if (dev.name!=null) {
if (!dev.name.isEmpty()) {
return true;
}
}
}
}
}
return false;
}
/**
* Start Recording in mono
* @param devid : recording device, start from 0 = first, 1 = second device
* @param samplingrate : sampling rate
* @param bytescount : how many pcm bytes captured for raising event recordpcm
* @return true if recording started
*/
public boolean StartRecording(int devid, int samplingrate, int bytescount) {
recorddev = -1;
if (devid < 0) return false;
if (inited) {
if (!bass.BASS_RecordSetDevice(devid)) {
// tidak bisa recordsetdevice
switch(bass.BASS_ErrorGetCode()) {
case bassconstant.BASS_ERROR_DEVICE :
// tidak ada device ini
raise_bass_log_event("Start_Recording","RecordSetDevice "+devid);
return false;
case bassconstant.BASS_ERROR_INIT :
// belum init, maka di-init aja
if (!bass.BASS_RecordInit(devid)) {
// tidak bisa init
raise_bass_log_event("Start_Recording","RecordInit "+devid);
return false;
}
break;
default :
// unknown error
return false;
}
}
// sampe sini sudah bisa recordsetdevice atau sudah record init
// RECPROC tested OK !! set period = 5 ms, ChannelSetAttribute ATTRIB_GRANULE 500 --> hasil 1000 bytes per RECPROC
// recproc = create_recordproc();
int flag = 0;
// if (recproc!=null) {
// // ada recproc, maka nilai flag nya diganti
// flag = BASS.Utils.MAKELONG(0, 5); // RecordStart flag = 0, period = 5ms
// }
int rechd = bass.BASS_RecordStart(samplingrate, 1, flag, recproc, null);
if (rechd!=0) {
// bisa start recording
// maka mulai record thread
if (bytescount<1) bytescount = 1000; // basic value
Recording_Thread rt = new Recording_Thread(devid, rechd, bytescount);
rt.start();
return true;
}
}
return false;
}
// private RECORDPROC create_recordproc() {
// RECORDPROC result = new RECORDPROC() {
//
// @Override
// public boolean RECORDPROC(int handle, Pointer buffer, int length, Pointer user) {
// if (handle!=recordhandle) return false; // handle beda, stop recording
// if (buffer==null) return false; // ada masalah sama buffer
// if (length<1) return false; // ada masalah sama length
//
//
//
//
// return recording; // kalau recording masih true, lanjut
// }
//
// };
//
// return result;
// }
private class Recording_Thread extends Thread {
private final int maxpackagesize;
Recording_Thread(final int devid, final int rechd, final int bytecount){
recorddev = devid;
recordhandle = rechd;
maxpackagesize = bytecount;
if (!bass.BASS_ChannelSetAttribute(recordhandle, bassconstant.BASS_ATTRIB_VOL, 1.0f)) {
raise_bass_log_event("Recording_Thread","ChannelSetAttribute::ATTRIB_VOL");
}
// RECPROC tested OK
// attrib granule 500 --> 1000 bytes per recproc
// attrib granule 512 --> 1024 bytes per recproc
// if (recproc!=null) {
// // kalau pake recproc
// if (!bass.BASS_ChannelSetAttribute(recordhandle, bassconstant.BASS_ATTRIB_GRANULE, maxpackagesize/2)) {
// raise_bass_log_event("Recording_Thread","ChannelSetAttribute ATTRIB GRANULE");
// }
// }
}
public void run() {
// sampe sini dah recorstart berhasil
recording = true;
bytesent_multicast = 0;
bytesent_unicast = 0;
byteread = 0;
raise_recordstart_event();
int bytes_available = 0;
while(true) {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
break;
}
if (!recording) break;
if (recordhandle==0) break;
// kalau pake recproc , di sini gak ngapa-ngapain.
// sleep aja, tested OK
// ini kalau tidak pakai recproc
// tidak pake recproc
bytes_available = bass.BASS_ChannelGetData(recordhandle, null, bassconstant.BASS_DATA_AVAILABLE);
if (bytes_available == -1) {
// ada masalah ChannelGetData
raise_bass_log_event("RecordingThread","ChannelGetData::DATA_AVAILABLE");
break;
}
if (bytes_available < maxpackagesize) continue; // belum cukup data, loop balik
Pointer BX = new Memory(maxpackagesize); // alokasi bytes sebanyak maxpackagesize
bytes_available = bass.BASS_ChannelGetData(recordhandle, BX, maxpackagesize);
if (bytes_available<1) {
// ada masalah ChannelGetData
raise_bass_log_event("RecordingThread","ChannelGetData::Get PCM Data");
break;
}
if (bytes_available>0) {
byteread+=bytes_available;
ByteBuffer buf = BX.getByteBuffer(0, bytes_available);
Calculate_VU_and_Volume(buf);
byte[] bufbyte = new byte[bytes_available];
buf.get(bufbyte);
raise_recordpcm_event(bufbyte);
SendPCMData(bufbyte, bytes_available);
}
}
// sampe sini, selesai recording
recording = false;
recproc = null; // recordproc di null kan
raise_recordstop_event();
if (recordhandle!=0) {
if (!bass.BASS_ChannelStop(recordhandle)) {
raise_bass_log_event("close_all","ChannelStop");
}
// ChannelStop di recordhandle , otomatis StreamFree
recordhandle = 0;
}
if (bass.BASS_RecordSetDevice(recorddev)) {
if (!bass.BASS_RecordFree()) {
raise_bass_log_event("close_all","RecordFree");
}
} else raise_bass_log_event("close_all","RecordSetDevice");
}
}
/**
* To Stop recording.
* also will close Microphone
*/
public void StopRecording() {
recording = false;
}
/**
* Ambil recordhandle, kalau mau direkam, atau di-encode lagi
* @return 0 = not recording, lainnya = recording
*/
public int getRecord_Handle() {
return recordhandle;
}
/**
* Get / Set Recording Volume, or -1 if not recording
* 0 = silent, 1.0f = normal, more than 1.0f = amplification
*/
public float getRecordingVolume() {
return volfactor;
}
public void setRecordingVolume(float value) {
if (value<0) value = 0;
volfactor = value;
}
private void Calculate_VU_and_Volume(ByteBuffer bufx) {
if (bufx==null) return;
ShortBuffer vubuf = bufx.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
// calculate volume and vu
int temp = 0;
short maxvalue = 0;
int pos = 0;
vubuf.rewind();
while(vubuf.hasRemaining()) {
pos = vubuf.position(); // ambil posisi sekarang
temp = (int)(vubuf.get() * volfactor); // ambil nilai, dikali volfactor
if (temp > Short.MAX_VALUE) temp = (short)Short.MAX_VALUE; // harga mutlak max
if (temp < Short.MIN_VALUE) temp = (short)Short.MIN_VALUE; // harga mutlak min
vubuf.position(pos); // balik posisi tadi
vubuf.put((short)temp); // taruh balik
if (temp > maxvalue) maxvalue = (short) temp; // cari nilai maksimum untuk VU
}
int vuvalue = (int)((maxvalue/32767.0)*100);
raise_recordvu_event(vuvalue);
}
/**
* Check if still in recording process
* @return true if recording
*/
public boolean getIsRecording() {
return recording;
}
/**
* Get Total Sent bytes in Multicast Mode
* @return total pcm byte sent, in long
*/
public long getPCMByteSent_Multicast() {
return bytesent_multicast;
}
/**
* Get Total sent Bytes in Unicast mode
* @return total pcm byte sent, in long
*/
public long getPCMByteSent_Unicast() {
return bytesent_unicast;
}
/**
* Get Total Bytes read from Microphone
* @return total pcm byte read, in long
*/
public long getPCMByteRead() {
return byteread;
}
private void raise_recordstart_event() {
if (need_recordstart_event) {
ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_recordstart", false, new Object[] {DateTime.getNow()});
}
}
private void raise_recordstop_event() {
if (need_recordstop_event) {
ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_recordstop", false, new Object[] {DateTime.getNow()});
}
}
private void raise_recordvu_event(int value) {
if (need_recordvu_event) {
ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_recordvu", false, new Object[] {value});
}
}
private void raise_recordpcm_event(byte[] bb) {
if (need_recordpcm_event) {
ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_recordpcm", false, new Object[] {bb});
}
}
private void raise_log_event(String msg) {
if (need_log_event) {
ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
}
}
private void raise_bass_log_event(String function, String command) {
int errcode = bass.BASS_ErrorGetCode();
if (errcode==0) return;
raise_log_event("BASS Error at Function="+function+" Command="+command+", Code="+BASS.GetBassErrorString(errcode));
}
private void raise_startstreaming_event(String value) {
if (need_startstreaming_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_startstreaming", false, new Object[] {value});
}
private void raise_stopstreaming_event(String value) {
if (need_stopstreaming_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_stopstreaming", false, new Object[] {value});
}
@BA.Hide
@Override
public void log(String msg) { // log dari DataSender
raise_log_event(msg);
}
@BA.Hide
@Override
public void startstreaming(String toip) {
raise_startstreaming_event(toip);
}
@BA.Hide
@Override
public void stopstreaming(String toip) {
raise_stopstreaming_event(toip);
}
}

View File

@@ -0,0 +1,602 @@
package QZARelated;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASS.BASS_CHANNELINFO;
import com.un4seen.bass.BASS.BASS_FILEPROCS;
import com.un4seen.bass.BASS.IDSPPROC;
import com.un4seen.bass.BASS.FILECLOSEPROC;
import com.un4seen.bass.BASS.FILELENPROC;
import com.un4seen.bass.BASS.FILEREADPROC;
import com.un4seen.bass.BASS.FILESEEKPROC;
import com.un4seen.bass.BASS.bassconstant;
import DatareceiverRelated.DataReceiver;
import DatareceiverRelated.DataReceiverEvent;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.DateTime;
@BA.ShortName("QZA_Network_StreamReceiver")
@BA.Events(values= {
"log(msg as string)",
"streamstarted(timetick as long,success as boolean)",
"streamstopped(timetick as long)",
"streamidle(timetick as long)",
"streamrunning(timetick as long)",
"vulevel(value as int)",
"tcpconnected(success as boolean)",
"datareceiverstatus(isconnected as boolean, targetip as string, targetport as int)",
"pcmbytes(bb() as byte)"
})
public class QZA_UDP_StreamReceiver extends DataReceiver implements DataReceiverEvent {
private BASS bass;
private String event = "";
private BA ba;
private boolean inited = false;
private boolean need_log_event = false;
private boolean need_streamstarted_event = false;
private boolean need_streamstopped_event = false;
private boolean need_streamidle_event = false;
private boolean need_streamrunning_event = false;
private boolean need_pcmbytes_event = false;
private boolean need_vulevel_event = false;
private boolean need_tcpconnected_event = false;
private boolean need_datareceiverstatus_event =false;
private boolean isstreaming = false;
private int current_play_handle = 0;
private int devid = -1;
private BASS_CHANNELINFO channelinfo;
private int dsphandle = 0;
private final int bufmemsize = 1024 * 32;
private ByteBuffer bufmem;
private float volfactor = 1.0f;
private final int bufmemwait = 60 * 1000; // 60 second
private long pcmbytescount = 0;
private boolean isidle = false; // untuk indikator streamidle dan streamrunning
private IDSPPROC dspproc;
private Object myobject;
/**
* Initialize Network Stream Receiver
* @param eventname : event name
* @param deviceid : playback device id
*/
public void Initialize(BA bax, String eventname, int deviceid) {
ba = bax;
event = eventname;
myobject = this;
if (ba!=null) {
if (!event.isEmpty()) {
need_log_event = ba.subExists(event+"_log");
need_streamstarted_event = ba.subExists(event+"_streamstarted");
need_streamstopped_event = ba.subExists(event+"_streamstopped");
need_streamidle_event = ba.subExists(event+"_streamidle");
need_streamrunning_event = ba.subExists(event+"_streamrunning");
need_datareceiverstatus_event = ba.subExists(event+"_datareceiverstatus");
need_pcmbytes_event = ba.subExists(event+"_pcmbytes");
need_vulevel_event = ba.subExists(event+"_vulevel");
need_tcpconnected_event = ba.subExists(event+"_tcpconnected");
}
}
devid = deviceid;
SetDataReceiverEvent(this); // perlu supaya DataReceiverEvent jalan
bass = new BASS();
int version = bass.BASS_GetVersion();
if (version!=0) {
bufmem = ByteBuffer.allocateDirect(bufmemsize);
inited = true;
}
// auto close kalau java program closed
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if (inited) {
Stop_Receiving_Streaming();
if (devid != -1) {
if (BASS.BASS_SetDevice(devid)) {
BASS.BASS_Stop();
}
}
BASS.BASS_Free();
}
}
});
}
/**
* Get / Set Streaming Volume
* 0 = silemt, 1.0f = normal, more than 1.0f = amplification
*/
public float getStreamVolume() {
return volfactor;
}
public void setStreamVolume(float value) {
volfactor = (value<0) ? 0 : value;
}
/**
* Check if Initialized
* @return true if initialized
*/
public boolean IsInitialized() {
return inited;
}
/**
* Open Playback Device for Streaming
* Stream format maybe PCM, MP3, or other supported codec
* Better to wait_for streamstarted event to get result
* @param samplingrate : sampling rate
*/
public void OpenStream(int samplingrate) {
isstreaming = false;
if (!IsInitialized()) {
raise_log_event("QZA_UDP_StreamReceiver Not Initialized");
raise_streamstarted_event(false);
}
if (!bass.BASS_SetDevice(devid)) { // kalau return true, artinya sudah init
// kalau return false, cek errornya
int errcode = bass.BASS_ErrorGetCode();
if (errcode==bassconstant.BASS_ERROR_DEVICE) {
// tidak ada device ini, return false;
raise_log_event("QZA_UDP_StreamReceiver No Device with ID="+devid);
raise_streamstarted_event(false);
} else if (errcode==bassconstant.BASS_ERROR_INIT) {
// belum initialize, maka sekarang initialize
int flags = bassconstant.BASS_DEVICE_16BITS | bassconstant.BASS_DEVICE_MONO | bassconstant.BASS_DEVICE_FREQ;
if (!bass.BASS_Init(devid, samplingrate, flags)) {
// gagal init, return false
raise_bass_error_log("OpenStream","Init");
raise_streamstarted_event(false);
}
}
}
isidle = false;
Thread creatingstream = new Thread(new streamcreatefileuser_job());
creatingstream.start();
}
// mesti di-thread, kalau nggak , akan stuck di BASS_StreamCreateFile
private class streamcreatefileuser_job implements Runnable{
private BASS_FILEPROCS procs;
private FILECLOSEPROC fc;
private FILELENPROC fl;
private FILESEEKPROC fs;
private FILEREADPROC fr;
streamcreatefileuser_job(){
fc = new FILECLOSEPROC() {
@Override
public void FILECLOSEPROC(Pointer user) {
// do thing here
}
};
fs = new FILESEEKPROC() {
@Override
public boolean FILESEEKPROC(long offset, Pointer user) {
// return false karena streaming gak bisa seek
return false;
}
};
fl = new FILELENPROC() {
@Override
public long FILELENPROC(Pointer user) {
// return 0 , karena size awal streaming tidak tahu
return 0;
}
};
fr = new FILEREADPROC() {
@Override
public int FILEREADPROC(Pointer buffer, int length, Pointer user) {
// isi Pointer buffer dengan data, supaya StreamCreateFileUser bisa keluarin handle
// kalau belum ada data masuk, tahan di sini
if (bufmem==null) return 0; // kalau sampai null, artinya selesai
synchronized(bufmem) {
if (bufmem.position()<1) {
// dalam write-mode, position<1 artinya tidak ada data, tungguin 60 detik
if (!isidle) {
// kalau sebelumnya running, sekarang jadi idle, dan raise event streamidle
isidle = true;
raise_streamidle_event();
}
try {
bufmem.wait(bufmemwait);
} catch (InterruptedException e) {
// ada interrupt selama waiting, artinya diminta selesai
return 0; // selesai aja
}
}
}
byte[] BX = BufMem_Read(length);
if (BX==null) return 0; // ternyata tidak ada datanya, selesai aja
if (BX.length<1) return 0; // ternyata tidak ada datanya, selesai aja
buffer.write(0, BX, 0, BX.length);
if (isidle) {
// kalau sebelumnya idle, sekarang menjadi running (tidak idle), dan raise event streamrunning
isidle = false;
raise_streamrunning_event();
}
return BX.length; // kembalikan berapa banyak data yang sudah diterima
}
};
procs = new BASS_FILEPROCS(fc, fl, fr, fs);
}
@Override
public void run() {
current_play_handle = bass.BASS_StreamCreateFileUser(bassconstant.STREAMFILE_BUFFER, 0, procs, null); // nunggu di sini sampai FILEREADPROC dapat data
if (current_play_handle==0) {
// tidak bisa open
raise_bass_error_log("OpenStream","StreamCreateFileUser");
raise_streamstarted_event(false);
return;
}
if (!bass.BASS_ChannelGetInfo(current_play_handle, channelinfo)) {
// tidak bisa get channelinfo
raise_bass_error_log("OpenStream","ChannelGetInfo");
raise_streamstarted_event(false);
return;
} else {
channelinfo.read(); // baca datanya
}
if (!bass.BASS_ChannelSetAttribute(current_play_handle, bassconstant.BASS_ATTRIB_VOL, 1.0f)) {
// tidak bisa set volume
raise_bass_error_log("OpenStream","ChannelSetAttribute::Attrib_Vol");
raise_streamstarted_event(false);
return;
}
if (!bass.BASS_ChannelSetAttribute(current_play_handle, bassconstant.BASS_ATTRIB_NOBUFFER, 1)) {
// tidak bisa set No Buffer
raise_bass_error_log("OpenStream","ChannelSetAttribute::Attrib_NoBuffer");
raise_streamstarted_event(false);
return;
}
if (!bass.BASS_ChannelSetAttribute(current_play_handle, bassconstant.BASS_ATTRIB_GRANULE, 500)) { // 500 = 1000 bytes, karena 16bits = 2 byte
// tidak bisa set Granule
raise_bass_error_log("OpenStream","ChannelSetAttribute::Attrib_Granule");
raise_streamstarted_event(false);
return;
}
dspproc = create_dspproc();
dsphandle = bass.BASS_ChannelSetDSP(current_play_handle,dspproc , null, 1);
if (dsphandle==0) {
// tidak bisa set DSP
raise_bass_error_log("OpenStream","ChannelSetDSP");
raise_streamstarted_event(false);
return;
}
if (!bass.BASS_ChannelPlay(current_play_handle, true)) {
raise_bass_error_log("OpenStream","ChannelPlay");
raise_streamstarted_event(false);
return;
}
pcmbytescount = 0;
isstreaming = true;
Thread tx = new Thread(new playback_monitoring());
tx.start();
}
};
private class playback_monitoring implements Runnable {
@Override
public void run() {
raise_streamstarted_event(true);
int playstatus = 0;
while(true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
if (!isstreaming) break;
if (current_play_handle==0) break;
playstatus = bass.BASS_ChannelIsActive(current_play_handle);
if (playstatus==bassconstant.BASS_ACTIVE_PAUSED) break; // paused, berhenti aja
if (playstatus==bassconstant.BASS_ACTIVE_STOPPED) break; // kalau stop, berhenti
// kalau statusnya active_play atau active_stalled , biarin aja
}
close_all();
raise_streamstopped_event();
}
}
/**
* How much bytes already decoded in playback buffer (PCM format)
* @return value in long
*/
public long getPCM_BytesCount() {
return pcmbytescount;
}
private IDSPPROC create_dspproc() {
IDSPPROC newproc = new IDSPPROC() {
@Override
public void DSPPROC(int handle, int channel, Pointer bx, int length, Pointer user) {
// handle = DSP handle
// channel = current_play_handle
// buffer = decoded bytes di sini
// length = jumlahnya
// user = abaikan aja
if (handle==0) return;
if (channel==0) return;
if (length<1) return;
if (bx==null) return;
pcmbytescount+= length;
Calculate_VU_and_Volume(bx.getByteBuffer(0, length));
byte[] bufbyte = bx.getByteArray(0, length);
raise_pcmbytes_event(bufbyte);
}
};
return newproc;
}
/**
* Ambil Streaming Handle, kalau mau direkam, atau di-encode lagi
* @return 0 = Not Streaming, lainnya = Streaming
*/
public int getStream_Handle() {
return current_play_handle;
}
private void Calculate_VU_and_Volume(ByteBuffer bufx) {
if (bufx==null) return;
ShortBuffer vubuf = bufx.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
// calculate volume and vu
short temp = 0;
short maxvalue = 0;
int pos = 0;
vubuf.rewind();
while(vubuf.hasRemaining()) {
pos = vubuf.position(); // ambil posisi sekarang
temp = (short)(vubuf.get() * volfactor); // ambil nilai, dikali volfactor
vubuf.position(pos); // balik posisi tadi
vubuf.put(temp); // taruh balik
if (temp > maxvalue) maxvalue = temp; // cari nilai maksimum untuk VU
}
int vuvalue = (int)((maxvalue/32767.0)*100);
raise_vulevel_event(vuvalue);
}
/**
* Stop Playback from receiving streams
*/
public void Stop_Receiving_Streaming() {
isstreaming = false;
close_all();
}
private void close_all() {
isstreaming = false;
if (current_play_handle!=0) {
if (dsphandle!=0) {
if (!bass.BASS_ChannelRemoveDSP(current_play_handle, dsphandle)) {
raise_bass_error_log("close_all","ChannelRemoveDSP");
}
dsphandle = 0;
}
if (!bass.BASS_ChannelStop(current_play_handle)) {
raise_bass_error_log("close_all","ChannelStop");
}
if (!bass.BASS_StreamFree(current_play_handle)) {
raise_bass_error_log("close_all","StreamFree");
}
current_play_handle = 0;
}
// revisi 29-09-2020
// Bass_Stop dan Bass_Free di Auto Close
// if (bass.BASS_SetDevice(devid)) {
// if (!bass.BASS_Stop()) {
// raise_bass_error_log("close_all","Stop");
// }
// jangan di-free-in , siapa tau deviceid ini terpakai di tempat lain, malah jadi error
// if (!bass.BASS_Free()) {
// raise_bass_error_log("close_all","Free");
// }
// } else raise_bass_error_log("close_all","SetDevice");
}
private void raise_log_event(String msg) {
if (need_log_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
}
private void raise_bass_error_log(String function, String command) {
int errcode = bass.BASS_ErrorGetCode();
if (errcode==0) return;
raise_log_event("BASS Error at Function="+function+" Command="+command+", Code="+BASS.GetBassErrorString(errcode));
}
private void raise_streamstarted_event(boolean success) {
if (need_streamstarted_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_streamstarted", false, new Object[] {DateTime.getNow(), success});
}
private void raise_streamstopped_event() {
if (need_streamstopped_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_streamstopped", false, new Object[] {DateTime.getNow()});
}
private void raise_streamidle_event() {
if (need_streamidle_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_streamidle", false, new Object[] {DateTime.getNow()});
}
private void raise_streamrunning_event() {
if (need_streamrunning_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_streamrunning", false, new Object[] {DateTime.getNow()});
}
private void raise_pcmbytes_event(byte[] bb) {
if (need_pcmbytes_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_pcmbytes", false, new Object[] {bb});
}
private void raise_vulevel_event(int value) {
if (need_vulevel_event) {
ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_vulevel", false, new Object[] {value});
}
}
private void raise_tcpconnected_event(boolean value) {
if (need_tcpconnected_event) {
ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_tcpconnected", false, new Object[] {value});
}
}
private void raise_datareceiverstatus_event(boolean status, InetSocketAddress sa) {
if (need_datareceiverstatus_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_datareceiverstatus", false, new Object[] {status, sa.getHostString(),sa.getPort()});
}
private byte[] BufMem_Read(int readsize) {
if (bufmem==null) return null;
if (readsize<1) return null; // kagak niat baca
synchronized(bufmem){
bufmem.flip(); // pindah mode baca
int bytescount = bufmem.remaining();
if (bytescount>0) {
// ada yang bisa dibaca
if (readsize > bytescount) readsize = bytescount; // data tersedia, tidak sebanyak jumlah data yang ingin dibaca. Maka crop aja
byte[] bytenya = new byte[readsize];
bufmem.get(bytenya);
bufmem.compact(); // pindah mode tulis
return bytenya;
} else {
// tidak ada yang bisa dibaca
bufmem.flip(); // gak jadi baca, pindah mode tulis lagi
return null;
}
}
}
@Override
@BA.Hide
public void data_received(Pointer datanya, int length) {
if (bufmem!=null) {
if (bufmem.remaining() < length) {
// kurang size nya, buang dulu yang depan
int sizebuang = length - bufmem.remaining(); // ini size yang harus dibuang
BufMem_Read(sizebuang); // hasil balikan ini, bodo amat !
}
synchronized(bufmem) {
byte[] bytetulis = datanya.getByteArray(0, length);
bufmem.put(bytetulis);
bufmem.notifyAll(); // notify bufmem.wait yang ada di FILEREADPROC
}
}
}
@Override
@BA.Hide
public void log(String msg) {
raise_log_event("DataReceiver Log : "+msg);
}
@Override
@BA.Hide
public void tcpconnectresult(boolean success) {
raise_tcpconnected_event(success);
}
@Override
@BA.Hide
public void connected(InetSocketAddress sa) {
raise_log_event("DataReceiver connected with "+sa.getHostString()+":"+sa.getPort());
raise_datareceiverstatus_event(true,sa);
}
@Override
@BA.Hide
public void disconnected(InetSocketAddress sa) {
if (isstreaming) {
raise_log_event("Force Stop Receiving Streaming, because DataReceiver disconnected");
Stop_Receiving_Streaming();
} else {
raise_log_event("DataReceiver disconnected");
}
raise_datareceiverstatus_event(false,sa);
}
}

View File

@@ -0,0 +1,789 @@
package aas;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASS.BASS_DEVICEINFO;
import com.un4seen.bass.BASS.BASS_INFO;
import com.un4seen.bass.BASS.IDSPPROC;
import com.un4seen.bass.BASS.SYNCPROC;
import com.un4seen.bass.BASS.bassconstant;
import com.un4seen.bass.BASSmix;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.Bit;
import jbass.Bass_DeviceInfo;
@BA.Events(values = {
"log(msg as string)",
"openudpstreamchannel(success as boolean, localip as string, localport as int, streamhandle as int, channelnumber as int)",
"playbackdevicefailed(deviceid as int)",
"streamingstatus(channel as int, vu as int, bufferspace as int)",
"playbackstatus(channel as int, value as string)"
})
@BA.ShortName("AAS_Receiver_Multichannel")
/**
* AAS Receiver multichannel with 7.1 soundcard
* @author rdkartono
*/
public class AAS_Receiver_Multichannel {
private BA ba;
private String event;
private Object Me = this;
private BASS bass;
private BASSmix bassmix;
private boolean inited = false;
private int deviceid = -1;
private int mixerhandle = 0 ;
private boolean need_log_event = false;
private boolean need_openudpstreamchannel_event = false;
private boolean need_playbackdevicefailed_event = false;
private boolean need_streamingstatus_event = false;
private boolean need_playbackstatus_event = false;
ExecutorService exec = null;
private class streamstatusclass {
public int vu = 0;
public int handle = 0;
public final Integer chnumber;
public DatagramSocket theudp = null;
private ByteBuffer buffer;
public final IDSPPROC channeldsp;
public final SYNCPROC channelstalled;
public final SYNCPROC channelend;
public final SYNCPROC mixerchannelstalled;
public final SYNCPROC mixerchannelend;
public int relaystatus = -1;
public streamstatusclass(int chnumber) {
this.chnumber = chnumber;
buffer = ByteBuffer.allocate(32*1000);
channeldsp = new IDSPPROC() {
@Override
public void DSPPROC(int handle, int channel, Pointer buffer, int length, Pointer user) {
if (buffer!=null) {
if (length>0) {
ByteBuffer bb = buffer.getByteBuffer(0, length);
ShortBuffer sb = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short curvalue=0, maxvalue=0;
while(sb.hasRemaining()) {
curvalue = sb.get();
if (curvalue>maxvalue) maxvalue = curvalue;
}
vu = (int)( (maxvalue * 100.0)/Short.MAX_VALUE );
}
}
}
};
channelstalled = new SYNCPROC() {
@Override
public void SYNCPROC(int handle, int channel, int data, Pointer user) {
switch(data) {
case 0 :
raise_log_event("BASS_ChannelSetSync Channel="+chnumber+" is stalled");
break;
case 1 :
raise_log_event("BASS_ChannelSetSync Channel="+chnumber+" is resumed");
break;
default :
raise_log_event("BASS_ChannelSetSync Channel="+chnumber+" is unknown stall, data="+data);
break;
}
}
};
mixerchannelstalled = new SYNCPROC() {
@Override
public void SYNCPROC(int handle, int channel, int data, Pointer user) {
switch(data) {
case 0 :
raise_log_event("BASS_Mixer_ChannelSetSync Channel="+chnumber+" is stalled");
break;
case 1 :
raise_log_event("BASS_Mixer_ChannelSetSync Channel="+chnumber+" is resumed");
break;
default :
raise_log_event("BASS_Mixer_ChannelSetSync Channel="+chnumber+" is unknown stall, data="+data);
break;
}
}
};
channelend = new SYNCPROC() {
@Override
public void SYNCPROC(int handle, int channel, int data, Pointer user) {
switch(data) {
case 0 :
raise_log_event("BASS_ChannelSetSync channel="+chnumber+" is normal end position");
break;
case 1 :
raise_log_event("BASS_ChannelSetSync channel="+chnumber+" is backward jump in MOD music");
break;
case 2 :
raise_log_event("BASS_ChannelSetSync channel="+chnumber+" is BASS_POS_END position");
break;
case 3 :
raise_log_event("BASS_ChannelSetSync channel="+chnumber+" is end of tail (BASS_ATTRIB_TAIL)");
break;
default :
raise_log_event("BASS_ChannelSetSync channel="+chnumber+" is unknown end, data="+data);
break;
}
}
};
mixerchannelend = new SYNCPROC() {
@Override
public void SYNCPROC(int handle, int channel, int data, Pointer user) {
switch(data) {
case 0 :
raise_log_event("BASS_Mixer_ChannelSetSync channel="+chnumber+" is normal end position");
break;
case 1 :
raise_log_event("BASS_Mixer_ChannelSetSync channel="+chnumber+" is backward jump in MOD music");
break;
case 2 :
raise_log_event("BASS_Mixer_ChannelSetSync channel="+chnumber+" is BASS_POS_END position");
break;
case 3 :
raise_log_event("BASS_Mixer_ChannelSetSync channel="+chnumber+" is end of tail (BASS_ATTRIB_TAIL)");
break;
default :
raise_log_event("BASS_Mixer_ChannelSetSync channel="+chnumber+" is unknown end, data="+data);
break;
}
}
};
}
public int BufferRemaining() {
return buffer.remaining();
}
public void PushData(byte[] bb, int bblen) {
buffer.put(bb,0,bblen);
}
@SuppressWarnings("unused")
public void PushData(byte[] bb) {
buffer.put(bb);
}
@SuppressWarnings("unused")
public byte[] PullData(int length) {
byte[] readdata = new byte[length];
buffer.flip();
buffer.get(readdata);
buffer.compact();
return readdata;
}
public byte[] PullAllData() {
if (BufferPosition()>0) {
buffer.flip();
byte[] readdata = new byte[buffer.remaining()];
buffer.get(readdata);
buffer.compact();
return readdata;
} else return null;
}
public int BufferPosition() {
return buffer.position();
}
@SuppressWarnings("unused")
public boolean Buffer_inWriteMode() {
return buffer.limit()==buffer.capacity() ? true : false;
}
}
private volatile streamstatusclass[] streamingstatus = null;
public AAS_Receiver_Multichannel() {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
BA.Log("AAS_Receiver_Multichannel ShutdownHook");
if (exec!=null) {
exec.shutdown();
try {
exec.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
BA.Log("ExecutorService awaitTermination failed, exception="+e.getMessage());
}
}
CloseDevice();
}
});
}
/**
* Initialize AAS Receiver Multichannel
* @param event eventname
*/
public void Initialize(BA ba, String event) {
this.ba = ba;
this.event = event;
check_events();
bass = new BASS();
bassmix = new BASSmix();
int bassversion = bass.BASS_GetVersion();
int bassmixversion = bassmix.BASS_Mixer_GetVersion();
if (bassversion!=0) {
if (bassmixversion!=0) {
BA.Log("BASS Version="+Bit.ToHexString(bassversion)+", Mixer Version="+Bit.ToHexString(bassmixversion));
inited = true;
exec = Executors.newFixedThreadPool(20);
}
}
}
/**
* Check if already initialized and BASS can be loaded
* @return
*/
public boolean IsInitialized() {
return inited;
}
/**
* Open Playback device
* @param devid device id, 0 = no sound, 1 = first real device, 2 = ....
* @param samplingrate samplingrate
* @return true if can be opened
*/
public boolean OpenDevice(final int devid, final int samplingrate) {
deviceid = -1;
if (inited) {
BASS_DEVICEINFO dvi = new BASS_DEVICEINFO();
if (bass.BASS_GetDeviceInfo(devid, dvi)) {
dvi.read();
Bass_DeviceInfo dv = new Bass_DeviceInfo(devid, dvi);
if (dv.isvalid) {
if (dv.IsEnabled()) {
if (dv.IsInited()) {
// sudah initialized, free dulu
bass.BASS_SetDevice(devid);
bass.BASS_Free();
}
int flags = bassconstant.BASS_DEVICE_16BITS | bassconstant.BASS_DEVICE_SPEAKERS | bassconstant.BASS_DEVICE_FREQ;
if (bass.BASS_Init(devid, samplingrate, flags)) {
this.deviceid = devid;
BASS_INFO bi = new BASS_INFO();
if (bass.BASS_GetInfo(bi)) {
bi.read();
streamingstatus = new streamstatusclass[bi.speakers];
for(int ii=0;ii<streamingstatus.length;ii++) streamingstatus[ii] = new streamstatusclass(ii);
} else raise_log_event("BASS_GetInfo failed, error="+bass.GetBassErrorString());
// bassmix.BASS_MIXER_NONSTOP
int mixerflag = 0;
int mh = bassmix.BASS_Mixer_StreamCreate(samplingrate, 8, mixerflag);
if (mh!=0) {
if (bass.BASS_ChannelPlay(mh, true)) {
this.mixerhandle = mh;
bass.BASS_ChannelSetSync(mh, bassconstant.BASS_SYNC_DEV_FAIL, 0, mixerdevfail, null);
bass.BASS_ChannelSetSync(mh, bassconstant.BASS_SYNC_STALL, 0, mixerstalled, null);
bass.BASS_ChannelSetSync(mh, bassconstant.BASS_SYNC_END, 0, mixerend, null);
// pancingan
exec.submit(new raisestreamingstatusrunnable());
return true;
} else raise_log_event("BASS_ChannelPlay mixerhandle failed, error="+bass.GetBassErrorString());
} else raise_log_event("BASS_Mixer_StreamCreate failed, error="+bass.GetBassErrorString());
} else raise_log_event("BASS_Init device="+devid+" failed, error="+bass.GetBassErrorString());
} else raise_log_event("OpenDevice device="+devid+" failed, Device is disabled");
} else raise_log_event("DeviceInfo for device="+devid+" is invalid");
} else raise_log_event("BASS_GetDeviceInfo device="+devid+" failed, error="+bass.GetBassErrorString());
} else raise_log_event("Call Initialize first");
return false;
}
private SYNCPROC mixerdevfail = new SYNCPROC() {
@Override
public void SYNCPROC(int handle, int channel, int data, Pointer user) {
raise_playbackdevicefailed_event(deviceid);
}
};
private SYNCPROC mixerend = new SYNCPROC() {
@Override
public void SYNCPROC(int handle, int channel, int data, Pointer user) {
raise_log_event("Mixer reached END");
}
};
private SYNCPROC mixerstalled = new SYNCPROC() {
@Override
public void SYNCPROC(int handle, int channel, int data, Pointer user) {
switch(data) {
case 0 :
raise_playbackstatus_event(100,"stalled");
break;
case 1 :
raise_playbackstatus_event(100,"resumed");
break;
}
}
};
/**
* Get Opened Playback Device ID
* @return -1 if not opened
*/
public int getDeviceID() {
return deviceid;
}
/**
* Close Playback Device
*/
public void CloseDevice() {
if (deviceid!=-1) {
if (inited) {
if (mixerhandle!=0) {
bass.BASS_StreamFree(mixerhandle);
}
mixerhandle = 0;
bass.BASS_SetDevice(deviceid);
bass.BASS_Free();
}
}
deviceid = -1;
}
/**
* Close UDP Streaming for channel
* @param channelnumber 0 - 7
* @return true if success
*/
public boolean Close_UDP_StreamChannel(int channelnumber) {
if (inited) {
if (deviceid!=-1) {
streamstatusclass sc = GetStreamingStatusMember(channelnumber);
if (sc!=null) {
if (sc.theudp!=null) {
if (sc.theudp.isClosed()==false) {
sc.theudp.close();
raise_log_event("Close_UDP_StreamChannel, closing UDP for channel="+sc.chnumber);
}
sc.theudp = null;
}
if (sc.handle!=0) {
if (!bassmix.BASS_Mixer_ChannelRemove(sc.handle)) {
raise_log_event("Close_UDP_StreamChannel BASS_Mixer_ChannelRemove failed for channel="+sc.chnumber+", error="+bass.GetBassErrorString());
}
if (!bass.BASS_ChannelStop(sc.handle)) {
raise_log_event("Close_UDP_StreamChannel BASS_ChannelStop failed for channel="+sc.chnumber+", error="+bass.GetBassErrorString());
}
if (!bass.BASS_StreamFree(sc.handle)) {
raise_log_event("Close_UDP_StreamChannel BASS_StreamFree failed for channel="+sc.chnumber+", error="+bass.GetBassErrorString());
}
sc.handle = 0;
}
return true;
}
}
}
return false;
}
/**
* Open UDP Streaming , linked with Soundcard channel
* will raise event openudpstreamchannel(success as boolean, localip as string, localport as int, streamhandle as int, channelnumber as int)
* @param samplingrate samplingrate
* @param localip valid IP address to bind
* @param localport valid port
* @param channelnumber 0 - (detectedspeakers-1)
* @return true if DatagramSocket can be created
*/
public boolean Open_UDP_StreamChannel(int samplingrate, String localip, int localport, int channelnumber) {
if (inited) {
if (deviceid!=-1) {
streamstatusclass sc = GetStreamingStatusMember(channelnumber);
if (sc!=null) {
DatagramSocket udp = null;
// coba open UDP listen
try {
udp = new DatagramSocket(new InetSocketAddress(localip, localport));
} catch (SocketException e) {
raise_log_event("Unable to create DatagramSocket, exception="+e.getMessage());
}
if (udp != null) {
// berhasil UDP listen
// mesti bikin final supaya bisa masuk ba.submitrunnable
sc.theudp = udp;
// runnable untuk ambil data dari udp;
exec.submit(new udprunnable(channelnumber));
int flag = bassconstant.BASS_STREAM_DECODE;
int handle = bass.BASS_StreamCreate(samplingrate, 1, flag, bassconstant.STREAMPROC_PUSH, null);
if (handle!=0) {
sc.handle = handle;
bass.BASS_ChannelSetAttribute(handle,bassconstant.BASS_ATTRIB_PUSH_LIMIT , 0);
bass.BASS_ChannelSetDSP(handle, sc.channeldsp, null, 0);
bass.BASS_ChannelSetSync(handle, bassconstant.BASS_SYNC_END, 0, sc.channelend, null);
bass.BASS_ChannelSetSync(handle, bassconstant.BASS_SYNC_STALL, 0, sc.channelstalled, null);
raise_openudpstreamchannel_event(true, sc.theudp.getLocalAddress().getHostAddress(), sc.theudp.getLocalPort(), sc.handle, sc.chnumber);
exec.submit(new streamputdatarunnable(channelnumber));
}else {
// gagal buka bass stream
raise_log_event("BASS_StreamCreate failed, error="+bass.GetBassErrorString());
if (sc.theudp != null) {
raise_openudpstreamchannel_event(false, sc.theudp.getLocalAddress().getHostAddress(), sc.theudp.getLocalPort(),0, sc.chnumber);
if (sc.theudp.isClosed()==false) {
sc.theudp.close();
}
sc.theudp = null;
}
}
return true;
}
}
} else raise_log_event("Call OpenDevice first");
} else raise_log_event("Call Initialize first");
return false;
}
private streamstatusclass GetStreamingStatusMember(int index) {
if (streamingstatus != null) {
if (index>=0) {
if (index<streamingstatus.length) {
return streamingstatus[index];
}
}
}
return null;
}
// Runnable untuk raise_streamingstatus_event periodik
private class raisestreamingstatusrunnable implements Runnable{
boolean keeprunning = true;
public raisestreamingstatusrunnable() {
}
@Override
public void run() {
keeprunning = true;
raise_log_event("raisestreamingstatusrunnable started");
while(keeprunning) {
try {
Thread.sleep(150);
} catch (InterruptedException e) {
keeprunning = false;
}
for(streamstatusclass ssc : streamingstatus) {
raise_streamingstatus_event(ssc.chnumber, ssc.vu, ssc.BufferRemaining());
}
}
raise_log_event("raisestreamingstatusrunnable finished");
}
}
private class streamputdatarunnable implements Runnable{
private final int channelnumber;
private final streamstatusclass ssc;
private int mixchanflag = 0;
private boolean keeprunning = false;
public streamputdatarunnable(int chnum) {
channelnumber = chnum;
ssc = GetStreamingStatusMember(channelnumber);
}
@Override
public void run() {
if (ssc!=null) {
final long delay = bass.BASS_ChannelSeconds2Bytes(ssc.handle, 2);
raise_log_event("Runnable StreamPutData started for channel="+ssc.chnumber+", will delay "+delay+" bytes");
keeprunning = true;
while(keeprunning) {
synchronized(ssc) {
if (ssc.handle==0) {
raise_log_event("ssc.handle=0 for channel="+ssc.chnumber+", breaking now");
keeprunning = false;
}
if (ssc.BufferPosition()==0) {
try {
ssc.wait(3000);
} catch (InterruptedException e) {
raise_log_event("InterruptException on buffer waiting for channel="+ssc.chnumber);
keeprunning = false;
}
}
// sampe sini , harusnya ada data, kalau gak ada, selesai aja
byte[] dataread = ssc.PullAllData();
if (dataread!=null) {
int rem = dataread.length;
Pointer bb = new Memory(rem);
bb.write(0, dataread, 0, rem);
int qq = bass.BASS_StreamPutData(ssc.handle, bb, rem);
if (ssc.relaystatus!=1) {
ssc.relaystatus = 1;
raise_playbackstatus_event(ssc.chnumber, "resumed");
}
mixchanflag = bassmix.BASS_Mixer_ChannelFlags(ssc.handle, 0, 0);
if ((mixchanflag & bassmix.BASS_MIXER_CHAN_PAUSE)>0) {
if (qq>delay) {
raise_log_event("Resuming mixer for channel="+ssc.chnumber);
bassmix.BASS_Mixer_ChannelFlags(ssc.handle, 0, bassmix.BASS_MIXER_CHAN_PAUSE);
}
}
} else {
ssc.vu = 0;
if (ssc.relaystatus!=0) {
ssc.relaystatus = 0;
raise_playbackstatus_event(ssc.chnumber,"stalled");
//bass.BASS_StreamPutData(ssc.handle, null, bassconstant.BASS_STREAMPROC_END); // bikin gak bisa streaming
raise_log_event("Pausing mixer for channel="+ssc.chnumber);
bassmix.BASS_Mixer_ChannelFlags(ssc.handle, bassmix.BASS_MIXER_CHAN_PAUSE, bassmix.BASS_MIXER_CHAN_PAUSE);
}
}
}
}
if (ssc!=null) {
raise_log_event("Runnable StreamPutData finished for channel="+ssc.chnumber);
}
}
}
}
private class udprunnable implements Runnable{
private final int channelnumber;
private final streamstatusclass ssc;
private boolean keeprunning = false;
public udprunnable(int chnum) {
channelnumber = chnum;
ssc = GetStreamingStatusMember(channelnumber);
}
@Override
public void run() {
if (ssc!=null) {
raise_log_event("Runnable UDP Receive started for channel="+ssc.chnumber);
keeprunning = true;
while(keeprunning) {
if (ssc.theudp!=null && ssc.theudp.isClosed()==false) {
try {
// ngeblok di sini sampe dapat paket
DatagramPacket pkg = new DatagramPacket(new byte[1500],1500);
ssc.theudp.receive(pkg);
if (pkg!=null) {
int length = pkg.getLength();
byte[] data = pkg.getData();
if (ssc!=null) {
synchronized(ssc) {
ssc.PushData(data, length);
ssc.notify();
}
};
}
} catch (IOException e) {
raise_log_event("IOException dari theudp, exception="+e.getMessage());
ssc.theudp.close();
ssc.theudp = null;
keeprunning = false;
}
} else {
raise_log_event("theudp Runnable must break, theudp isclosed");
keeprunning = false;
}
}
raise_log_event("Runnable UDP Receive finished for channel="+ssc.chnumber);
}
}
}
/**
* Play handle
* Obtain handle from event openudpstreamchannel
* @param handle
* @param channelnumber 0 - 7
* @return true if can be played
*/
public boolean PlayHandle(int handle, int channelnumber) {
if (inited) {
if (mixerhandle!=0) {
if (handle != 0) {
int flag = bassmix.BASS_MIXER_CHAN_PAUSE;
switch(channelnumber) {
case 1 :
flag |= bassconstant.BASS_SPEAKER_FRONTRIGHT;
break;
case 2:
flag |= bassconstant.BASS_SPEAKER_REARLEFT;
break;
case 3:
flag |= bassconstant.BASS_SPEAKER_REARRIGHT;
break;
case 4:
flag |= bassconstant.BASS_SPEAKER_REAR2LEFT;
break;
case 5:
flag |= bassconstant.BASS_SPEAKER_REAR2RIGHT;
break;
case 6:
flag |= bassconstant.BASS_SPEAKER_CENTER;
break;
case 7:
flag |= bassconstant.BASS_SPEAKER_LFE;
break;
default:
flag |= bassconstant.BASS_SPEAKER_FRONTLEFT;
break;
}
if (bassmix.BASS_Mixer_StreamAddChannel(mixerhandle, handle, flag)) {
streamstatusclass ssc = GetStreamingStatusMember(channelnumber);
if (ssc!=null) {
bassmix.BASS_Mixer_ChannelSetSync(handle, bassconstant.BASS_SYNC_STALL, 0, ssc.mixerchannelstalled, null);
bassmix.BASS_Mixer_ChannelSetSync(handle, bassconstant.BASS_SYNC_END, 0, ssc.mixerchannelend, null);
}
return true;
} else raise_log_event("BASS_Mixer_StreamAddChannel failed , error="+bass.GetBassErrorString());
} else raise_log_event("Invalid handle, obtain handle from event openudpstreamchannel");
} else raise_log_event("Call OpenDevice first");
} else raise_log_event("Call Initialize first");
return false;
}
/**
* Stop playing handle, and remove it
* @param handle
* @return true if can be done
*/
public boolean StopHandle(int handle) {
if (inited) {
if (mixerhandle!=0) {
if (handle!=0) {
if (bassmix.BASS_Mixer_ChannelRemove(handle)) {
if (bass.BASS_StreamFree(handle)) {
raise_log_event("StopHandle handle="+handle+" succcess");
return true;
} else raise_log_event("BASS_StreamFree failed, error="+bass.GetBassErrorString());
} else raise_log_event("BASS_Mixer_ChannelRemove failed, error="+bass.GetBassErrorString());
} else raise_log_event("Invalid handle, obtain handle from event openudpstreamchannel");
} else raise_log_event("Call OpenDevice first");
} else raise_log_event("Call Initialize first");
return false;
}
private void check_events() {
need_log_event = ba.subExists(event+"_log");
need_openudpstreamchannel_event = ba.subExists(event+"_openudpstreamchannel");
need_playbackdevicefailed_event = ba.subExists(event+"_playbackdevicefailed");
need_streamingstatus_event = ba.subExists(event+"_streamingstatus");
need_playbackstatus_event = ba.subExists(event+"_playbackstatus");
}
private void raise_log_event(String msg) {
if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg});
}
private void raise_openudpstreamchannel_event(boolean success, String localip, int localport, int streamhandle, int channelnumber) {
if (need_openudpstreamchannel_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_openudpstreamchannel", false, new Object[] {success, localip, localport, streamhandle, channelnumber});
}
private void raise_playbackdevicefailed_event(int deviceid) {
if (need_playbackdevicefailed_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_playbackdevicefailed", false, new Object[] {deviceid});
}
private void raise_streamingstatus_event(int channelnumber, int vu, int buffferspace) {
if (need_streamingstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_streamingstatus", false, new Object[] {channelnumber, vu, buffferspace});
}
private void raise_playbackstatus_event(int channelnumber, String value) {
if (need_playbackstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_playbackstatus", false, new Object[] {channelnumber, value});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
/*
BASSALAC 2.4 C/C++ header file
Copyright (c) 2016 Un4seen Developments Ltd.
See the BASSALAC.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSALAC
{
// additional error codes returned by BASS_ErrorGetCode
public static final int BASS_ERROR_MP4_NOSTREAM = 6000; // non-streamable due to MP4 atom order ("mdat" before "moov")
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_ALAC = 0x10e00;
public static native int BASS_ALAC_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_ALAC_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user);
public static native int BASS_ALAC_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassalac);
if (path!=null && path.length()>0) Native.register(BASSALAC.class, path);
}
}

View File

@@ -0,0 +1,73 @@
/*
BASSDSD 2.4 Java class
Copyright (c) 2014-2017 Un4seen Developments Ltd.
See the BASSDSD.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import anywheresoftware.b4a.BA;
public class BASSDSD
{
// Additional BASS_SetConfig options
public static final int BASS_CONFIG_DSD_FREQ = 0x10800;
public static final int BASS_CONFIG_DSD_GAIN = 0x10801;
// Additional BASS_DSD_StreamCreateFile/etc flags
public static final int BASS_DSD_RAW = 0x200;
public static final int BASS_DSD_DOP = 0x400;
public static final int BASS_DSD_DOP_AA = 0x800;
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_DSD = 0x11700;
// Additional tag types
public static final int BASS_TAG_DSD_ARTIST = 0x13000; // DSDIFF artist : String
public static final int BASS_TAG_DSD_TITLE = 0x13001; // DSDIFF title : String
public static final int BASS_TAG_DSD_COMMENT = 0x13100; // + index, DSDIFF comment : TAG_DSD_COMMENT
@BA.ShortName("TAG_DSD_COMMENT")
// @Structure.FieldOrder({"timeStampYear","TimeStampMonth","timeStampDay","timeStampHour","timeStampMinutes","cmtType","cmtRef","commentText"})
public static class TAG_DSD_COMMENT extends Structure {
short timeStampYear; // creation year
byte TimeStampMonth; // creation month
byte timeStampDay; // creation day
byte timeStampHour; // creation hour
byte timeStampMinutes; // creation minutes
short cmtType; // comment type
short cmtRef; // comment reference
String commentText; // text
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"timeStampYear","TimeStampMonth","timeStampDay","timeStampHour","timeStampMinutes",
"cmtType","cmtRef","commentText");
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Additional attributes
public static final int BASS_ATTRIB_DSD_GAIN = 0x14000;
public static final int BASS_ATTRIB_DSD_RATE = 0x14001;
public static native int BASS_DSD_StreamCreateFile(String file, long offset, long length, int flags, int freq);
public static native int BASS_DSD_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user, int freq);
public static native int BASS_DSD_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user, int freq);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassdsd);
if (path!=null && path.length()>0) Native.register(BASSDSD.class, path); }
}

View File

@@ -0,0 +1,135 @@
/*
BASSFLAC 2.4 Java class
Copyright (c) 2004-2017 Un4seen Developments Ltd.
See the BASSFLAC.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import anywheresoftware.b4a.BA;
public class BASSFLAC
{
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_FLAC = 0x10900;
public static final int BASS_CTYPE_STREAM_FLAC_OGG = 0x10901;
// Additional tag types
public static final int BASS_TAG_FLAC_CUE = 12; // cuesheet : TAG_FLAC_CUE
public static final int BASS_TAG_FLAC_PICTURE = 0x12000; // + index #, picture : TAG_FLAC_PICTURE
public static final int BASS_TAG_FLAC_METADATA = 0x12400; // + index #, application metadata : TAG_FLAC_METADATA
@BA.ShortName("TAG_FLAC_PICTURE")
// @Structure.FieldOrder({"apic","mime","desc","width","height","depth","colors","length","data"})
public static class TAG_FLAC_PICTURE extends Structure{
public int apic; // ID3v2 "APIC" picture type
public String mime; // mime type
public String desc; // description
public int width;
public int height;
public int depth;
public int colors;
public int length; // data length
public Pointer data;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"apic","mime","desc","width","height","depth","colors","length","data");
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
@BA.ShortName("TAG_FLAC_CUE_TRACK_INDEX")
// @Structure.FieldOrder({"offset","number"})
public static class TAG_FLAC_CUE_TRACK_INDEX extends Structure{
public long offset; // index offset relative to track offset (samples)
public int number; // index number
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"offset","number");
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
@BA.ShortName("TAG_FLAC_CUE_TRACK")
// @Structure.FieldOrder({"offset","number","isrc","flags","nindexes","indexes"})
public static class TAG_FLAC_CUE_TRACK extends Structure{
public long offset; // track offset (samples)
public int number; // track number
public String isrc; // ISRC
public int flags;
public int nindexes; // number of indexes
public TAG_FLAC_CUE_TRACK_INDEX[] indexes; // the indexes
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"offset","number","isrc","flags","nindexes","indexes");
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
@BA.ShortName("TAG_FLAC_CUE")
// @Structure.FieldOrder({"catalog","leadin","iscd","ntracks","tracks"})
public static class TAG_FLAC_CUE extends Structure{
public String catalog; // media catalog number
public int leadin; // lead-in (samples)
public boolean iscd; // a CD?
public int ntracks; // number of tracks
public TAG_FLAC_CUE_TRACK[] tracks; // the tracks
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"catalog","leadin","iscd","ntracks","tracks");
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// TAG_FLAC_CUE_TRACK flags
public static final int TAG_FLAC_CUE_TRACK_DATA = 1; // data track
public static final int TAG_FLAC_CUE_TRACK_PRE = 2; // pre-emphasis
@BA.ShortName("TAG_FLAC_METADATA")
// @Structure.FieldOrder({"id","length","data"})
public static class TAG_FLAC_METADATA extends Structure{
public String id;
public int length; // data length
public Pointer data;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"id","length","data");
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
public static native int BASS_FLAC_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_FLAC_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user);
public static native int BASS_FLAC_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassflac);
if (path!=null && path.length()>0) Native.register(BASSFLAC.class, path); }
}

View File

@@ -0,0 +1,40 @@
/*
BASSHLS 2.4 Java class
Copyright (c) 2015-2019 Un4seen Developments Ltd.
See the BASSHLS.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSHLS
{
// additional BASS_SetConfig options
public static final int BASS_CONFIG_HLS_DOWNLOAD_TAGS = 0x10900;
public static final int BASS_CONFIG_HLS_BANDWIDTH = 0x10901;
public static final int BASS_CONFIG_HLS_DELAY = 0x10902;
// additional sync type
public static final int BASS_SYNC_HLS_SEGMENT = 0x10300;
// additional tag types
public static final int BASS_TAG_HLS_EXTINF = 0x14000; // segment's EXTINF tag : String
public static final int BASS_TAG_HLS_STREAMINF = 0x14001; // EXT-X-STREAM-INF tag : UTF-8 string
public static final int BASS_TAG_HLS_DATE = 0x14002; // EXT-X-PROGRAM-DATE-TIME tag : UTF-8 string
// additional BASS_StreamGetFilePosition mode
public static final int BASS_FILEPOS_HLS_SEGMENT = 0x10000; // segment sequence number
public static native int BASS_HLS_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_HLS_StreamCreateURL(String url, int flags, BASS.DOWNLOADPROC proc, Pointer user);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_basshls);
if (path!=null && path.length()>0) Native.register(BASSHLS.class, path);
}
}

View File

@@ -0,0 +1,389 @@
/*
BASSMIDI 2.4 Java class
Copyright (c) 2006-2018 Un4seen Developments Ltd.
See the BASSMIDI.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Callback;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import anywheresoftware.b4a.BA;
//import com.un4seen.bass.BASS.Asset;
public class BASSMIDI
{
// Additional error codes returned by BASS_ErrorGetCode
public static final int BASS_ERROR_MIDI_INCLUDE=7000;
// Additional BASS_SetConfig options
public static final int BASS_CONFIG_MIDI_COMPACT=0x10400;
public static final int BASS_CONFIG_MIDI_VOICES=0x10401;
public static final int BASS_CONFIG_MIDI_AUTOFONT=0x10402;
// Additional BASS_SetConfigPtr options
public static final int BASS_CONFIG_MIDI_DEFFONT=0x10403;
// Additional sync types
public static final int BASS_SYNC_MIDI_MARK=0x10000;
public static final int BASS_SYNC_MIDI_EVENT=0x10004;
public static final int BASS_SYNC_MIDI_TICK=0x10005;
// Additional BASS_MIDI_StreamCreateFile/etc flags
public static final int BASS_MIDI_NOSYSRESET=0x800;
public static final int BASS_MIDI_DECAYEND=0x1000;
public static final int BASS_MIDI_NOFX=0x2000;
public static final int BASS_MIDI_DECAYSEEK=0x4000;
public static final int BASS_MIDI_NOCROP=0x8000;
public static final int BASS_MIDI_NOTEOFF1=0x10000;
public static final int BASS_MIDI_SINCINTER=0x800000;
// BASS_MIDI_FontInit flags
public static final int BASS_MIDI_FONT_MMAP=0x20000;
public static final int BASS_MIDI_FONT_XGDRUMS=0x40000;
public static final int BASS_MIDI_FONT_NOFX=0x80000;
public static final int BASS_MIDI_FONT_LINATTMOD=0x100000;
@BA.ShortName("BASS_MIDI_FONT")
// @Structure.FieldOrder({"font","preset","bank"})
public static class BASS_MIDI_FONT extends Structure {
public int font; // soundfont
public int preset; // preset number (-1=all)
public int bank;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"font","preset","bank"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
@BA.ShortName("BASS_MIDI_FONTEX")
// @Structure.FieldOrder({"font","spreset","sbank","dpreset","dbank","dbanklsb"})
public static class BASS_MIDI_FONTEX extends Structure{
public int font; // soundfont
public int spreset; // source preset number
public int sbank; // source bank number
public int dpreset; // destination preset/program number
public int dbank; // destination bank number
public int dbanklsb; // destination bank number LSB
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"font","spreset","sbank","dpreset","dbank","dbanklsb"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// BASS_MIDI_StreamSet/GetFonts flag
public static final int BASS_MIDI_FONT_EX=0x1000000; // BASS_MIDI_FONTEX (auto-detected)
@BA.ShortName("BASS_MIDI_FONTINFO")
// @Structure.FieldOrder({"name","copyright","comment","presets","samsize","samload","samtype"})
public static class BASS_MIDI_FONTINFO extends Structure{
public String name;
public String copyright;
public String comment;
public int presets; // number of presets/instruments
public int samsize; // total size (in bytes) of the sample data
public int samload; // amount of sample data currently loaded
public int samtype; // sample format (CTYPE) if packed
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"name","copyright","comment","presets","samsize","samload","samtype"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
@BA.ShortName("BASS_MIDI_MARK")
// @Structure.FieldOrder({"track","pos","text"})
public static class BASS_MIDI_MARK extends Structure{
public int track; // track containing marker
public int pos; // marker position
public String text; // marker text
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"track","pos","text"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
@BA.ShortName("BASS_MIDI_MARKB")
// @Structure.FieldOrder({"track","pos","text"})
public static class BASS_MIDI_MARKB extends Structure{
public int track; // track containing marker
public int pos; // marker position
public byte[] text; // marker text
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"track","pos","text"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Marker types
public static final int BASS_MIDI_MARK_MARKER=0; // marker
public static final int BASS_MIDI_MARK_CUE=1; // cue point
public static final int BASS_MIDI_MARK_LYRIC=2; // lyric
public static final int BASS_MIDI_MARK_TEXT=3; // text
public static final int BASS_MIDI_MARK_TIMESIG=4; // time signature
public static final int BASS_MIDI_MARK_KEYSIG=5; // key signature
public static final int BASS_MIDI_MARK_COPY=6; // copyright notice
public static final int BASS_MIDI_MARK_TRACK=7; // track name
public static final int BASS_MIDI_MARK_INST=8; // instrument name
public static final int BASS_MIDI_MARK_TRACKSTART=9; // track start (SMF2)
public static final int BASS_MIDI_MARK_TICK=0x10000; // flag: get position in ticks (otherwise bytes)
// MIDI events
public static final int MIDI_EVENT_NOTE=1;
public static final int MIDI_EVENT_PROGRAM=2;
public static final int MIDI_EVENT_CHANPRES=3;
public static final int MIDI_EVENT_PITCH=4;
public static final int MIDI_EVENT_PITCHRANGE=5;
public static final int MIDI_EVENT_DRUMS=6;
public static final int MIDI_EVENT_FINETUNE=7;
public static final int MIDI_EVENT_COARSETUNE=8;
public static final int MIDI_EVENT_MASTERVOL=9;
public static final int MIDI_EVENT_BANK=10;
public static final int MIDI_EVENT_MODULATION=11;
public static final int MIDI_EVENT_VOLUME=12;
public static final int MIDI_EVENT_PAN=13;
public static final int MIDI_EVENT_EXPRESSION=14;
public static final int MIDI_EVENT_SUSTAIN=15;
public static final int MIDI_EVENT_SOUNDOFF=16;
public static final int MIDI_EVENT_RESET=17;
public static final int MIDI_EVENT_NOTESOFF=18;
public static final int MIDI_EVENT_PORTAMENTO=19;
public static final int MIDI_EVENT_PORTATIME=20;
public static final int MIDI_EVENT_PORTANOTE=21;
public static final int MIDI_EVENT_MODE=22;
public static final int MIDI_EVENT_REVERB=23;
public static final int MIDI_EVENT_CHORUS=24;
public static final int MIDI_EVENT_CUTOFF=25;
public static final int MIDI_EVENT_RESONANCE=26;
public static final int MIDI_EVENT_RELEASE=27;
public static final int MIDI_EVENT_ATTACK=28;
public static final int MIDI_EVENT_DECAY=29;
public static final int MIDI_EVENT_REVERB_MACRO=30;
public static final int MIDI_EVENT_CHORUS_MACRO=31;
public static final int MIDI_EVENT_REVERB_TIME=32;
public static final int MIDI_EVENT_REVERB_DELAY=33;
public static final int MIDI_EVENT_REVERB_LOCUTOFF=34;
public static final int MIDI_EVENT_REVERB_HICUTOFF=35;
public static final int MIDI_EVENT_REVERB_LEVEL=36;
public static final int MIDI_EVENT_CHORUS_DELAY=37;
public static final int MIDI_EVENT_CHORUS_DEPTH=38;
public static final int MIDI_EVENT_CHORUS_RATE=39;
public static final int MIDI_EVENT_CHORUS_FEEDBACK=40;
public static final int MIDI_EVENT_CHORUS_LEVEL=41;
public static final int MIDI_EVENT_CHORUS_REVERB=42;
public static final int MIDI_EVENT_USERFX=43;
public static final int MIDI_EVENT_USERFX_LEVEL=44;
public static final int MIDI_EVENT_USERFX_REVERB=45;
public static final int MIDI_EVENT_USERFX_CHORUS=46;
public static final int MIDI_EVENT_DRUM_FINETUNE=50;
public static final int MIDI_EVENT_DRUM_COARSETUNE=51;
public static final int MIDI_EVENT_DRUM_PAN=52;
public static final int MIDI_EVENT_DRUM_REVERB=53;
public static final int MIDI_EVENT_DRUM_CHORUS=54;
public static final int MIDI_EVENT_DRUM_CUTOFF=55;
public static final int MIDI_EVENT_DRUM_RESONANCE=56;
public static final int MIDI_EVENT_DRUM_LEVEL=57;
public static final int MIDI_EVENT_DRUM_USERFX=58;
public static final int MIDI_EVENT_SOFT=60;
public static final int MIDI_EVENT_SYSTEM=61;
public static final int MIDI_EVENT_TEMPO=62;
public static final int MIDI_EVENT_SCALETUNING=63;
public static final int MIDI_EVENT_CONTROL=64;
public static final int MIDI_EVENT_CHANPRES_VIBRATO=65;
public static final int MIDI_EVENT_CHANPRES_PITCH=66;
public static final int MIDI_EVENT_CHANPRES_FILTER=67;
public static final int MIDI_EVENT_CHANPRES_VOLUME=68;
public static final int MIDI_EVENT_MOD_VIBRATO=69;
public static final int MIDI_EVENT_MODRANGE=69;
public static final int MIDI_EVENT_BANK_LSB=70;
public static final int MIDI_EVENT_KEYPRES=71;
public static final int MIDI_EVENT_KEYPRES_VIBRATO=72;
public static final int MIDI_EVENT_KEYPRES_PITCH=73;
public static final int MIDI_EVENT_KEYPRES_FILTER=74;
public static final int MIDI_EVENT_KEYPRES_VOLUME=75;
public static final int MIDI_EVENT_SOSTENUTO=76;
public static final int MIDI_EVENT_MOD_PITCH=77;
public static final int MIDI_EVENT_MOD_FILTER=78;
public static final int MIDI_EVENT_MOD_VOLUME=79;
public static final int MIDI_EVENT_MIXLEVEL=0x10000;
public static final int MIDI_EVENT_TRANSPOSE=0x10001;
public static final int MIDI_EVENT_SYSTEMEX=0x10002;
public static final int MIDI_EVENT_SPEED=0x10004;
public static final int MIDI_EVENT_END=0;
public static final int MIDI_EVENT_END_TRACK=0x10003;
public static final int MIDI_EVENT_NOTES=0x20000;
public static final int MIDI_EVENT_VOICES=0x20001;
public static final int MIDI_SYSTEM_DEFAULT=0;
public static final int MIDI_SYSTEM_GM1=1;
public static final int MIDI_SYSTEM_GM2=2;
public static final int MIDI_SYSTEM_XG=3;
public static final int MIDI_SYSTEM_GS=4;
@BA.ShortName("BASS_MIDI_EVENT")
// @Structure.FieldOrder({"event","param","chan","tick","pos"})
public static class BASS_MIDI_EVENT extends Structure{
public int event; // MIDI_EVENT_xxx
public int param;
public int chan;
public int tick; // event position (ticks)
public int pos; // event position (bytes)
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"event","param","chan","tick","pos"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// BASS_MIDI_StreamEvents modes
public static final int BASS_MIDI_EVENTS_STRUCT=0; // BASS_MIDI_EVENT structures
public static final int BASS_MIDI_EVENTS_RAW=0x10000; // raw MIDI event data
public static final int BASS_MIDI_EVENTS_SYNC=0x1000000; // flag: trigger event syncs
public static final int BASS_MIDI_EVENTS_NORSTATUS=0x2000000; // flag: no running status
public static final int BASS_MIDI_EVENTS_CANCEL=0x4000000; // flag: cancel pending events
public static final int BASS_MIDI_EVENTS_TIME=0x8000000; // flag: delta-time info is present
public static final int BASS_MIDI_EVENTS_ABSTIME=0x10000000; // flag: absolute time info is present
// BASS_MIDI_StreamGetChannel special channels
public static final int BASS_MIDI_CHAN_CHORUS=-1;
public static final int BASS_MIDI_CHAN_REVERB=-2;
public static final int BASS_MIDI_CHAN_USERFX=-3;
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_MIDI=0x10d00;
// Additional attributes
public static final int BASS_ATTRIB_MIDI_PPQN=0x12000;
public static final int BASS_ATTRIB_MIDI_CPU=0x12001;
public static final int BASS_ATTRIB_MIDI_CHANS=0x12002;
public static final int BASS_ATTRIB_MIDI_VOICES=0x12003;
public static final int BASS_ATTRIB_MIDI_VOICES_ACTIVE=0x12004;
public static final int BASS_ATTRIB_MIDI_STATE=0x12005;
public static final int BASS_ATTRIB_MIDI_SRC=0x12006;
public static final int BASS_ATTRIB_MIDI_KILL=0x12007;
public static final int BASS_ATTRIB_MIDI_TRACK_VOL=0x12100; // + track #
// Additional tag type
public static final int BASS_TAG_MIDI_TRACK=0x11000; // + track #, track text : array of null-terminated ANSI strings
// BASS_ChannelGetLength/GetPosition/SetPosition mode
public static final int BASS_POS_MIDI_TICK=2; // tick position
public interface MIDIFILTERPROC extends Callback
{
boolean MIDIFILTERPROC(int handle, int track, BASS_MIDI_EVENT event, boolean seeking, Pointer user);
/* Event filtering callback function.
handle : MIDI stream handle
track : Track containing the event
event : The event
seeking: true = the event is being processed while seeking, false = it is being played
user : The 'user' parameter value given when calling BASS_MIDI_StreamSetFilter
RETURN : true = process the event, false = drop the event */
}
@BA.ShortName("BASS_MIDI_DEVICEINFO")
// @Structure.FieldOrder({"name","id","flags"})
public static class BASS_MIDI_DEVICEINFO extends Structure{
public String name; // description
public int id;
public int flags;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"name","id","flags"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
public static native int BASS_MIDI_StreamCreate(int channels, int flags, int freq);
public static native int BASS_MIDI_StreamCreateFile(String file, long offset, long length, int flags, int freq);
public static native int BASS_MIDI_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user, int freq);
public static native int BASS_MIDI_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user, int freq);
public static native int BASS_MIDI_StreamCreateEvents(BASS_MIDI_EVENT[] events, int ppqn, int flags, int freq);
public static native boolean BASS_MIDI_StreamGetMark(int handle, int type, int index, BASS_MIDI_MARK mark);
public static native boolean BASS_MIDI_StreamGetMark(int handle, int type, int index, BASS_MIDI_MARKB mark);
public static native int BASS_MIDI_StreamGetMarks(int handle, int track, int type, BASS_MIDI_MARK[] marks);
public static native int BASS_MIDI_StreamGetMarks(int handle, int track, int type, BASS_MIDI_MARKB[] marks);
public static native boolean BASS_MIDI_StreamSetFonts(int handle, BASS_MIDI_FONT[] fonts, int count);
public static native boolean BASS_MIDI_StreamSetFonts(int handle, BASS_MIDI_FONTEX[] fonts, int count);
public static native int BASS_MIDI_StreamGetFonts(int handle, BASS_MIDI_FONT[] fonts, int count);
public static native int BASS_MIDI_StreamGetFonts(int handle, BASS_MIDI_FONTEX[] fonts, int count);
public static native boolean BASS_MIDI_StreamLoadSamples(int handle);
public static native boolean BASS_MIDI_StreamEvent(int handle, int chan, int event, int param);
public static native int BASS_MIDI_StreamEvents(int handle, int mode, BASS_MIDI_EVENT[] events, int length);
public static native int BASS_MIDI_StreamEvents(int handle, int mode, Pointer events, int length);
public static native int BASS_MIDI_StreamGetEvent(int handle, int chan, int event);
public static native int BASS_MIDI_StreamGetEvents(int handle, int track, int filter, BASS_MIDI_EVENT[] events);
public static native int BASS_MIDI_StreamGetEventsEx(int handle, int track, int filter, BASS_MIDI_EVENT[] events, int start, int count);
public static native boolean BASS_MIDI_StreamGetPreset(int handle, int chan, BASS_MIDI_FONT font);
public static native int BASS_MIDI_StreamGetChannel(int handle, int chan);
public static native boolean BASS_MIDI_StreamSetFilter(int handle, boolean seeking, MIDIFILTERPROC proc, Pointer user);
public static native int BASS_MIDI_FontInit(String file, int flags);
public static native int BASS_MIDI_FontInitUser(BASS.BASS_FILEPROCS procs, Pointer user, int flags);
public static native boolean BASS_MIDI_FontFree(int handle);
public static native boolean BASS_MIDI_FontGetInfo(int handle, BASS_MIDI_FONTINFO info);
public static native boolean BASS_MIDI_FontGetPresets(int handle, int[] presets);
public static native String BASS_MIDI_FontGetPreset(int handle, int preset, int bank);
public static native boolean BASS_MIDI_FontLoad(int handle, int preset, int bank);
public static native boolean BASS_MIDI_FontUnload(int handle, int preset, int bank);
public static native boolean BASS_MIDI_FontCompact(int handle);
public static native boolean BASS_MIDI_FontUnpack(int handle, String outfile, int flags);
public static native boolean BASS_MIDI_FontSetVolume(int handle, float volume);
public static native float BASS_MIDI_FontGetVolume(int handle);
public static native int BASS_MIDI_ConvertEvents(Pointer data, int length, BASS_MIDI_EVENT[] events, int count, int flags);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassmidi);
if (path!=null && path.length()>0) Native.register(BASSMIDI.class, path); }
}

View File

@@ -0,0 +1,29 @@
/*
BASSOPUS 2.4 Java class
Copyright (c) 2012 Un4seen Developments Ltd.
See the BASSOPUS.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSOPUS
{
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_OPUS = 0x11200;
// Additional attributes
public static final int BASS_ATTRIB_OPUS_ORIGFREQ = 0x13000;
public static native int BASS_OPUS_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_OPUS_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user);
public static native int BASS_OPUS_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassopus);
if (path!=null && path.length()>0) Native.register(BASSOPUS.class, path); }
}

View File

@@ -0,0 +1,34 @@
/*
BASSWEBM 2.4 Java class
Copyright (c) 2018-2019 Un4seen Developments Ltd.
See the BASSWEBM.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSWEBM
{
// Additional error codes returned by BASS_ErrorGetCode
public static final int BASS_ERROR_WEBM_TRACK = 8000;
// Additional tag types
public static final int BASS_TAG_WEBM = 0x15000; // file tags : String array
public static final int BASS_TAG_WEBM_TRACK = 0x15001; // track tags : String array
// Additional attributes
public static final int BASS_ATTRIB_WEBM_TRACK = 0x16000;
public static final int BASS_ATTRIB_WEBM_TRACKS = 0x16001;
public static native int BASS_WEBM_StreamCreateFile(String file, long offset, long length, int flags, int track);
public static native int BASS_WEBM_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user, int track);
public static native int BASS_WEBM_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user, int track);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_basswebm);
if (path!=null && path.length()>0) Native.register(BASSWEBM.class, path); }
}

View File

@@ -0,0 +1,26 @@
/*
BASSWV 2.4 Java class
Copyright (c) 2007-2012 Un4seen Developments Ltd.
See the BASSWV.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSWV
{
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_WV = 0x10500;
public static native int BASS_WV_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_WV_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user);
public static native int BASS_WV_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_basswv);
if (path!=null && path.length()>0) Native.register(BASSWV.class, path); }
}

View File

@@ -0,0 +1,37 @@
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASS_AAC
{
// additional error codes returned by BASS_ErrorGetCode
public static final int BASS_ERROR_MP4_NOSTREAM = 6000; // non-streamable due to MP4 atom order ("mdat" before "moov")
// Additional BASS_SetConfig options
public static final int BASS_CONFIG_MP4_VIDEO = 0x10700; // play the audio from MP4 videos
public static final int BASS_CONFIG_AAC_MP4 = 0x10701; // support MP4 in BASS_AAC_StreamCreateXXX functions (no need for BASS_MP4_StreamCreateXXX)
public static final int BASS_CONFIG_AAC_PRESCAN = 0x10702; // pre-scan ADTS AAC files for seek points and accurate length
// Additional BASS_AAC_StreamCreateFile/etc flags
public static final int BASS_AAC_FRAME960 = 0x1000; // 960 samples per frame
public static final int BASS_AAC_STEREO = 0x400000; // downmatrix to stereo
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_AAC = 0x10b00; // AAC
public static final int BASS_CTYPE_STREAM_MP4 = 0x10b01; // AAC in MP4
public static native int BASS_AAC_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_AAC_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user);
public static native int BASS_AAC_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
public static native int BASS_MP4_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_MP4_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
public static boolean lib_is_loaded = false;
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bass_aac);
if (path!=null && path.length()>0) Native.register(BASS_AAC.class, path);
}
}

View File

@@ -0,0 +1,21 @@
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASS_APE
{
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_APE = 0x10700;
public static native int BASS_APE_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_APE_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bass_ape);
if (path!=null && path.length()>0) Native.register(BASS_APE.class, path);
}
}

View File

@@ -0,0 +1,713 @@
/*===========================================================================
BASS_FX 2.4 - Copyright (c) 2002-2018 (: JOBnik! :) [Arthur Aminov, ISRAEL]
[http://www.jobnik.org]
bugs/suggestions/questions:
forum : http://www.un4seen.com/forum/?board=1
http://www.jobnik.org/forums
e-mail : bass_fx@jobnik.org
--------------------------------------------------
NOTE: This header will work only with BASS_FX version 2.4.12
Check www.un4seen.com or www.jobnik.org for any later versions.
* Requires BASS 2.4 (available at http://www.un4seen.com)
===========================================================================*/
package com.un4seen.bass;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Callback;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.FloatByReference;
import anywheresoftware.b4a.BA;
public class BASS_FX
{
// BASS_CHANNELINFO types
public static final int BASS_CTYPE_STREAM_TEMPO = 0x1f200;
public static final int BASS_CTYPE_STREAM_REVERSE = 0x1f201;
// Tempo / Reverse / BPM / Beat flag
public static final int BASS_FX_FREESOURCE = 0x10000; // Free the source handle as well?
// BASS_FX Version
public static native int BASS_FX_GetVersion();
/*===========================================================================
DSP (Digital Signal Processing)
===========================================================================*/
/*
Multi-channel order of each channel is as follows:
3 channels left-front, right-front, center.
4 channels left-front, right-front, left-rear/side, right-rear/side.
5 channels left-front, right-front, center, left-rear/side, right-rear/side.
6 channels (5.1) left-front, right-front, center, LFE, left-rear/side, right-rear/side.
8 channels (7.1) left-front, right-front, center, LFE, left-rear/side, right-rear/side, left-rear center, right-rear center.
*/
// DSP channels flags
public static final int BASS_BFX_CHANALL = -1; // all channels at once (as by default)
public static final int BASS_BFX_CHANNONE = 0; // disable an effect for all channels
public static final int BASS_BFX_CHAN1 = 1; // left-front channel
public static final int BASS_BFX_CHAN2 = 2; // right-front channel
public static final int BASS_BFX_CHAN3 = 4; // see above info
public static final int BASS_BFX_CHAN4 = 8; // see above info
public static final int BASS_BFX_CHAN5 = 16; // see above info
public static final int BASS_BFX_CHAN6 = 32; // see above info
public static final int BASS_BFX_CHAN7 = 64; // see above info
public static final int BASS_BFX_CHAN8 = 128; // see above info
// if you have more than 8 channels (7.1), use this function
public static int BASS_BFX_CHANNEL_N(int n) { return (1<<((n)-1)); }
// DSP effects
public static final int BASS_FX_BFX_ROTATE = 0x10000; // A channels volume ping-pong / multi channel
public static final int BASS_FX_BFX_ECHO = 0x10001; // Echo / 2 channels max (deprecated)
public static final int BASS_FX_BFX_FLANGER = 0x10002; // Flanger / multi channel (deprecated)
public static final int BASS_FX_BFX_VOLUME = 0x10003; // Volume / multi channel
public static final int BASS_FX_BFX_PEAKEQ = 0x10004; // Peaking Equalizer / multi channel
public static final int BASS_FX_BFX_REVERB = 0x10005; // Reverb / 2 channels max (deprecated)
public static final int BASS_FX_BFX_LPF = 0x10006; // Low Pass Filter 24dB / multi channel (deprecated)
public static final int BASS_FX_BFX_MIX = 0x10007; // Swap, remap and mix channels / multi channel
public static final int BASS_FX_BFX_DAMP = 0x10008; // Dynamic Amplification / multi channel
public static final int BASS_FX_BFX_AUTOWAH = 0x10009; // Auto Wah / multi channel
public static final int BASS_FX_BFX_ECHO2 = 0x1000a; // Echo 2 / multi channel (deprecated)
public static final int BASS_FX_BFX_PHASER = 0x1000b; // Phaser / multi channel
public static final int BASS_FX_BFX_ECHO3 = 0x1000c; // Echo 3 / multi channel (deprecated)
public static final int BASS_FX_BFX_CHORUS = 0x1000d; // Chorus/Flanger / multi channel
public static final int BASS_FX_BFX_APF = 0x1000e; // All Pass Filter / multi channel (deprecated)
public static final int BASS_FX_BFX_COMPRESSOR = 0x1000f; // Compressor / multi channel (deprecated)
public static final int BASS_FX_BFX_DISTORTION = 0x10010; // Distortion / multi channel
public static final int BASS_FX_BFX_COMPRESSOR2 = 0x10011; // Compressor 2 / multi channel
public static final int BASS_FX_BFX_VOLUME_ENV = 0x10012; // Volume envelope / multi channel
public static final int BASS_FX_BFX_BQF = 0x10013; // BiQuad filters / multi channel
public static final int BASS_FX_BFX_ECHO4 = 0x10014; // Echo 4 / multi channel
public static final int BASS_FX_BFX_PITCHSHIFT = 0x10015; // Pitch shift using FFT / multi channel (not available on mobile)
public static final int BASS_FX_BFX_FREEVERB = 0x10016; // Reverb using "Freeverb" algo / multi channel
/*
Deprecated effects in 2.4.10 version:
------------------------------------
BASS_FX_BFX_ECHO -> use BASS_FX_BFX_ECHO4
BASS_FX_BFX_ECHO2 -> use BASS_FX_BFX_ECHO4
BASS_FX_BFX_ECHO3 -> use BASS_FX_BFX_ECHO4
BASS_FX_BFX_REVERB -> use BASS_FX_BFX_FREEVERB
BASS_FX_BFX_FLANGER -> use BASS_FX_BFX_CHORUS
BASS_FX_BFX_COMPRESSOR -> use BASS_FX_BFX_COMPRESSOR2
BASS_FX_BFX_APF -> use BASS_FX_BFX_BQF with BASS_BFX_BQF_ALLPASS filter
BASS_FX_BFX_LPF -> use 2x BASS_FX_BFX_BQF with BASS_BFX_BQF_LOWPASS filter and appropriate fQ values
*/
// Rotate
@BA.ShortName("BASS_BFX_ROTATE")
// @Structure.FieldOrder({"fRate","lChannel"})
public static class BASS_BFX_ROTATE extends Structure{
public float fRate; // rotation rate/speed in Hz (A negative rate can be used for reverse direction)
public int lChannel; // BASS_BFX_CHANxxx flag/s (supported only even number of channels)
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fRate","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Echo (deprecated)
@BA.ShortName("BASS_BFX_ECHO")
// @Structure.FieldOrder({"fLevel","lDelay"})
public static class BASS_BFX_ECHO extends Structure{
public float fLevel; // [0....1....n] linear
public int lDelay; // [1200..30000]
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fLevel","lDelay"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Flanger (deprecated)
@BA.ShortName("BASS_BFX_FLANGER")
// @Structure.FieldOrder({"fWetDry","fSpeed","lChannel"})
public static class BASS_BFX_FLANGER extends Structure{
public float fWetDry; // [0....1....n] linear
public float fSpeed; // [0......0.09]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fWetDry","fSpeed","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Volume
@BA.ShortName("BASS_BFX_VOLUME")
// @Structure.FieldOrder({"lChannel","fVolume"})
public static class BASS_BFX_VOLUME extends Structure{
public int lChannel; // BASS_BFX_CHANxxx flag/s or 0 for global volume control
public float fVolume; // [0....1....n] linear
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"lChannel","fVolume"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Peaking Equalizer
@BA.ShortName("BASS_BFX_PEAKEQ")
// @Structure.FieldOrder({"lBand","fBandwidth","fQ","fCenter","fGain","lChannel"})
public static class BASS_BFX_PEAKEQ extends Structure{
public int lBand; // [0...............n] more bands means more memory & cpu usage
public float fBandwidth; // [0.1...........<10] in octaves - fQ is not in use (Bandwidth has a priority over fQ)
public float fQ; // [0...............1] the EE kinda definition (linear) (if Bandwidth is not in use)
public float fCenter; // [1Hz..<info.freq/2] in Hz
public float fGain; // [-15dB...0...+15dB] in dB (can be above/below these limits)
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"lBand","fBandwidth","fQ","fCenter","fGain","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Reverb (deprecated)
@BA.ShortName("BASS_BFX_REVERB")
// @Structure.FieldOrder({"fLevel","lDelay"})
public static class BASS_BFX_REVERB extends Structure{
public float fLevel; // [0....1....n] linear
public int lDelay; // [1200..10000]
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fLevel","lDelay"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Low Pass Filter (deprecated)
@BA.ShortName("BASS_BFX_LPF")
// @Structure.FieldOrder({"fResonance","fCutOffFreq","lChannel"})
public static class BASS_BFX_LPF extends Structure{
public float fResonance; // [0.01...........10]
public float fCutOffFreq; // [1Hz...info.freq/2] cutoff frequency
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fResonance","fCutOffFreq","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Swap, remap and mix
@BA.ShortName("BASS_BFX_MIX")
// @Structure.FieldOrder({"lChannel"})
public static class BASS_BFX_MIX extends Structure{
public int[] lChannel; // an array of channels to mix using BASS_BFX_CHANxxx flag/s (lChannel[0] is left channel...)
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Dynamic Amplification
@BA.ShortName("BASS_BFX_DAMP")
// @Structure.FieldOrder({"fTarget","fQuiet","fRate","fGain","fDelay","lChannel"})
public static class BASS_BFX_DAMP extends Structure{
public float fTarget; // target volume level [0<......1] linear
public float fQuiet; // quiet volume level [0.......1] linear
public float fRate; // amp adjustment rate [0.......1] linear
public float fGain; // amplification level [0...1...n] linear
public float fDelay; // delay in seconds before increasing level [0.......n] linear
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fTarget","fQuiet","fRate","fGain","fDelay","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Auto Wah
@BA.ShortName("BASS_BFX_AUTOWAH")
// @Structure.FieldOrder({"fDryMix","fWetMix","fFeedback","fRate","fRange","fFreq","lChannel"})
public static class BASS_BFX_AUTOWAH extends Structure{
public float fDryMix; // dry (unaffected) signal mix [-2......2]
public float fWetMix; // wet (affected) signal mix [-2......2]
public float fFeedback; // output signal to feed back into input [-1......1]
public float fRate; // rate of sweep in cycles per second [0<....<10]
public float fRange; // sweep range in octaves [0<....<10]
public float fFreq; // base frequency of sweep Hz [0<...1000]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fDryMix","fWetMix","fFeedback","fRate","fRange","fFreq","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Echo 2 (deprecated)
@BA.ShortName("BASS_BFX_ECHO2")
// @Structure.FieldOrder({"fDryMix","fWetMix","fFeedback","fDelay","lChannel"})
public static class BASS_BFX_ECHO2 extends Structure{
public float fDryMix; // dry (unaffected) signal mix [-2......2]
public float fWetMix; // wet (affected) signal mix [-2......2]
public float fFeedback; // output signal to feed back into input [-1......1]
public float fDelay; // delay sec [0<......n]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fDryMix","fWetMix","fFeedback","fDelay","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Phaser
@BA.ShortName("BASS_BFX_PHASER")
// @Structure.FieldOrder({"fDryMix","fWetMix","fFeedback","fRate","fRange","fFreq","lChannel"})
public static class BASS_BFX_PHASER extends Structure{
public float fDryMix; // dry (unaffected) signal mix [-2......2]
public float fWetMix; // wet (affected) signal mix [-2......2]
public float fFeedback; // output signal to feed back into input [-1......1]
public float fRate; // rate of sweep in cycles per second [0<....<10]
public float fRange; // sweep range in octaves [0<....<10]
public float fFreq; // base frequency of sweep [0<...1000]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fDryMix","fWetMix","fFeedback","fRate","fRange","fFreq","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Echo 3 (deprecated)
@BA.ShortName("BASS_BFX_ECHO3")
// @Structure.FieldOrder({"fDryMix","fWetMix","fDelay","lChannel"})
public static class BASS_BFX_ECHO3 extends Structure{
public float fDryMix; // dry (unaffected) signal mix [-2......2]
public float fWetMix; // wet (affected) signal mix [-2......2]
public float fDelay; // delay sec [0<......n]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fDryMix","fWetMix","fDelay","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Chorus/Flanger
@BA.ShortName("BASS_BFX_CHORUS")
// @Structure.FieldOrder({"fDryMix","fWetMix","fFeedback","fMinSweep","fMaxSweep","fRate","lChannel"})
public static class BASS_BFX_CHORUS extends Structure{
public float fDryMix; // dry (unaffected) signal mix [-2......2]
public float fWetMix; // wet (affected) signal mix [-2......2]
public float fFeedback; // output signal to feed back into input [-1......1]
public float fMinSweep; // minimal delay ms [0<...6000]
public float fMaxSweep; // maximum delay ms [0<...6000]
public float fRate; // rate ms/s [0<...1000]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fDryMix","fWetMix","fFeedback","fMinSweep","fMaxSweep","fRate","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// All Pass Filter (deprecated)
@BA.ShortName("BASS_BFX_APF")
// @Structure.FieldOrder({"fGain","fDelay","lChannel"})
public static class BASS_BFX_APF extends Structure{
public float fGain; // reverberation time [-1=<..<=1]
public float fDelay; // delay sec [0<....<=n]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fGain","fDelay","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Compressor (deprecated)
@BA.ShortName("BASS_BFX_COMPRESSOR")
// @Structure.FieldOrder({"fThreshold","fAttacktime","fReleasetime","lChannel"})
public static class BASS_BFX_COMPRESSOR extends Structure{
public float fThreshold; // compressor threshold [0<=...<=1]
public float fAttacktime; // attack time ms [0<.<=1000]
public float fReleasetime; // release time ms [0<.<=5000]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fThreshold","fAttacktime","fReleasetime","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Distortion
@BA.ShortName("BASS_BFX_DISTORTION")
// @Structure.FieldOrder({"fDrive","fDryMix","fWetMix","fFeedback","fVolume","lChannel"})
public static class BASS_BFX_DISTORTION extends Structure{
public float fDrive; // distortion drive [0<=...<=5]
public float fDryMix; // dry (unaffected) signal mix [-5<=..<=5]
public float fWetMix; // wet (affected) signal mix [-5<=..<=5]
public float fFeedback; // output signal to feed back into input [-1<=..<=1]
public float fVolume; // distortion volume [0=<...<=2]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fDrive","fDryMix","fWetMix","fFeedback","fVolume","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Compressor 2
@BA.ShortName("BASS_BFX_COMPRESSOR2")
// @Structure.FieldOrder({"fGain","fThreshold","fRatio","fAttack","fRelease","lChannel"})
public static class BASS_BFX_COMPRESSOR2 extends Structure{
public float fGain; // output gain of signal after compression [-60....60] in dB
public float fThreshold; // point at which compression begins [-60.....0] in dB
public float fRatio; // compression ratio [1.......n]
public float fAttack; // attack time in ms [0.01.1000]
public float fRelease; // release time in ms [0.01.5000]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fGain","fThreshold","fRatio","fAttack","fRelease","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Volume envelope
@BA.ShortName("BASS_BFX_VOLUME_ENV")
// @Structure.FieldOrder({"lChannel","lNodeCount","pNodes","bFollow"})
public static class BASS_BFX_VOLUME_ENV extends Structure{
public int lChannel; // BASS_BFX_CHANxxx flag/s
public int lNodeCount; // number of nodes
public BASS_BFX_ENV_NODE[] pNodes; // the nodes
public boolean bFollow; // follow source position
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"lChannel","lNodeCount","pNodes","bFollow"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
@BA.ShortName("BASS_BFX_ENV_NODE")
// @Structure.FieldOrder({"pos","val"})
public static class BASS_BFX_ENV_NODE extends Structure{
public double pos; // node position in seconds (1st envelope node must be at position 0)
public float val; // node value
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"pos","val"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// BiQuad Filters
public static final int BASS_BFX_BQF_LOWPASS = 0;
public static final int BASS_BFX_BQF_HIGHPASS = 1;
public static final int BASS_BFX_BQF_BANDPASS = 2; // constant 0 dB peak gain
public static final int BASS_BFX_BQF_BANDPASS_Q = 3; // constant skirt gain, peak gain = Q
public static final int BASS_BFX_BQF_NOTCH = 4;
public static final int BASS_BFX_BQF_ALLPASS = 5;
public static final int BASS_BFX_BQF_PEAKINGEQ = 6;
public static final int BASS_BFX_BQF_LOWSHELF = 7;
public static final int BASS_BFX_BQF_HIGHSHELF = 8;
@BA.ShortName("BASS_BFX_BQF")
// @Structure.FieldOrder({"lFilter","fCenter","fGain","fBandwidth","fQ","fS"})
public static class BASS_BFX_BQF extends Structure {
public int lFilter; // BASS_BFX_BQF_xxx filter types
public float fCenter; // [1Hz..<info.freq/2] Cutoff (central) frequency in Hz
public float fGain; // [-15dB...0...+15dB] Used only for PEAKINGEQ and Shelving filters in dB (can be above/below these limits)
public float fBandwidth; // [0.1...........<10] Bandwidth in octaves (fQ is not in use (fBandwidth has a priority over fQ))
// (between -3 dB frequencies for BANDPASS and NOTCH or between midpoint
// (fGgain/2) gain frequencies for PEAKINGEQ)
public float fQ; // [0.1.............1] The EE kinda definition (linear) (if fBandwidth is not in use)
public float fS; // [0.1.............1] A "shelf slope" parameter (linear) (used only with Shelving filters)
// when fS = 1, the shelf slope is as steep as you can get it and remain monotonically
// increasing or decreasing gain with frequency.
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"lFilter","fCenter","fGain","fBandwidth","fQ","fS"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Echo 4
@BA.ShortName("BASS_BFX_ECHO4")
// @Structure.FieldOrder({"fDryMix","fWetMix","fFeedback","fDelay","bStereo","lChannel"})
public static class BASS_BFX_ECHO4 extends Structure{
public float fDryMix; // dry (unaffected) signal mix [-2.......2]
public float fWetMix; // wet (affected) signal mix [-2.......2]
public float fFeedback; // output signal to feed back into input [-1.......1]
public float fDelay; // delay sec [0<.......n]
public boolean bStereo; // echo adjoining channels to each other [TRUE/FALSE]
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fDryMix","fWetMix","fFeedback","fDelay","bStereo","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Pitch shift (not available on mobile)
@BA.ShortName("BASS_BFX_PITCHSHIFT")
// @Structure.FieldOrder({"fPitchShift","fSemitones","lFFTsize","lOsamp","lChannel"})
public static class BASS_BFX_PITCHSHIFT extends Structure{
public float fPitchShift; // A factor value which is between 0.5 (one octave down) and 2 (one octave up) (1 won't change the pitch) [1 default]
// (fSemitones is not in use, fPitchShift has a priority over fSemitones)
public float fSemitones; // Semitones (0 won't change the pitch) [0 default]
public int lFFTsize; // Defines the FFT frame size used for the processing. Typical values are 1024, 2048 and 4096 [2048 default]
// It may be any value <= 8192 but it MUST be a power of 2
public int lOsamp; // Is the STFT oversampling factor which also determines the overlap between adjacent STFT frames [8 default]
// It should at least be 4 for moderate scaling ratios. A value of 32 is recommended for best quality (better quality = higher CPU usage)
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fPitchShift","fSemitones","lFFTsize","lOsamp","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
// Freeverb
public static final int BASS_BFX_FREEVERB_MODE_FREEZE = 1;
@BA.ShortName("BASS_BFX_FREEVERB")
// @Structure.FieldOrder({"fDryMix","fWetMix","fRoomSize","fDamp","fWidth","lMode","lChannel"})
public static class BASS_BFX_FREEVERB extends Structure {
public float fDryMix; // dry (unaffected) signal mix [0........1], def. 0
public float fWetMix; // wet (affected) signal mix [0........3], def. 1.0f
public float fRoomSize; // room size [0........1], def. 0.5f
public float fDamp; // damping [0........1], def. 0.5f
public float fWidth; // stereo width [0........1], def. 1
public int lMode; // 0 or BASS_BFX_FREEVERB_MODE_FREEZE, def. 0 (no freeze)
public int lChannel; // BASS_BFX_CHANxxx flag/s
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(
"fDryMix","fWetMix","fRoomSize","fDamp","fWidth","lMode","lChannel"
);
}
@BA.Hide public void autoWrite(){}
@BA.Hide public void autoRead(){}
}
/*===========================================================================
set dsp fx - BASS_ChannelSetFX
remove dsp fx - BASS_ChannelRemoveFX
set parameters - BASS_FXSetParameters
retrieve parameters - BASS_FXGetParameters
reset the state - BASS_FXReset
===========================================================================*/
/*===========================================================================
Tempo, Pitch scaling and Sample rate changers
===========================================================================*/
// NOTE: Enable Tempo supported flags in BASS_FX_TempoCreate and the others to source handle.
// tempo attributes (BASS_ChannelSet/GetAttribute)
public static final int BASS_ATTRIB_TEMPO = 0x10000;
public static final int BASS_ATTRIB_TEMPO_PITCH = 0x10001;
public static final int BASS_ATTRIB_TEMPO_FREQ = 0x10002;
// tempo attributes options
public static final int BASS_ATTRIB_TEMPO_OPTION_USE_AA_FILTER = 0x10010; // TRUE (default) / FALSE (default for multi-channel on mobile devices for lower CPU usage)
public static final int BASS_ATTRIB_TEMPO_OPTION_AA_FILTER_LENGTH = 0x10011; // 32 default (8 .. 128 taps)
public static final int BASS_ATTRIB_TEMPO_OPTION_USE_QUICKALGO = 0x10012; // TRUE (default on mobile devices for lower CPU usage) / FALSE (default)
public static final int BASS_ATTRIB_TEMPO_OPTION_SEQUENCE_MS = 0x10013; // 82 default, 0 = automatic
public static final int BASS_ATTRIB_TEMPO_OPTION_SEEKWINDOW_MS = 0x10014; // 28 default, 0 = automatic
public static final int BASS_ATTRIB_TEMPO_OPTION_OVERLAP_MS = 0x10015; // 8 default
public static final int BASS_ATTRIB_TEMPO_OPTION_PREVENT_CLICK = 0x10016; // TRUE / FALSE (default)
// tempo algorithm flags
public static final int BASS_FX_TEMPO_ALGO_LINEAR = 0x200;
public static final int BASS_FX_TEMPO_ALGO_CUBIC = 0x400; // default
public static final int BASS_FX_TEMPO_ALGO_SHANNON = 0x800;
public static native int BASS_FX_TempoCreate(int chan, int flags);
public static native int BASS_FX_TempoGetSource(int chan);
public static native float BASS_FX_TempoGetRateRatio(int chan);
/*===========================================================================
Reverse playback
===========================================================================*/
// NOTES: 1. MODs won't load without BASS_MUSIC_PRESCAN flag.
// 2. Enable Reverse supported flags in BASS_FX_ReverseCreate and the others to source handle.
// reverse attribute (BASS_ChannelSet/GetAttribute)
public static final int BASS_ATTRIB_REVERSE_DIR = 0x11000;
// playback directions
public static final int BASS_FX_RVS_REVERSE = -1;
public static final int BASS_FX_RVS_FORWARD = 1;
public static native int BASS_FX_ReverseCreate(int chan, float dec_block, int flags);
public static native int BASS_FX_ReverseGetSource(int chan);
/*===========================================================================
BPM (Beats Per Minute)
===========================================================================*/
// bpm flags
public static final int BASS_FX_BPM_BKGRND = 1; // if in use, then you can do other processing while detection's in progress. Available only in Windows platforms (BPM/Beat)
public static final int BASS_FX_BPM_MULT2 = 2; // if in use, then will auto multiply bpm by 2 (if BPM < minBPM*2)
// translation options (deprecated)
public static final int BASS_FX_BPM_TRAN_X2 = 0; // multiply the original BPM value by 2 (may be called only once & will change the original BPM as well!)
public static final int BASS_FX_BPM_TRAN_2FREQ = 1; // BPM value to Frequency
public static final int BASS_FX_BPM_TRAN_FREQ2 = 2; // Frequency to BPM value
public static final int BASS_FX_BPM_TRAN_2PERCENT = 3; // BPM value to Percents
public static final int BASS_FX_BPM_TRAN_PERCENT2 = 4; // Percents to BPM value
public interface BPMPROC extends Callback
{
void BPMPROC(int chan, float bpm, Pointer user);
}
public interface BPMPROGRESSPROC extends Callback
{
void BPMPROGRESSPROC(int chan, float percent, Pointer user);
}
// back-compatibility
public interface BPMPROCESSPROC extends Callback
{
void BPMPROCESSPROC(int chan, float percent, Pointer user);
}
public static native float BASS_FX_BPM_DecodeGet(int chan, double startSec, double endSec, int minMaxBPM, int flags, BPMPROGRESSPROC proc, Pointer user);
public static native boolean BASS_FX_BPM_CallbackSet(int handle, BPMPROC proc, double period, int minMaxBPM, int flags, Pointer user);
public static native boolean BASS_FX_BPM_CallbackReset(int handle);
public static native float BASS_FX_BPM_Translate(int handle, float val2tran, int trans); // deprecated
public static native boolean BASS_FX_BPM_Free(int handle);
/*===========================================================================
Beat position trigger
===========================================================================*/
public interface BPMBEATPROC extends Callback
{
void BPMBEATPROC(int chan, double beatpos, Pointer user);
}
public static native boolean BASS_FX_BPM_BeatCallbackSet(int handle, BPMBEATPROC proc, Pointer user);
public static native boolean BASS_FX_BPM_BeatCallbackReset(int handle);
public static native boolean BASS_FX_BPM_BeatDecodeGet(int chan, double startSec, double endSec, int flags, BPMBEATPROC proc, Pointer user);
public static native boolean BASS_FX_BPM_BeatSetParameters(int handle, float bandwidth, float centerfreq, float beat_rtime);
public static native boolean BASS_FX_BPM_BeatGetParameters(int handle, FloatByReference bandwidth, FloatByReference centerfreq, FloatByReference beat_rtime);
public static native boolean BASS_FX_BPM_BeatFree(int handle);
/*===========================================================================
Macros
===========================================================================*/
// translate linear level to logarithmic dB
public static double BASS_BFX_Linear2dB(double level)
{
return (20*Math.log10(level));
}
// translate logarithmic dB level to linear
public static double BASS_BFX_dB2Linear(double dB)
{
return (Math.pow(10,(dB)/20));
}
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bass_fx);
if (path!=null && path.length()>0) Native.register(BASS_FX.class, path);
}
}

View File

@@ -0,0 +1,21 @@
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASS_MPC
{
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_MPC = 0x10a00;
public static native int BASS_MPC_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_MPC_StreamCreateURL(String url, int offset, int flags, BASS.DOWNLOADPROC proc, Pointer user);
public static native int BASS_MPC_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bass_mpc);
if (path!=null && path.length()>0) Native.register(BASS_MPC.class, path);
}
}

View File

@@ -0,0 +1,20 @@
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASS_TTA
{
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_TTA = 0x10f00;
public static native int BASS_TTA_StreamCreateFile(String file, long offset, long length, int flags);
public static native int BASS_TTA_StreamCreateFileUser(int system, int flags, BASS.BASS_FILEPROCS procs, Pointer user);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bass_tta);
if (path!=null && path.length()>0) Native.register(BASS_TTA.class, path);
}
}

View File

@@ -0,0 +1,164 @@
/*
BASSenc 2.4 Java class
Copyright (c) 2003-2018 Un4seen Developments Ltd.
See the BASSENC.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Callback;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSenc
{
// Additional error codes returned by BASS_ErrorGetCode
public static final int BASS_ERROR_CAST_DENIED = 2100; // access denied (invalid password)
// Additional BASS_SetConfig options
public static final int BASS_CONFIG_ENCODE_PRIORITY = 0x10300;
public static final int BASS_CONFIG_ENCODE_QUEUE = 0x10301;
public static final int BASS_CONFIG_ENCODE_CAST_TIMEOUT = 0x10310;
// Additional BASS_SetConfigPtr options
public static final int BASS_CONFIG_ENCODE_CAST_PROXY = 0x10311;
// BASS_Encode_Start flags
public static final int BASS_ENCODE_NOHEAD = 1; // don't send a WAV header to the encoder
public static final int BASS_ENCODE_FP_8BIT = 2; // convert floating-point sample data to 8-bit integer
public static final int BASS_ENCODE_FP_16BIT = 4; // convert floating-point sample data to 16-bit integer
public static final int BASS_ENCODE_FP_24BIT = 6; // convert floating-point sample data to 24-bit integer
public static final int BASS_ENCODE_FP_32BIT = 8; // convert floating-point sample data to 32-bit integer
public static final int BASS_ENCODE_FP_AUTO = 14; // convert floating-point sample data back to channel's format
public static final int BASS_ENCODE_BIGEND = 16; // big-endian sample data
public static final int BASS_ENCODE_PAUSE = 32; // start encording paused
public static final int BASS_ENCODE_PCM = 64; // write PCM sample data (no encoder)
public static final int BASS_ENCODE_RF64 = 128; // send an RF64 header
public static final int BASS_ENCODE_QUEUE = 0x200; // queue data to feed encoder asynchronously
public static final int BASS_ENCODE_WFEXT = 0x400; // WAVEFORMATEXTENSIBLE "fmt" chunk
public static final int BASS_ENCODE_CAST_NOLIMIT = 0x1000; // don't limit casting data rate
public static final int BASS_ENCODE_LIMIT = 0x2000; // limit data rate to real-time
public static final int BASS_ENCODE_AIFF = 0x4000; // send an AIFF header rather than WAV
public static final int BASS_ENCODE_DITHER = 0x8000; // apply dither when converting floating-point sample data to integer
public static final int BASS_ENCODE_AUTOFREE = 0x40000; // free the encoder when the channel is freed
// BASS_Encode_GetCount counts
public static final int BASS_ENCODE_COUNT_IN = 0; // sent to encoder
public static final int BASS_ENCODE_COUNT_OUT = 1; // received from encoder
public static final int BASS_ENCODE_COUNT_CAST = 2; // sent to cast server
public static final int BASS_ENCODE_COUNT_QUEUE = 3; // queued
public static final int BASS_ENCODE_COUNT_QUEUE_LIMIT = 4; // queue limit
public static final int BASS_ENCODE_COUNT_QUEUE_FAIL = 5; // failed to queue
// BASS_Encode_CastInit content MIME types
public static final String BASS_ENCODE_TYPE_MP3 = "audio/mpeg";
public static final String BASS_ENCODE_TYPE_OGG = "audio/ogg";
public static final String BASS_ENCODE_TYPE_AAC = "audio/aacp";
// BASS_Encode_CastGetStats types
public static final int BASS_ENCODE_STATS_SHOUT = 0; // Shoutcast stats
public static final int BASS_ENCODE_STATS_ICE = 1; // Icecast mount-point stats
public static final int BASS_ENCODE_STATS_ICESERV = 2; // Icecast server stats
public interface ENCODEPROC extends Callback
{
void ENCODEPROC(int handle, int channel, Pointer buffer, int length, Pointer user);
/* Encoding callback function.
handle : The encoder
channel: The channel handle
buffer : Buffer containing the encoded data
length : Number of bytes
user : The 'user' parameter value given when starting the encoder */
}
public interface ENCODEPROCEX extends Callback
{
void ENCODEPROCEX(int handle, int channel, Pointer buffer, int length, long offset, Pointer user);
/* Encoding callback function.
handle : The encoder
channel: The channel handle
buffer : Buffer containing the encoded data
length : Number of bytes
offset : File offset of the data
user : The 'user' parameter value given when starting the encoder */
}
public interface ENCODERPROC extends Callback
{
int ENCODERPROC(int handle, int channel, Pointer buffer, int length, int maxout, Pointer user);
/* Encoder callback function.
handle : The encoder
channel: The channel handle
buffer : Buffer containing the PCM data (input) and receiving the encoded data (output)
length : Number of bytes in (-1=closing)
maxout : Maximum number of bytes out
user : The 'user' parameter value given when calling BASS_Encode_StartUser
RETURN : The amount of encoded data (-1=stop) */
}
public interface ENCODECLIENTPROC extends Callback
{
boolean ENCODECLIENTPROC(int handle, boolean connect, String client, StringBuffer headers, Pointer user);
/* Client connection notification callback function.
handle : The encoder
connect: true/false=client is connecting/disconnecting
client : The client's address (xxx.xxx.xxx.xxx:port)
headers: Request headers (optionally response headers on return)
user : The 'user' parameter value given when calling BASS_Encode_ServerInit
RETURN : true/false=accept/reject connection (ignored if connect=false) */
}
public interface ENCODENOTIFYPROC extends Callback
{
void ENCODENOTIFYPROC(int handle, int status, Pointer user);
/* Encoder death notification callback function.
handle : The encoder
status : Notification (BASS_ENCODE_NOTIFY_xxx)
user : The 'user' parameter value given when calling BASS_Encode_SetNotify */
}
// Encoder notifications
public static final int BASS_ENCODE_NOTIFY_ENCODER = 1; // encoder died
public static final int BASS_ENCODE_NOTIFY_CAST = 2; // cast server connection died
public static final int BASS_ENCODE_NOTIFY_CAST_TIMEOUT = 0x10000; // cast timeout
public static final int BASS_ENCODE_NOTIFY_QUEUE_FULL = 0x10001; // queue is out of space
public static final int BASS_ENCODE_NOTIFY_FREE = 0x10002; // encoder has been freed
// BASS_Encode_ServerInit flags
public static final int BASS_ENCODE_SERVER_NOHTTP = 1; // no HTTP headers
public static final int BASS_ENCODE_SERVER_META = 2; // Shoutcast metadata
public static native int BASS_Encode_GetVersion();
public static native int BASS_Encode_Start(int handle, String cmdline, int flags, ENCODEPROC proc, Pointer user);
public static native int BASS_Encode_StartLimit(int handle, String cmdline, int flags, ENCODEPROC proc, Pointer user, int limit);
public static native int BASS_Encode_StartUser(int handle, String file, int flags, ENCODERPROC proc, Pointer user);
public static native boolean BASS_Encode_AddChunk(int handle, String id, Pointer buffer, int length);
public static native int BASS_Encode_IsActive(int handle);
public static native boolean BASS_Encode_Stop(int handle);
public static native boolean BASS_Encode_StopEx(int handle, boolean queue);
public static native boolean BASS_Encode_SetPaused(int handle, boolean paused);
public static native boolean BASS_Encode_Write(int handle, Pointer buffer, int length);
public static native boolean BASS_Encode_SetNotify(int handle, ENCODENOTIFYPROC proc, Pointer user);
public static native long BASS_Encode_GetCount(int handle, int count);
public static native boolean BASS_Encode_SetChannel(int handle, int channel);
public static native int BASS_Encode_GetChannel(int handle);
//public static native boolean BASS_Encode_UserOutput(int handle, long offset, Pointer buffer, int length);
public static native boolean BASS_Encode_CastInit(int handle, String server, String pass, String content, String name, String url, String genre, String desc, String headers, int bitrate, boolean pub);
public static native boolean BASS_Encode_CastSetTitle(int handle, String title, String url);
public static native boolean BASS_Encode_CastSendMeta(int handle, int type, Pointer data, int length);
public static native String BASS_Encode_CastGetStats(int handle, int type, String pass);
public static native int BASS_Encode_ServerInit(int handle, String port, int buffer, int burst, int flags, ENCODECLIENTPROC proc, Pointer user);
public static native boolean BASS_Encode_ServerKick(int handle, String client);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassenc);
Native.register(BASSenc.class, path);
}
}

View File

@@ -0,0 +1,26 @@
/*
BASSenc_FLAC 2.4 Java class
Copyright (c) 2017-2018 Un4seen Developments Ltd.
See the BASSENC_FLAC.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSenc_FLAC
{
public static native int BASS_Encode_FLAC_GetVersion();
public static native int BASS_Encode_FLAC_Start(int handle, String options, int flags, BASSenc.ENCODEPROCEX proc, Pointer user);
public static native int BASS_Encode_FLAC_StartFile(int handle, String options, int flags, String filename);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassenc_flac);
if (path!=null && path.length()>0) Native.register(BASSenc_FLAC.class, path); }
}

View File

@@ -0,0 +1,29 @@
/*
BASSenc_MP3 2.4 Java class
Copyright (c) 2018 Un4seen Developments Ltd.
See the BASSENC_MP3.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSenc_MP3
{
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassenc_mp3);
Native.register(BASSenc_MP3.class, path);
}
public static native int BASS_Encode_MP3_GetVersion();
public static native int BASS_Encode_MP3_Start(int handle, String options, int flags, BASSenc.ENCODEPROCEX proc, Pointer user);
public static native int BASS_Encode_MP3_StartFile(int handle, String options, int flags, String filename);
}

View File

@@ -0,0 +1,28 @@
/*
BASSenc_OGG 2.4 Java class
Copyright (c) 2016 Un4seen Developments Ltd.
See the BASSENC_OGG.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSenc_OGG
{
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassenc_ogg);
if (path!=null && path.length()>0) Native.register(BASSenc_OGG.class, path);
}
public native static int BASS_Encode_OGG_GetVersion();
public static native int BASS_Encode_OGG_Start(int handle, String options, int flags, BASSenc.ENCODEPROC proc, Pointer user);
public static native int BASS_Encode_OGG_StartFile(int handle, String options, int flags, String filename);
}

View File

@@ -0,0 +1,26 @@
/*
BASSenc_OPUS 2.4 Java class
Copyright (c) 2016 Un4seen Developments Ltd.
See the BASSENC_OPUS.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class BASSenc_OPUS
{
public static native int BASS_Encode_OPUS_GetVersion();
public static native int BASS_Encode_OPUS_Start(int handle, String options, int flags, BASSenc.ENCODEPROC proc, Pointer user);
public static native int BASS_Encode_OPUS_StartFile(int handle, String options, int flags, String filename);
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassenc_opus);
if (path!=null && path.length()>0) Native.register(BASSenc_OPUS.class, path); }
}

View File

@@ -0,0 +1,152 @@
/*
BASSmix 2.4 Java class
Copyright (c) 2005-2017 Un4seen Developments Ltd.
See the BASSMIX.CHM file for more detailed documentation
*/
package com.un4seen.bass;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.FloatByReference;
import anywheresoftware.b4a.BA;
public class BASSmix
{
static {
String path = BassLibrary.ExtractLibraryFromJar(BassLibrary.lib_bassmix);
Native.register(BASSmix.class, path);
}
// additional BASS_SetConfig option
public static final int BASS_CONFIG_MIXER_BUFFER = 0x10601;
public static final int BASS_CONFIG_MIXER_POSEX = 0x10602;
public static final int BASS_CONFIG_SPLIT_BUFFER = 0x10610;
// BASS_Mixer_StreamCreate flags
public static final int BASS_MIXER_RESUME = 0x1000; // resume stalled immediately upon new/unpaused source
public static final int BASS_MIXER_POSEX = 0x2000; // enable BASS_Mixer_ChannelGetPositionEx support
public static final int BASS_MIXER_NOSPEAKER = 0x4000; // ignore speaker arrangement
public static final int BASS_MIXER_QUEUE = 0x8000; // queue sources
public static final int BASS_MIXER_END = 0x10000; // end the stream when there are no sources
public static final int BASS_MIXER_NONSTOP = 0x20000; // don't stall when there are no sources
// BASS_Mixer_StreamAddChannel/Ex flags
public static final int BASS_MIXER_CHAN_ABSOLUTE = 0x1000; // start is an absolute position
public static final int BASS_MIXER_CHAN_BUFFER = 0x2000; // buffer data for BASS_Mixer_ChannelGetData/Level
public static final int BASS_MIXER_CHAN_LIMIT = 0x4000; // limit mixer processing to the amount available from this source
public static final int BASS_MIXER_CHAN_MATRIX = 0x10000; // matrix mixing
public static final int BASS_MIXER_CHAN_PAUSE = 0x20000; // don't process the source
public static final int BASS_MIXER_CHAN_DOWNMIX = 0x400000; // downmix to stereo/mono
public static final int BASS_MIXER_CHAN_NORAMPIN = 0x800000; // don't ramp-in the start
public static final int BASS_MIXER_BUFFER = BASS_MIXER_CHAN_BUFFER;
public static final int BASS_MIXER_LIMIT = BASS_MIXER_CHAN_LIMIT;
public static final int BASS_MIXER_MATRIX = BASS_MIXER_CHAN_MATRIX;
public static final int BASS_MIXER_PAUSE = BASS_MIXER_CHAN_PAUSE;
public static final int BASS_MIXER_DOWNMIX = BASS_MIXER_CHAN_DOWNMIX;
public static final int BASS_MIXER_NORAMPIN = BASS_MIXER_CHAN_NORAMPIN;
// Mixer attributes
public static final int BASS_ATTRIB_MIXER_LATENCY = 0x15000;
public static final int BASS_ATTRIB_MIXER_THREADS = 0x15001;
public static final int BASS_ATTRIB_MIXER_VOL = 0x15002;
// Additional BASS_Mixer_ChannelIsActive return values
public static final int BASS_ACTIVE_WAITING = 5;
public static final int BASS_ACTIVE_QUEUED = 6;
// splitter flags
public static final int BASS_SPLIT_SLAVE = 0x1000; // only read buffered data
public static final int BASS_SPLIT_POS = 0x2000;
// splitter attributes
public static final int BASS_ATTRIB_SPLIT_ASYNCBUFFER = 0x15010;
public static final int BASS_ATTRIB_SPLIT_ASYNCPERIOD = 0x15011;
// envelope node
@BA.ShortName("BASS_MIXER_NODE")
// @Structure.FieldOrder({"pos","value"})
public static class BASS_MIXER_NODE extends Structure{
public BASS_MIXER_NODE() {}
public BASS_MIXER_NODE(long _pos, float _value) { pos=_pos; value=_value; }
public long pos;
public float value;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("pos","value");
}
@BA.Hide public void autoRead(){};
@BA.Hide public void autoWrite(){};
}
// envelope types
public static final int BASS_MIXER_ENV_FREQ = 1;
public static final int BASS_MIXER_ENV_VOL = 2;
public static final int BASS_MIXER_ENV_PAN = 3;
public static final int BASS_MIXER_ENV_LOOP = 0x10000; // flag: loop
public static final int BASS_MIXER_ENV_REMOVE = 0x20000; // flag: remove at end
// additional sync type
public static final int BASS_SYNC_MIXER_ENVELOPE = 0x10200;
public static final int BASS_SYNC_MIXER_ENVELOPE_NODE = 0x10201;
public static final int BASS_SYNC_MIXER_QUEUE = 0x10202;
// additional BASS_Mixer_ChannelSetPosition flag
public static final int BASS_POS_MIXER_RESET = 0x10000; // flag: clear mixer's playback buffer
// Additional BASS_Mixer_ChannelGetPosition mode
public static final int BASS_POS_MIXER_DELAY = 5;
// BASS_CHANNELINFO type
public static final int BASS_CTYPE_STREAM_MIXER = 0x10800;
public static final int BASS_CTYPE_STREAM_SPLIT = 0x10801;
public static native int BASS_Mixer_GetVersion();
public static native int BASS_Mixer_StreamCreate(int freq, int chans, int flags);
public static native boolean BASS_Mixer_StreamAddChannel(int handle, int channel, int flags);
public static native boolean BASS_Mixer_StreamAddChannelEx(int handle, int channel, int flags, long start, long length);
public static native int BASS_Mixer_StreamGetChannels(int handle, int[] channels, int count);
public static native int BASS_Mixer_ChannelGetMixer(int handle);
public static native int BASS_Mixer_ChannelIsActive(int handle);
public static native int BASS_Mixer_ChannelFlags(int handle, int flags, int mask);
public static native boolean BASS_Mixer_ChannelRemove(int handle);
public static native boolean BASS_Mixer_ChannelSetPosition(int handle, long pos, int mode);
public static native long BASS_Mixer_ChannelGetPosition(int handle, int mode);
public static native long BASS_Mixer_ChannelGetPositionEx(int channel, int mode, int delay);
public static native int BASS_Mixer_ChannelGetLevel(int handle);
public static native boolean BASS_Mixer_ChannelGetLevelEx(int handle, float[] levels, float length, int flags);
public static native int BASS_Mixer_ChannelGetData(int handle, Pointer buffer, int length);
public static native int BASS_Mixer_ChannelSetSync(int handle, int type, long param, BASS.SYNCPROC proc, Pointer user);
public static native boolean BASS_Mixer_ChannelRemoveSync(int channel, int sync);
//public static native boolean BASS_Mixer_ChannelSetMatrix(int handle, float[][] matrix);
public static native boolean BASS_Mixer_ChannelSetMatrix(int handle, float[] matrix);
//public static native boolean BASS_Mixer_ChannelSetMatrixEx(int handle, float[][] matrix, float time);
public static native boolean BASS_Mixer_ChannelSetMatrixEx(int handle, float[] matrix, float time);
//public static native boolean BASS_Mixer_ChannelGetMatrix(int handle, float[][] matrix);
public static native boolean BASS_Mixer_ChannelGetMatrix(int handle, float[] matrix);
//public static native boolean BASS_Mixer_ChannelSetEnvelope(int handle, int type, BASS_MIXER_NODE[] nodes, int count);
public static native boolean BASS_Mixer_ChannelSetEnvelope(int handle, int type, Structure nodes, int count);
public static native boolean BASS_Mixer_ChannelSetEnvelopePos(int handle, int type, long pos);
public static native long BASS_Mixer_ChannelGetEnvelopePos(int handle, int type, FloatByReference value);
public static native int BASS_Split_StreamCreate(int channel, int flags, int[] chanmap);
public static native int BASS_Split_StreamGetSource(int handle);
public static native int BASS_Split_StreamGetSplits(int handle, int[] splits, int count);
public static native boolean BASS_Split_StreamReset(int handle);
public static native boolean BASS_Split_StreamResetEx(int handle, int offset);
public static native int BASS_Split_StreamGetAvailable(int handle);
}

View File

@@ -0,0 +1,320 @@
package com.un4seen.bass;
import java.io.File;
import java.io.IOException;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import android.os.Build;
import anywheresoftware.b4a.BA;
/**
* BASS Library copier
* @author rdkartono
*
*/
public class BassLibrary {
public static final String lib_bass = "bass";
public static final String lib_bass_ac3 = "bass_ac3";
public static final String lib_bass_ape = "bass_ape";
public static final String lib_bass_fx = "bass_fx";
public static final String lib_bass_mpc = "bass_mpc";
public static final String lib_bass_spx = "bass_spx";
public static final String lib_bass_tta = "bass_tta";
public static final String lib_basscd = "basscd";
public static final String lib_bassdsd = "bassdsd";
public static final String lib_bassenc_flac = "bassenc_flac";
public static final String lib_bassenc_mp3 = "bassenc_mp3";
public static final String lib_bassenc_ogg = "bassenc_ogg";
public static final String lib_bassenc_opus = "bassenc_opus";
public static final String lib_bassenc = "bassenc";
public static final String lib_bassflac = "bassflac";
public static final String lib_basshls = "basshls";
public static final String lib_bassmidi = "bassmidi";
public static final String lib_bassmix = "bassmix";
public static final String lib_bassopus = "bassopus";
public static final String lib_basswebm = "basswebm";
public static final String lib_basswv = "basswv";
public static final String lib_tags = "tags";
public static final String lib_bass_aac = "bass_aac";
public static final String lib_bassalac = "bassalac";
public static String userdir = ""; // user directory
public static String homedir = ""; // home directory
public static String rundir = ""; // run directory
private static String osname = ""; // OS name
private static String osarch = ""; // OS architecture
private static String osversion = ""; // OS version
private static String javaLibPath = ""; // Java library path
private static String jvm = ""; // java virtual machine
private static String nativepath = ""; // library native path
private static String pathseparator = ""; // path separator untuk library
private static String jnalibpath = "";
private static void setJNALibPath(String value) {
if (value != null) {
if (!value.isEmpty()) {
if (!value.equals(jnalibpath)) {
String prev = System.setProperty("jna.library.path", value);
jnalibpath = value;
if (prev instanceof String) {
BA.Log("jna.library.path changed from "+prev+" to "+value);
} else {
BA.Log("New value for jna.library.path to "+value);
}
}
}
}
}
private static String getJNALibPath() {
if (jnalibpath.isEmpty()) {
jnalibpath = System.getProperty("jna.library.path");
}
return jnalibpath;
}
private static String getJavaLibPath() {
if (javaLibPath.isEmpty()) {
javaLibPath = System.getProperty("java.library.path");
}
return javaLibPath;
}
@SuppressWarnings("unused")
private static void DetectOS() {
System.setProperty("jna.debug_load", "true");
osname = System.getProperty("os.name");
osarch = System.getProperty("os.arch");
osversion = System.getProperty("os.version");
jvm = System.getProperty("java.vm.name");
homedir = System.getProperty("user.home");
userdir = System.getProperty("user.dir");
rundir = new File("").getAbsolutePath();
pathseparator = System.getProperty("path.separator");
javaLibPath = getJavaLibPath();
jnalibpath = getJNALibPath();
if (jnalibpath==null)
setJNALibPath(rundir);
else if (jnalibpath.isEmpty())
setJNALibPath(rundir);
BA.Log("OS Name : "+osname);
BA.Log("OS Architecture : "+osarch);
BA.Log("OS Version : "+osversion);
BA.Log("JVM Name : "+jvm);
BA.Log("Java Lib path : "+javaLibPath);
BA.Log("Path Separator : ["+pathseparator+"]");
BA.Log("Run Directory : "+rundir);
BA.Log("User Home : "+homedir);
BA.Log("User Directory : "+userdir);
BA.Log("JNA Library Path : "+jnalibpath);
if (osname.toLowerCase().contains("window")) {
if (osarch.toLowerCase().contains("86")) {
BA.Log("Windows 32bit");
nativepath="/lib/win32/";
} else if (osarch.toLowerCase().contains("64")) {
BA.Log("Windows 64bit");
nativepath = "/lib/win64/";
}
} else if (osname.toLowerCase().contains("linux")) {
if (jvm.toLowerCase().contains("dalvik")) {
String[] abi_list = Build.SUPPORTED_ABIS;
if (abi_list == null) {
BA.Log("Android ABI is null");
} else if (abi_list.length==0) {
BA.Log("ABI list is zero");
} else {
String abi = abi_list[0].toLowerCase();
BA.Log("Android ABI="+abi);
if (abi.contains("arm64")) {
nativepath="/lib/arm64-v8a/";
BA.Log("Android ARM64");
} else if (abi.contains("armeabi-v7a")) {
nativepath="/lib/armeabi-v7a/";
BA.Log("Android ARMEABI-V7A");
} else if (abi.contains("x86")){
nativepath="/lib/x86/";
BA.Log("Android X86");
} else if (abi.contains("x86_64")) {
nativepath="/lib/x86_64/";
BA.Log("Android X86_64");
}
else {
BA.Log("Unsupported ABI");
}
}
} else {
if (osarch.toLowerCase().contains("arm")) {
BA.Log("Raspberry");
nativepath = "/lib/raspberry/";
} else if (osarch.toLowerCase().contains("aarch64")) {
BA.Log("ARM 64bit");
nativepath = "/lib/aarch64/";
} else if (osarch.toLowerCase().contains("86")) {
BA.Log("Linux 32bit");
nativepath = "/lib/linux32/";
} else if (osarch.toLowerCase().contains("64")) {
BA.Log("Linux 64bit");
nativepath = "/lib/linux64/";
} else {
BA.Log("Unsuppported Linux");
}
}
} else {
BA.Log("Unsupported OS");
}
}
/**
* Check if BassLibrary is Initialized or not
* @return true if initalized
*/
private static boolean IsInitialized() {
if (nativepath.length()>0) {
if (rundir.length()>0) {
return true;
}
}
return false;
}
/**
* Extract selected library to App Directory
* @param libname : select from predefined values
* @return true if success
*/
private static String Extract_Library(String libname) {
if (IsInitialized()) {
return copylib(nativepath, System.mapLibraryName(libname), rundir);
}
return "";
}
public static boolean Valid_String(String str) {
if (str!=null) {
if (str.length()>0) {
return true;
}
}
return false;
}
private static String copylib(String sourcepath, String libname, String targetpath){
if (Valid_String(sourcepath)) {
if (Valid_String(libname)) {
if (Valid_String(targetpath)) {
File targetcheck = new File(targetpath);
if (targetcheck.exists()) {
if (targetcheck.isDirectory()) {
if (targetcheck.canWrite()) {
File fsource = null;
String filetoextract = sourcepath+libname;
try {
fsource = Native.extractFromResourcePath(filetoextract);
File ftarget = new File(targetpath, libname);
if (ftarget.exists()) {
if (ftarget.length()==fsource.length()) {
if (!fsource.delete()) {BA.Log("Unable to delete "+fsource.getAbsolutePath());}
BA.Log(libname+" already exists and same in "+targetpath);
return ftarget.getAbsolutePath();
} else {
if (fsource.renameTo(ftarget)) {
BA.Log("Copied "+libname+" to "+targetpath);
return ftarget.getAbsolutePath();
} else {
BA.Log("Failed to copy "+libname+" to "+targetpath);
}
}
} else {
if (fsource.renameTo(ftarget)) {
BA.Log("Copied "+libname+" to "+targetpath);
return ftarget.getAbsolutePath();
} else {
BA.Log("Failed to copy "+libname+" to "+targetpath);
}
}
} catch (IOException e1) {
BA.Log("Unable to extractFromResourcePath "+filetoextract);
}
} else BA.Log("copylib failed, targetpath="+targetpath+" is not writable");
} else BA.Log("copylib failed, targetpath="+targetpath+" is not folder");
} else BA.Log("copylib failed, targetpath="+targetpath+" is not exists");
} else BA.Log("copylib failed, targetpath is invalid");
} else BA.Log("copylib failed, libname is invalid");
} else BA.Log("copylib failed, sourcepath is invalid");
return "";
};
@SuppressWarnings("rawtypes")
public static boolean LoadLibraryFromJar(Class c, String libname) {
if (Platform.isAndroid()) {
try {
System.loadLibrary(libname);
return true;
} catch(Exception e) {
System.out.println("LoadLibraryFromJar isAndroid failed on library="+libname);
return false;
}
} else {
// if (!IsInitialized()) DetectOS();
// Native.register(c, libname);
// return true;
String libpath = Extract_Library(libname);
if (Valid_String(libpath)) {
Native.register(c, libname);
return true;
} else {
System.out.println("Extract_Library failed on library="+libname);
return false;
}
}
}
public static String ExtractLibraryFromJar(String libname) {
String currentdir = new File("").getAbsolutePath();
String fulllibname = new File(currentdir, System.mapLibraryName(libname)).getAbsolutePath();
//System.out.println("Checking if "+fulllibname+" exists");
File libfile = new File(fulllibname);
if (libfile.exists() && libfile.length()>0) {
System.out.println(fulllibname+" already existed");
return fulllibname;
} else {
try {
File ff = Native.extractFromResourcePath(libname);
if (ff.exists() && ff.length()>0) {
if (ff.renameTo(libfile)) {
System.out.println("created "+fulllibname);
return fulllibname;
} else System.out.println("Unable to create "+fulllibname);
} else System.out.println("extractFromResourcePath produce invalid File");
} catch (IOException e) {
System.out.println("ExtractLibraryFromJar exception on libname="+libname+", message = "+e.getMessage());
}
return null;
}
}
}

View File

@@ -0,0 +1,18 @@
package internetradio;
import anywheresoftware.b4a.BA;
@BA.ShortName("radioURL")
public class RadioURLs {
public static final String radioparadise_uk = "http://stream-uk3.radioparadise.com:80/mp3-128";
public static final String JakFM = "http://streaming.sportku.com:8000/jak";
public static final String GenFM = "http://streaming.sportku.com:8000/genfm";
public static final String HardRockFM = "http://streaming.mramedia.com:8000/hardrock";
public static final String ElshintaFM = "http://202.137.4.147:8000/";
public static final String CosmopolitanFM = "http://streaming.mramedia.com:8000/cosmo-aac";
public static final String SonoraFM = "http://103.226.246.245/kompas-sonorajakarta";
public static final String SmartFM = "http://103.226.246.245/kompas-smartjakarta";
public static final String HeartLineFM = "http://202.129.186.43:7010/";
}

View File

@@ -0,0 +1,358 @@
package jbass;
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASS.bassconstant;
import com.un4seen.bass.BASSmix;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.Bit;
import anywheresoftware.b4a.objects.collections.List;
@BA.ShortName("AudioCombiner")
@BA.Events(values={
"log(msg as string)",
"combinewav(success as boolean, destination as string)",
"combinemp3(success as boolean, destination as string)"
})
public class AudioCombiner {
private Object caller;
private String event;
private BA ba;
private boolean inited = false;
private boolean need_log_event = false;
private boolean need_combinewav_event = false;
private boolean need_combinemp3_event = false;
private BASS bass;
private BASSmix bassmix;
private ExecutorService executor = Executors.newFixedThreadPool(10);
public void Initialize(BA bax, Object callerobject, String eventname) {
ba = bax;
caller = callerobject;
event = eventname;
if (ba!=null) {
if (caller!=null) {
if (!event.isEmpty()) {
need_log_event = ba.subExists(event+"_log");
need_combinewav_event = ba.subExists(event+"_combinewav");
need_combinemp3_event = ba.subExists(event+"_combinemp3");
}
}
}
bass = new BASS();
bassmix = new BASSmix();
if (bass.BASS_GetVersion()!=0) {
if (bassmix.BASS_Mixer_GetVersion()!=0) {
inited = true;
} else raise_log("Failed to Load BassMix");
} else raise_log("Failed to Load Bass");
}
public boolean IsInitialized() {
return inited;
}
private boolean run_basic_checks(final List source, String destination) {
if (inited) {
if (source!=null) {
if (source.IsInitialized()) {
if (source.getSize()>0) {
if (!destination.isEmpty()) {
File fdest = new File(destination);
if (!fdest.isDirectory()) {
// cek satu per satu
for(int ii=0;ii<source.getSize();ii++) {
Object xx = source.Get(ii);
if ((xx instanceof String)==false) {
raise_log("Member of Source List is not String Path");
return false;
}
File xxx = new File((String) xx);
if (!xxx.exists()) {
raise_log("Member of Source List is not valid Path / not exist");
return false;
}
}
return true;
} else {
raise_log("Destination is directory path");
}
} else {
raise_log("Destination Path is empty");
}
} else {
raise_log("Source List is empty");
}
} else {
raise_log("Source List not initialized");
}
} else {
raise_log("Source List is null");
}
} else {
raise_log("BASS not inited");
}
return false;
}
/**
* Combine paths inside source to destination , in WAV format
* will raise event combinewav(success as boolean, destination as string)
* @param source : list of valid file path
* @param destination : valid full path of target WAV file
*/
public void Combine_to_Wav(final List source, String destination, int target_samplingrate, int target_channel) {
if (run_basic_checks(source, destination)) {
// sampe sini dah cek semua, tinggal execute
executor.execute(new WavCombineWork(source, destination, target_samplingrate, target_channel));
} else {
raise_combinewav(false, destination);
}
}
/**
* Combine paths inside source to destination , in MP3 format
* will raise event combinemp3(success as boolean, destination as string)
* @param source : list of valid file path
* @param destination : valid full path of target MP3 file
*/
public void Combine_to_MP3(final List source, String destination, int target_samplingrate, int target_channel) {
if (run_basic_checks(source, destination)) {
// sampe sini dah cek semua, tinggal execute
executor.execute(new Mp3CombineWork(source, destination, target_samplingrate, target_channel));
} else {
raise_combinemp3(false, destination);
}
}
private abstract class CombineBasic implements Runnable{
protected List source;
protected String destination;
protected int destinationhandle = 0;
protected final int readsize = 16000;
protected final int samplingrate;
protected final int channel;
protected final int mode;
private int mixerhandle = 0;
/**
* Create CombineBasic
* @param source : List of valid paths
* @param destination : target path
* @param samplingrate : sampling rate
* @param channel : 1 = mono, 2 = stereo
* @param mode : 0 = wav, 1 = mp3
*/
CombineBasic(final List source, final String destination, int samplingrate, int channel, int mode){
this.source = source;
this.destination = destination;
this.samplingrate = samplingrate;
this.channel = channel;
this.mode = mode;
}
@Override
public void run() {
if (bass_init_dev0()) {
if (Create_Mixer()) {
if (Create_HandleWriter(mixerhandle)) {
List sourcehandles = new List();
if (Open_Sources(sourcehandles)) {
// sampe sini semua bisa diopen dengan normal
final int mixerflag = Bit.Or(bassconstant.BASS_STREAM_AUTOFREE, bassmix.BASS_MIXER_CHAN_NORAMPIN);
for(int ii=0;ii<sourcehandles.getSize();ii++) {
int hh = (int)sourcehandles.Get(ii);
String namafile = (String) source.Get(ii);
if (bassmix.BASS_Mixer_StreamAddChannel(mixerhandle, hh, mixerflag)) {
int readcount = 0;
do {
Pointer buffer = new Memory(readsize);
readcount = bass.BASS_ChannelGetData(mixerhandle, buffer, readsize);
} while (readcount>0);
} else raise_log("Failed Mixer_StreamAddChannel file="+namafile+", Msg:"+bass.GetBassErrorString());
}
}
// tutup mixer, dah kelar
bass.BASS_StreamFree(mixerhandle);
raise_combineresult(true);
return;
}
}
}
raise_combineresult(false);
}
private boolean Open_Sources(List sourcehandles) {
sourcehandles.Initialize();
for (int ii = 0; ii<source.getSize();ii++) {
String filename = (String)source.Get(ii);
int handle = bass.BASS_StreamCreateFile(false, filename, 0, 0, bassconstant.BASS_STREAM_DECODE);
if (handle==0) {
// gagal buka
raise_log("Unable to Open "+filename+", Msg:"+bass.GetBassErrorString());
return false;
}
// sampe sini bisa open
sourcehandles.Add(handle);
}
// sampe sini berarti semua file bisa open
return true;
}
private boolean Create_Mixer() {
final int mixerflag = bassconstant.BASS_STREAM_DECODE;
mixerhandle = bassmix.BASS_Mixer_StreamCreate(samplingrate, channel, mixerflag);
if (mixerhandle!=0) {
return true;
} else {
raise_log("CreateMixer failed, Msg:"+bass.GetBassErrorString());
return false;
}
}
// cuma beda di sini aja
protected abstract boolean Create_HandleWriter();
protected abstract boolean Create_HandleWriter(int fromhandle);
private boolean bass_init_dev0() {
if (bass.BASS_SetDevice(0)) {
// bisa setdevice, berarti sudah init
return true;
} else {
int err = bass.BASS_ErrorGetCode();
if (err == bassconstant.BASS_ERROR_DEVICE) {
raise_log("Unable to SetDevice 0");
return false;
}
if (bass.BASS_Init(0, samplingrate, 0)) {
return true;
} else {
raise_log("Unable to Init Device 0");
return false;
}
}
}
private void raise_combineresult(boolean value) {
switch(mode) {
case 1 :
raise_combinemp3(value, destination);
break;
default :
raise_combinewav(value, destination);
break;
}
}
}
private class WavCombineWork extends CombineBasic{
private HandleWavWriter hww;
WavCombineWork(List source, String destination, int samplingrate, int channel) {
super(source, destination, samplingrate, channel,0);
}
@Override
protected boolean Create_HandleWriter() {
hww = new HandleWavWriter();
hww.Initialize(ba, event);
if (hww.CreateFile_from_Handle(destination,destinationhandle)) {
return true;
}
else {
raise_log("Unable to Create_HandleWavWriter"); return false; }
}
@Override
protected boolean Create_HandleWriter(int fromhandle) {
hww = new HandleWavWriter();
hww.Initialize(ba, event);
if (hww.CreateFile_from_Handle(destination,fromhandle)) {
return true;
}
else {
raise_log("Unable to Create_HandleWavWriter"); return false;
}
}
}
private class Mp3CombineWork extends CombineBasic{
private HandleMP3Writer hww;
Mp3CombineWork(List source, String destination, int samplingrate, int channel) {
super(source, destination, samplingrate, channel,1);
}
@Override
protected boolean Create_HandleWriter() {
hww = new HandleMP3Writer();
hww.Initialize(ba, event);
if (hww.CreateFile_from_Handle(destination, destinationhandle)) {
return true;
} else {
return false;
}
}
@Override
protected boolean Create_HandleWriter(int fromhandle) {
hww = new HandleMP3Writer();
hww.Initialize(ba, event);
if (hww.CreateFile_from_Handle(destination, fromhandle)) {
return true;
} else {
return false;
}
}
}
private void raise_log(String value) {
if (need_log_event) ba.raiseEventFromDifferentThread(caller, null, 0, event+"_log", false, new Object[] {value});
}
private void raise_combinewav(boolean success, String destination) {
if (need_combinewav_event) ba.raiseEventFromDifferentThread(caller, null, 0, event+"_combinewav", false, new Object[] {success, destination});
}
private void raise_combinemp3(boolean success, String destination){
if (need_combinemp3_event) ba.raiseEventFromDifferentThread(caller, null, 0, event+"_combinemp3", false, new Object[] {success, destination});
}
}

View File

@@ -0,0 +1,234 @@
package jbass;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.DateTime;
@BA.ShortName("MessageInformation")
public class AudioFileInformation {
private String filename;
private long size;
private double lengthseconds;
private boolean valid;
private boolean found;
private boolean playing = false;
private long startplayingtick = 0;
private final long KB_threshold = 1024;
private final long MB_threshold = 1024 * 1024;
private final long GB_threshold = 1024 * 1024 * 1024;
/**
* Check if currently being played
* @return true if playing
*/
public boolean getIsPlaying() {
return playing;
}
/**
* Set playing status
* @param value : true if playing
*/
public void setIsPlaying(boolean value) {
playing = value;
if (playing) {
startplayingtick = DateTime.getNow();
} else {
startplayingtick = -1;
}
}
/**
* Get Playback duration since IsPlaying is set true
* @return seconds of playback
*/
public int getPlaybackDuration() {
if (startplayingtick==-1) {
return -1;
} else {
return (int)((DateTime.getNow() - startplayingtick)/DateTime.TicksPerSecond);
}
}
/**
* Get Playback duration since IsPlaying is set true
* @return duration of playback in String
*/
public String getPlaybackDurationInString() {
int xx = getPlaybackDuration();
if (xx<0) {
return "N/A";
} else if (xx<60) {
return xx+" sec";
} else if (xx<3600) {
int mm = xx / 60;
int ss = xx - (mm*60);
return mm+"min "+ss+"sec";
} else {
int hh = xx / 3600;
xx = xx - (hh*3600);
int mm = xx / 60;
int ss = xx - (mm*60);
return hh+"h "+mm+"m "+ss+"s";
}
}
/**
* Get File size in bytes
* @return file size in bytes
*/
public long getSizeInBytes() {
return size;
}
/**
* Set File Size in bytes
* @param value : file size in bytes
*/
public void setSizeInBytes(long value) {
size = value;
}
/**
* Get File Size in readable string
* @return file size in readable string
*/
public String getSizeinString() {
if (valid) {
if (size<KB_threshold) {
return size+" B";
} else if (size<MB_threshold) {
double vv = (double)size / KB_threshold;
vv = Math.round(vv * 10.0) / 10.0; // 1 desimal
return vv+" KB";
} else if (size<GB_threshold) {
double vv = (double)size / MB_threshold;
vv = Math.round(vv*10.0)/ 10.0; // 1 desimal
return vv+" MB";
} else {
double vv = (double)size/ GB_threshold;
vv = Math.round(vv*10.0) / 10.0; // 1 desimal
return vv+" GB";
}
} else return "N/A";
}
/**
* Get File length in seconds
* @return audio file duration in seconds
*/
public double getLengthInSeconds() {
return lengthseconds;
}
/**
* Set File length in seconds
* @param value : file duration in seconds
*/
public void setLengthInSeconds(double value) {
lengthseconds = value;
}
/**
* Get File length in seconds in readable string
* @return audio file duration in readable string
*/
public String getLengthinString() {
if (valid) {
int sec = (int)lengthseconds;
if (sec < 60) {
return sec+" sec";
} else if (sec < 3600) {
int mm = (int)sec / 60;
int ss = (int)sec - (mm*60);
return mm+" min "+ss+" sec";
} else {
int hh = sec / 3600;
sec = sec - (hh*3600);
int mm = sec / 60;
int ss = sec - (mm*60);
return hh+"h "+mm+"m "+ss+"s";
}
} else return "N/A";
}
/**
* Get Filepath assigned
* @return filename or N/A if not assigned
*/
public String getFilePath() {
if (valid) {
return filename;
} else return "N/A";
}
/**
* Set Filepath
* @param value : filepath assigned
*/
public void setFilePath(String value) {
filename = value;
}
/**
* Check if this file is a valid audio file and can be played
* @return true if valid audio file
*/
public boolean getIsValid() {
return valid;
}
/**
* Set audio file validity
* @param value : true if valid
*/
public void setIsValid(boolean value) {
valid = value;
}
/**
* Check if this file path is found
* @return true if found
*/
public boolean getIsFound() {
return found;
}
/**
* Set audio file found
* @param value
*/
public void setIsFound(boolean value) {
found = value;
}
/**
* Return summary in string
* @return PATH:[filepath or N/A];VALID:[YES or NO];SIZE:[size in bytes];LENGTH:[length in seconds]
*/
public String toString() {
StringBuilder str = new StringBuilder();
str.append("PATH:").append((filename.isEmpty())?"N/A":filename).append(";");
str.append("VALID:").append((valid) ? "YES":"NO").append(";");
str.append("SIZE:").append(getSizeinString()).append(";");
str.append("LENGTH:").append(getLengthinString());
return str.toString();
}
}

View File

@@ -0,0 +1,156 @@
package jbass;
import com.un4seen.bass.BASS.BASS_CHANNELINFO;
import com.un4seen.bass.BASS.bassconstant;
import anywheresoftware.b4a.BA;
@BA.ShortName("BASS_CHANNELINFO")
/**
* Pengganti BASS_CHANNELINFO
* @author rdkartono
*
*/
public class Bass_ChannelInfo {
/**
* Samplingrate for playback or recording
*/
public final int frequency;
/**
* Number of channel
* 1 = mono, 2 = stereo, etc
*/
public final int channels;
/**
* Flags
* BASS_SAMPLE_8BITS The channel's resolution is 8-bit. If neither this or the BASS_SAMPLE_FLOAT flags are present, then the channel's resolution is 16-bit.
* BASS_SAMPLE_FLOAT The channel's resolution is 32-bit floating-point.
* BASS_SAMPLE_LOOP The channel is looped.
* BASS_SAMPLE_3D The channel has 3D functionality enabled.
* BASS_SAMPLE_SOFTWARE The channel is not using hardware DirectSound mixing.
* BASS_SAMPLE_VAM The channel is using the DX7 voice allocation and management features. (HCHANNEL only)
* BASS_SAMPLE_MUTEMAX The channel is muted when at (or beyond) its max distance.
* BASS_SAMPLE_FX The channel has the "with FX flag" DX8 effect implementation enabled. (HSTREAM/HMUSIC)
* BASS_STREAM_RESTRATE The internet file download rate is restricted. (HSTREAM)
* BASS_STREAM_BLOCK The internet file (or "buffered" user file) is streamed in small blocks. (HSTREAM)
* BASS_STREAM_AUTOFREE The channel will automatically be freed when it ends. (HSTREAM/HMUSIC)
* BASS_STREAM_DECODE The channel is a "decoding channel". (HSTREAM/HMUSIC/HRECORD)
* BASS_MUSIC_RAMP The MOD music is using "normal" ramping. (HMUSIC)
* BASS_MUSIC_RAMPS The MOD music is using "sensitive" ramping. (HMUSIC)
* BASS_MUSIC_SURROUND The MOD music is using surround sound. (HMUSIC)
* BASS_MUSIC_SURROUND2 The MOD music is using surround sound mode 2. (HMUSIC)
* BASS_MUSIC_NONINTER The MOD music is using non-interpolated mixing. (HMUSIC)
* BASS_MUSIC_FT2MOD The MOD music is using FastTracker 2 .MOD playback. (HMUSIC)
* BASS_MUSIC_PT1MOD The MOD music is using ProTracker 1 .MOD playback. (HMUSIC)
* BASS_MUSIC_STOPBACK The MOD music will be stopped when a backward jump effect is played. (HMUSIC)
* BASS_SPEAKER_xxx Speaker assignment flags. (HSTREAM/HMUSIC)
* BASS_ASYNCFILE The file is read asynchronously. (HSTREAM)
* BASS_UNICODE filename is in UTF-16 form.
*/
public final int flags;
/**
* Channel Type
* BASS_CTYPE_SAMPLE Sample channel. (HCHANNEL)
* BASS_CTYPE_STREAM User sample stream. This can also be used as a flag to test if the channel is any kind of HSTREAM.
* BASS_CTYPE_STREAM_DUMMY "dummy" stream (using STREAMPROC_DUMMY).
* BASS_CTYPE_STREAM_DEVICE "dummy" stream for device final output mix (using STREAMPROC_DEVICE).
* BASS_CTYPE_STREAM_VORBIS Ogg Vorbis format stream.
* BASS_CTYPE_STREAM_MP1 MPEG layer 1 format stream.
* BASS_CTYPE_STREAM_MP2 MPEG layer 2 format stream.
* BASS_CTYPE_STREAM_MP3 MPEG layer 3 format stream.
* BASS_CTYPE_STREAM_AIFF Audio IFF format stream.
* BASS_CTYPE_STREAM_AM Android media codec stream. Additional format information is avaliable from BASS_ChannelGetTags (BASS_TAG_AM_MIME).
* BASS_CTYPE_STREAM_CA CoreAudio codec stream. Additional format information is avaliable from BASS_ChannelGetTags (BASS_TAG_CA_CODEC).
* BASS_CTYPE_STREAM_MF Media Foundation codec stream. Additional format information is avaliable from BASS_ChannelGetTags (BASS_TAG_WAVEFORMAT).
* BASS_CTYPE_STREAM_WAV_PCM Integer PCM WAVE format stream.
* BASS_CTYPE_STREAM_WAV_FLOAT Floating-point PCM WAVE format stream.
* BASS_CTYPE_STREAM_WAV WAVE format flag. This can be used to test if the channel is any kind of WAVE format. The codec (the file's "wFormatTag") is specified in the LOWORD. Additional information is also avaliable from BASS_ChannelGetTags (BASS_TAG_WAVEFORMAT).
* BASS_CTYPE_MUSIC_MOD Generic MOD format music. This can also be used as a flag to test if the channel is any kind of HMUSIC.
* BASS_CTYPE_MUSIC_MTM MultiTracker format music.
* BASS_CTYPE_MUSIC_S3M ScreamTracker 3 format music.
* BASS_CTYPE_MUSIC_XM FastTracker 2 format music.
* BASS_CTYPE_MUSIC_IT Impulse Tracker format music.
* BASS_CTYPE_MUSIC_MO3 MO3 format flag, used in combination with one of the BASS_CTYPE_MUSIC types.
* BASS_CTYPE_RECORD Recording channel. (HRECORD)
*/
public final int channeltype;
/**
* The original resolution (bits per sample)... 0 = undefined. If the original sample format is floating-point, then the BASS_ORIGRES_FLOAT flag will be set and the number of bits will be in the LOWORD.
*/
public final int originalresolution;
/**
* The plugin that is handling the channel... 0 = not using a plugin. Note this is only available with streams created using the plugin system via the standard BASS stream creation functions, not those created by add-on functions. Information on the plugin can be retrieved via BASS_PluginGetInfo.
*/
public final int pluginhandle;
/**
* The sample that is playing on the channel. (HCHANNEL only)
*/
public final int handlesample;
/**
* The filename associated with the channel. (HSTREAM only)
*/
public final String streamfilename;
public final boolean isvalid;
public Bass_ChannelInfo(BASS_CHANNELINFO chi) {
if (chi instanceof BASS_CHANNELINFO) {
chi.read();
this.frequency = chi.freq;
this.channels = chi.chans;
this.channeltype = chi.ctype;
this.flags = chi.flags;
this.handlesample = chi.sample;
this.originalresolution = chi.origres;
this.pluginhandle = chi.plugin;
this.streamfilename = chi.filename;
this.isvalid = true;
} else {
this.frequency = 0;
this.channels = 0;
this.channeltype = 0;
this.flags = 0;
this.handlesample = 0;
this.originalresolution = 0;
this.pluginhandle = 0;
this.streamfilename=null;
this.isvalid = false;
}
}
/**
* Check if this channel is recording channel
* @return true if recording channel
*/
public boolean IsRecordingChannel() {
if (isvalid) {
return (channeltype & bassconstant.BASS_CTYPE_RECORD)>0 ? true:false;
}
return false;
}
/**
* Check if this channel is stream channel
* @return true if stream channel
*/
public boolean IsFileStreamChannel() {
if (isvalid) {
if ((channeltype & bassconstant.BASS_CTYPE_STREAM)>0) {
if (streamfilename instanceof String) {
if (streamfilename.length()>0) {
return true;
}
}
}
}
return false;
}
}

View File

@@ -0,0 +1,135 @@
package jbass;
import com.un4seen.bass.BASS.BASS_DEVICEINFO;
import com.un4seen.bass.BASS.bassconstant;
import anywheresoftware.b4a.BA;
@BA.ShortName("BASS_DEVICEINFO")
/**
* Pengganti BASS_DEVICEINFO dari Bass
* @author rdkartono
*
*/
public class Bass_DeviceInfo {
public final int index;
public final String Name;
public final String Driver;
public final int Flags;
public final boolean isvalid;
public Bass_DeviceInfo() {
index = -1;
Name = "";
Driver = "";
Flags = 0;
isvalid = false;
}
public Bass_DeviceInfo(int index, BASS_DEVICEINFO source) {
this.index = index;
if (source instanceof BASS_DEVICEINFO) {
source.read();
if (source.name instanceof String) {
Name = source.name;
Driver = (source.driver instanceof String) ? source.driver : "";
Flags = source.flags;
isvalid = true;
return;
}
Name="";
Driver="";
Flags = 0;
isvalid = false;
} else {
Name = "";
Driver = "";
Flags = 0;
isvalid = false;
}
}
/**
* Get String Status
*/
public String toString() {
return "index="+index+", Name="+Name+", Driver="+Driver+", Inited="+IsInited()+", Enabled="+IsEnabled()+", DefaultDevice="+IsDefaultDevice();
}
/**
* Check if this device is already inited by BASS
* @return true if inited
*/
public boolean IsInited() {
return (Flags & bassconstant.BASS_DEVICE_INIT)>0 ? true : false;
}
/**
* Check if this device is enabled by OS
* @return true if enabled
*/
public boolean IsEnabled() {
return (Flags & bassconstant.BASS_DEVICE_ENABLED) > 0 ? true : false;
}
/**
* Check if this device is the default device by OS
* @return true if default device
*/
public boolean IsDefaultDevice() {
return (Flags & bassconstant.BASS_DEVICE_DEFAULT) > 0 ? true : false;
}
/**
* Check if this device is the system default communication device.
* @return true if default device
*/
public boolean IsDefaultCommunicationDevice() {
return (Flags & bassconstant.BASS_DEVICE_DEFAULTCOM) > 0 ? true : false;
}
/**
* Check if this device capable to do loopback recording
* it captures the sound from an output device.
* The corresponding output device can be identified by having the same driver value.
* @return true if capable
*/
public boolean IsLoopbackRecordingDevice() {
return (Flags & bassconstant.BASS_DEVICE_LOOPBACK) > 0 ? true : false;
}
/**
* Get Device Connection Type
*
* DIGITAL : An audio endpoint device that connects to an audio adapter through a connector for a digital interface of unknown type.
* DISPLAYPORT : An audio endpoint device that connects to an audio adapter through a DisplayPort connector.
* HANDSET : part of telephone handheld contains a speaker and a microphone for two-way communication.
* HDMI : An audio endpoint device that connects to an audio adapter through a High-Definition Multimedia Interface (HDMI) connector.
* HEADPHONES : A set of headphones.
* HEADSET : An earphone or a pair of earphones with an attached mouthpiece for two-way communication.
* LINE : Line input / Line Output
* MICROPHONE : microphone.
* NETWORK : An audio endpoint device that the user accesses remotely through a network.
* SPDIF : An audio endpoint device that connects to an audio adapter through a Sony/Philips Digital Interface (S/PDIF) connector.
* SPEAKERS : A set of speakers.
* @return String value or empty string if unknown
*/
public String Device_Connection_Type() {
int val = Flags & bassconstant.BASS_DEVICE_TYPE_MASK;
switch(val) {
case bassconstant.BASS_DEVICE_TYPE_DIGITAL : return "DIGITAL";
case bassconstant.BASS_DEVICE_TYPE_DISPLAYPORT : return "DISPLAYPORT";
case bassconstant.BASS_DEVICE_TYPE_HANDSET: return "HANDSET";
case bassconstant.BASS_DEVICE_TYPE_HDMI: return "HDMI";
case bassconstant.BASS_DEVICE_TYPE_HEADPHONES: return "HEADPHONES";
case bassconstant.BASS_DEVICE_TYPE_HEADSET: return "HEADSET";
case bassconstant.BASS_DEVICE_TYPE_LINE: return "LINE";
case bassconstant.BASS_DEVICE_TYPE_MICROPHONE: return "MICROPHONE";
case bassconstant.BASS_DEVICE_TYPE_NETWORK: return "NETWORK";
case bassconstant.BASS_DEVICE_TYPE_SPDIF: return "SPDIF";
case bassconstant.BASS_DEVICE_TYPE_SPEAKERS: return "SPEAKERS";
default : return "";
}
}
}

181
src/jbass/Bass_Info.java Normal file
View File

@@ -0,0 +1,181 @@
package jbass;
import com.un4seen.bass.BASS.BASS_INFO;
import com.un4seen.bass.BASS.bassconstant;
import anywheresoftware.b4a.BA;
@BA.ShortName("BASS_DEVICEINFO")
/**
* Pengganti BASS_INFO dari BASS
* http://www.un4seen.com/doc/#bass/BASS_INFO.html
* @author rdkartono
*
*/
public class Bass_Info {
public final int flags;
/**
* Total Hardware Memory Size
*/
public final int hwsize;
/**
* Free Hardware Memory Size
*/
public final int hwfree;
/**
* Free sample slots in hardware
*/
public final int freesam;
/**
* Free 3D sample slots in hardware
*/
public final int free3d;
/**
* minimum sample rate supported
*/
public final int minrate;
/**
* maximum sample rate supported
*/
public final int maxrate;
/**
* Support EAX ?
*/
public final boolean eax;
/**
* Minimum Buffer length, in miliseconds
*/
public final int minbuf;
/**
* DirectSound version
*/
public final int dsver;
/**
* Average delay for channel playback to start and be heard
*/
public final int latency;
/**
* Flags parameter of the BASS_Init call
*/
public final int initflags;
/**
* Number of available speakers
*/
public final int speakers;
/**
* Device's current output sample rate
*/
public final int freq;
/**
* If Bass_Info contains valid data
*/
public final boolean isvalid;
public Bass_Info(BASS_INFO inf) {
if (inf instanceof BASS_INFO) {
inf.read();
flags = inf.flags;
hwsize = inf.hwsize;
hwfree = inf.hwfree;
freesam = inf.freesam;
free3d = inf.free3d;
minrate = inf.minrate;
maxrate= inf.maxrate;
eax = inf.eax == 0 ? false : true;
minbuf = inf.minbuf;
dsver = inf.dsver;
latency = inf.latency;
initflags = inf.initflags;
speakers = inf.speakers;
freq = inf.freq;
isvalid = true;
} else {
flags = 0;
hwsize = 0;
hwfree = 0;
freesam = 0;
free3d = 0;
minrate = 0;
maxrate = 0;
eax = false;
minbuf = 0;
dsver = 0;
latency = 0;
initflags = 0;
speakers = 0;
freq = 0;
isvalid = false;
}
}
/**
* check if device supports all sample rates between minrate and maxrate.
* @return true or false
*/
public boolean Support_Continuous_Rate() {
return (flags & bassconstant.DSCAPS_CONTINUOUSRATE) > 0 ? true : false;
}
/**
* Check if device have DirectSound support
* @return true or false
*/
public boolean Support_DirectSound() {
return (flags & bassconstant.DSCAPS_EMULDRIVER) > 0 ? false : true;
}
/**
* Check if device is certified by microsoft
* @return true or false
*/
public boolean Certified_by_Microsoft() {
return (flags & bassconstant.DSCAPS_CERTIFIED) > 0 ? true : false;
}
/**
* Check if device support hardware mixing in mono
* @return true or false
*/
public boolean Support_Mono_Mixing() {
return (flags & bassconstant.DSCAPS_SECONDARYMONO) > 0 ? true : false;
}
/**
* Check if device support hardware mixing in stereo
* @return true or false
*/
public boolean Support_Stereo_Mixing() {
return (flags & bassconstant.DSCAPS_SECONDARYSTEREO) > 0 ? true : false;
}
/**
* Check if device support hardware mixing in 8bits
* @return true or false
*/
public boolean Support_8bit_Mixing() {
return (flags & bassconstant.DSCAPS_SECONDARY8BIT) > 0 ? true : false;
}
/**
* Check if device support hardware mixing in 16bits
* @return true or false
*/
public boolean Support_16bit_Mixing() {
return (flags & bassconstant.DSCAPS_SECONDARY16BIT) > 0 ? true : false;
}
}

View File

@@ -0,0 +1,174 @@
package jbass;
import com.un4seen.bass.BASS.BASS_RECORDINFO;
import com.un4seen.bass.BASS.bassconstant;
import anywheresoftware.b4a.BA;
@BA.ShortName("BASS_RECORDINFO")
/**
* Pengganti BASS_RECORDINFO
* @author rdkartono
*
*/
public class Bass_RecordInfo {
/**
* Device Flags
*/
public final int flags;
/**
* Formats supported by device
*/
public final int formats;
/**
* Available input sources
*/
public final int inputs;
/**
* True = only one input may be active at a time
*/
public final boolean singlein;
/***
* Device native sample rate
*/
public final int freq;
/**
* True = have valid data
*/
public final boolean isvalid;
public Bass_RecordInfo(BASS_RECORDINFO inf) {
if (inf instanceof BASS_RECORDINFO) {
inf.read();
flags = inf.flags;
formats = inf.formats;
inputs = inf.inputs;
singlein = inf.singlein;
freq = inf.freq;
isvalid = true;
} else {
isvalid = false;
flags = 0;
formats = 0;
inputs = 0;
singlein = false;
freq = 0;
}
}
/**
* Check if device have DirectSound support
* @return true or false
*/
public boolean Support_DirectSound() {
return (flags & bassconstant.DSCAPS_EMULDRIVER) > 0 ? false : true;
}
/**
* Check if device is certified by microsoft
* @return true or false
*/
public boolean Certified_by_Microsoft() {
return (flags & bassconstant.DSCAPS_CERTIFIED) > 0 ? true : false;
}
/**
* Check if Recorder support this format
* @param samplingrate : samplingrate, 11025 , 22050, 44100, 48000, 96000
* @param bits : 8 or 16
* @param channel : 1 or 2
* @return true if support
*/
public boolean Recorder_SupportFormat(int samplingrate, int bits, int channel) {
switch(samplingrate) {
case 11025 :
if (bits==8) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_1M08)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_1S08)>0 ? true : false;
}
} else if (bits==16) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_1M16)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_1S16)>0 ? true : false;
}
}
break;
case 22050 :
if (bits==8) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_2M08)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_2S08)>0 ? true : false;
}
} else if (bits==16) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_2M16)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_2S16)>0 ? true : false;
}
}
break;
case 44100 :
if (bits==8) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_4M08)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_4S08)>0 ? true : false;
}
} else if (bits==16) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_4M16)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_4S16)>0 ? true : false;
}
}
break;
case 48000 :
if (bits==8) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_48M08)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_48S08)>0 ? true : false;
}
} else if (bits==16) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_48M16)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_48S16)>0 ? true : false;
}
}
break;
case 96000 :
if (bits==8) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_96M08)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_96S08)>0 ? true : false;
}
} else if (bits==16) {
if (channel==1) {
return (flags & bassconstant.WAVE_FORMAT_96M16)>0 ? true : false;
} else if (channel==2) {
return (flags & bassconstant.WAVE_FORMAT_96S16)>0 ? true : false;
}
}
break;
}
return false;
}
}

View File

@@ -0,0 +1,252 @@
package jbass;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASSenc;
import com.un4seen.bass.BASSenc.ENCODEPROCEX;
import com.un4seen.bass.BASSenc_MP3;
import anywheresoftware.b4a.BA;
@BA.ShortName("HandleMP3Writer")
@BA.Events(values= {
"log(msg as string)",
"mp3bytes(sourcehandle as int, bb() as byte)",
"localrecordstart(namafile as string, success as boolean)",
"localrecordstop(namafile as string)"
})
public class HandleMP3Writer implements HandleWriter {
private String event = "";
private BA ba;
private int MP3Size=0; // size of written MP3 bytes
private boolean inited = false;
private String NamaFile = "";
private boolean need_log_event = false;
private boolean need_mp3bytes_event = false;
private boolean need_localrecordstart_event = false;
private boolean need_localrecordstop_event = false;
private BASS bass;
private BASSenc bassenc;
private BASSenc_MP3 bassencmp3;
private ENCODEPROCEX theproc;
private int encoder_handle = 0;
private RandomAccessFile raf ;
private HandleMp3WriterEvent ev;
private Object myobject;
/**
* Initialize MP3 Writer for B4A and B4J
* @param eventname : event name
*/
public void Initialize(BA bax, String eventname) {
ba = bax;
event = eventname;
inited = false;
myobject = this;
if (ba!=null) {
if (!event.isEmpty()) {
need_log_event = ba.subExists(event+"_log");
need_mp3bytes_event = ba.subExists(event+"_mp3bytes");
need_localrecordstart_event = ba.subExists(event+"_localrecordstart");
need_localrecordstop_event = ba.subExists(event+"_localrecordstop");
}
}
bass = new BASS();
bassenc = new BASSenc();
bassencmp3 = new BASSenc_MP3();
if (bass.BASS_GetVersion()!=0) {
if (bassenc.BASS_Encode_GetVersion()!=0) {
if (bassencmp3.BASS_Encode_MP3_GetVersion()!=0) {
inited = true;
}
}
}
}
@BA.Hide
public void SetHandleMp3WriterEvent(HandleMp3WriterEvent xx) {
ev = xx;
}
/**
* Check if MP3 Writer is initialized
* @return true if initialized
*/
public boolean IsInitialized() {
return inited;
}
private ENCODEPROCEX create_encodeproc() {
ENCODEPROCEX newproc = new ENCODEPROCEX() {
@Override
public void ENCODEPROCEX(int handle, int channel, Pointer buffer, int length, long offset, Pointer user) {
if (handle==0) return;
if (channel==0) return;
if (buffer==null) return;
if (length<1) return;
byte[] bb = buffer.getByteArray(0, length);
raise_mp3bytes_event(channel, bb);
if (raf!=null) {
// ketika pertama dipanggil, offset = 0 (MP3Size masih 0)
// ketika terakhir dipanggil, offset juga = 0 , tapi MP3Size mestinya sudah lebih dari 1
// pakai ini untuk close randomaccessfile
try {
raf.seek(offset);
raf.write(bb);
if (offset==0) {
if (MP3Size>0) {
Close();
raise_localrecordstop_event(channel, NamaFile);
}
}
} catch (IOException e) {
raise_log_event("Failed to Write , Msg="+e.getMessage()+", Caused="+e.getCause());
// ada error writing , close ajah
Close();
raise_localrecordstop_event(channel, NamaFile);
}
}
// update nilai MP3Size di sini. Jangan dipindah pindah !!
MP3Size+=length;
}
};
return newproc;
}
/**
* Close RandomAccessFile
*/
private void Close() {
if (raf!=null) {
try {
raf.close();
raise_log_event("File "+NamaFile+" is closed with Size="+getSize());
} catch (IOException e) {
raise_log_event("Close RandomAccessFile failed, code="+e.getMessage());
}
raf = null;
}
}
/**
* Record from Other Bass Handle to file
* File will be auto written by source handle
* If source handle is closed, then MP3 Writer will also auto closed
* @param recordfile : target MP3 filename. If empty string, no file will be created
* @param handle : other handle
* @return true if success
*/
public boolean CreateFile_from_Handle(String recordfile, int handle) {
if (inited) {
int flag = bassenc.BASS_ENCODE_AUTOFREE;
MP3Size = 0;
NamaFile = "";
// -m m = mode mono
// --preset cd = sama dengan -b 192 = bitrate 192
// --preset studio = sama dengan -b 256 = bitrate 256
// --preset standard = sama dengan -V 2 = VBR encoding, average bitrate 190
// --preset extreme = sama dengan -V 0 = VBR encoding, best quality, average bitrate 245
// --preset insane = sama dengan -b 320 = bitrate 320
// -B [number] = ABR / VBR , max bitrate is [number]
// --abr [number] = average bitrate, bitrate is [number]
// -q [number, 0 - 9] = 0 is best quality, 9 = worst quality
String options = "-m m -q 0 -V 0";
theproc = create_encodeproc();
encoder_handle = bassencmp3.BASS_Encode_MP3_Start(handle, options, flag, theproc, null);
if (encoder_handle==0) {
// failed to open encoder
raise_localrecordstart_event(handle, recordfile, false);
raise_log_event("CreateFile_From_Handle::Encode_MP3_Start failed, code="+bass.GetBassErrorString());
return false;
} else {
// success to open encoder
if (recordfile!="") {
// ada request untuk recording ke file
try {
raf = new RandomAccessFile(recordfile,"rw");
NamaFile = recordfile;
raise_log_event("CreateFile_From_Handle success, target="+NamaFile);
raise_localrecordstart_event(handle, recordfile, true);
return true;
} catch (FileNotFoundException e) {
raise_localrecordstart_event(handle, recordfile,false);
raise_log_event("CreateFile_From_Handle::RandomAccessFile failed, code="+e.getMessage());
return false;
}
} else {
// tidak ada request untuk recording ke file
// jadi Class ini cuma dipakai untuk encoding, dapetin mp3 bytes nya aja
Close(); // cuma mau nge-null-in raf , kalau sampai kebuka
return true; // tetep return true.
}
}
} else return false;
}
/**
* Get Current Opened Filename
* @return empty string if no file is opened
*/
public String getCurrentFileName() {
return NamaFile;
}
/**
* Get MP3 Size, obtained from EncodeProcEx
* @return value in int
*/
public int getSize() {
return MP3Size;
}
/**
* Check if MP3 file is opened and ready to write
* @return true if opened
*/
public boolean FileIsOpened() {
return (encoder_handle != 0) ? true : false;
}
private void raise_log_event(String value) {
if (need_log_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {value});
if (ev!=null) ev.mp3writer_log(value);
}
private void raise_mp3bytes_event(int sourcehandle, byte[] value) {
if (need_mp3bytes_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_mp3bytes", false, new Object[] {sourcehandle, value});
if (ev!=null) ev.mp3writer_mp3bytes(sourcehandle, value);
}
private void raise_localrecordstart_event(int sourcehandle, String namafile, boolean success) {
if (need_localrecordstart_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_localrecordstart", false, new Object[] {namafile, success});
if (ev!=null) ev.localrecordstart(sourcehandle, namafile, success);
}
private void raise_localrecordstop_event(int sourcehandle, String namafile) {
if (need_localrecordstop_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_localrecordstop", false, new Object[] {namafile});
if (ev!=null) ev.localrecordstop(sourcehandle, namafile);
}
}

View File

@@ -0,0 +1,8 @@
package jbass;
public interface HandleMp3WriterEvent {
void mp3writer_log(String msg);
void mp3writer_mp3bytes(int sourcehandle, byte[] bb);
void localrecordstart(int sourcehandle, String namafile, boolean success);
void localrecordstop(int sourcehandle, String namafile);
}

View File

@@ -0,0 +1,156 @@
package jbass;
import com.sun.jna.Pointer;
import com.un4seen.bass.BASS;
import com.un4seen.bass.BASSenc;
import com.un4seen.bass.BASSenc.ENCODEPROC;
import anywheresoftware.b4a.BA;
@BA.ShortName("HandleWavWriter")
@BA.Events(values= {
"log(msg as string)",
"pcmbytes(bb() as byte)"
})
public class HandleWavWriter implements HandleWriter {
private String event = "";
private BA ba;
private int PCMSize=0; // size of written PCM bytes
private boolean inited = false;
private String NamaFile = "";
private boolean need_log_event = false;
private boolean need_pcmbytes_event = false;
private BASS bass;
private BASSenc bassenc;
private ENCODEPROC theproc;
private int encoder_handle = 0;
private Object myobject;
/**
* Initialize WavWriter for B4A and B4J
* @param eventname : event name
*/
public void Initialize(BA bax, String eventname) {
NamaFile = "";
inited = false;
ba = bax;
event = eventname;
myobject = this;
if (ba!=null) {
if (!event.isEmpty()) {
need_log_event = ba.subExists(event+"_log");
need_pcmbytes_event = ba.subExists(event+"_pcmbytes");
}
}
bass = new BASS();
bassenc = new BASSenc();
if (bass.BASS_GetVersion()!=0) {
if (bassenc.BASS_Encode_GetVersion()!=0) {
inited = true;
}
}
}
/**
* Check if WavWriter is initialized
* @return true if initialized
*/
public boolean IsInitialized() {
return inited;
}
private ENCODEPROC create_encodeproc() {
ENCODEPROC newproc = new ENCODEPROC() {
@Override
public void ENCODEPROC(int handle, int channel, Pointer buffer, int length, Pointer user) {
if (handle==0) return;
if (channel==0) return;
if (buffer==null) return;
if (length<1) return;
PCMSize+=length;
raise_pcmbytes_event(buffer.getByteArray(0, length));
}
};
return newproc;
}
/**
* Record from Other Bass Handle to file
* File will be auto written by source handle
* If source handle is closed, then WavWriter will also auto closed
* @param recordfile : target WAV filename, if empty string, no file will be created
* @param handle : other handle
* @return true if success
*/
public boolean CreateFile_from_Handle(String recordfile, int handle) {
if (inited) {
int flag = bassenc.BASS_ENCODE_PCM | bassenc.BASS_ENCODE_AUTOFREE;
PCMSize = 0;
theproc = create_encodeproc();
encoder_handle = bassenc.BASS_Encode_Start(handle, recordfile, flag, theproc, null);
if (encoder_handle==0) {
raise_log_event("CreateFile_From_Handle::Encode_Start failed, code="+bass.GetBassErrorString());
return false;
} else {
NamaFile = recordfile;
raise_log_event("CreateFile_From_Handle success, target="+NamaFile);
return true;
}
} else return false;
}
/**
* Get Current Opened Filename
* @return empty string if no file is opened
*/
public String getCurrentFileName() {
return NamaFile;
}
/**
* Get PCM Size, obtained from ENCPROC
* @return value in int
*/
public int getSize() {
return PCMSize;
}
// auto close by handler
// /**
// * Close Wav Writer
// */
// public void Close_Wav() {
//
// if (encoder_handle!=0) {
// if (!bassenc.BASS_Encode_Stop(encoder_handle)) {
// raise_log_event("Close_Wav::Encode_Stop failed, code="+bass.GetBassErrorString());
// }
// encoder_handle = 0;
// }
// }
/**
* Check if WAV file is opened and ready to write
* @return true if opened
*/
public boolean FileIsOpened() {
return (encoder_handle != 0) ? true : false;
}
private void raise_log_event(String msg) {
if (need_log_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
}
private void raise_pcmbytes_event(byte[] bb) {
if (need_pcmbytes_event) ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_pcmbytes", false, new Object[] {bb});
}
}

View File

@@ -0,0 +1,17 @@
package jbass;
import anywheresoftware.b4a.BA;
/**
* Basic functions that all HandleWriter should have
* @author rdkartono
*
*/
public interface HandleWriter {
public void Initialize(BA bax, String eventname);
public boolean IsInitialized();
public boolean CreateFile_from_Handle(String recordfile, int handle);
public String getCurrentFileName() ;
public int getSize();
public boolean FileIsOpened() ;
}

2731
src/jbass/JBass.java Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
package jbass;
import anywheresoftware.b4a.BA;
@BA.ShortName("RecordGetInputResult")
public class RecordGetInputResult {
public String InputName = "";
public float Volume = -1.0f;
public boolean IsOn = false;
}

Some files were not shown because too many files have changed in this diff Show More