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

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);
}
}