790 lines
24 KiB
Java
790 lines
24 KiB
Java
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});
|
|
}
|
|
|
|
}
|