commit 28/10/2025
This commit is contained in:
@@ -1,157 +0,0 @@
|
||||
package audio
|
||||
|
||||
import audio.Bass.BASS_STREAMPROC_END
|
||||
import audio.BassEnc.BASS_ENCODE_PCM
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import org.tinylog.Logger
|
||||
import java.io.PipedInputStream
|
||||
import java.io.PipedOutputStream
|
||||
import java.net.DatagramPacket
|
||||
import java.net.DatagramSocket
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.function.BiConsumer
|
||||
import java.util.function.Consumer
|
||||
|
||||
@Deprecated("Sepertinya gak jadi pake")
|
||||
@Suppress("unused")
|
||||
/**
|
||||
* UDPReceiverToFile is a class that listens for UDP packets on a specified address and port
|
||||
* and writes the received data to a specified file.
|
||||
* It is designed to run in a separate thread and can be stopped when no longer needed.
|
||||
* @param listeningAddress The address to listen for incoming UDP packets.
|
||||
* @param listeningPort The port to listen for incoming UDP packets.
|
||||
* @param samplingrate The sampling rate for the audio data, default is 44,100 Hz.
|
||||
* @param channel The number of audio channels, default is 1 (mono).
|
||||
* @param outputFilePath The path to the file where the received data will be written.
|
||||
* @param senderIP The IP address of the sender from which to accept packets.
|
||||
*/
|
||||
class UDPReceiverToFile(listeningAddress: String, listeningPort: Int, val samplingrate: Int=44100, val channel: Int=1, val outputFilePath: String, val senderIP: String) {
|
||||
|
||||
private var socket: DatagramSocket? = null
|
||||
private val bass : Bass = Bass.Instance
|
||||
private val bassenc : BassEnc = BassEnc.Instance
|
||||
private var isReceiving: Boolean = false
|
||||
private val pipeIn= PipedInputStream(16*1024) // 16K
|
||||
var isReady: Boolean = false; private set
|
||||
var bytesReceived: Long = 0; private set
|
||||
var bytesWritten: Long = 0; private set
|
||||
|
||||
|
||||
init {
|
||||
try{
|
||||
val socketaddress = InetSocketAddress(listeningAddress, listeningPort)
|
||||
socket = DatagramSocket(socketaddress)
|
||||
isReady = true
|
||||
} catch (e : Exception) {
|
||||
Logger.error {"Failed to create UDP socket: ${e.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
private val streamProc = Bass.STREAMPROC { handle, buffer, length, user ->
|
||||
try{
|
||||
val dd = ByteArray(length)
|
||||
val bytesread = pipeIn.read(dd)
|
||||
|
||||
if (bytesread>0){
|
||||
buffer?.write(0, dd, 0, bytesread) // Write the data to the buffer
|
||||
bytesWritten += bytesread
|
||||
// Return the number of bytes read
|
||||
bytesread
|
||||
} else {
|
||||
// if bytesread is 0, it means the pipe is empty, return BASS_STREAMPROC_END
|
||||
BASS_STREAMPROC_END
|
||||
}
|
||||
} catch (e : Exception){
|
||||
// If an error occurs, log it and return BASS_STREAMPROC_END
|
||||
Logger.error { "STREAMPROC exception on UDPReceiverToFile $senderIP $outputFilePath" }
|
||||
BASS_STREAMPROC_END
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts receiving data from the UDP socket and writing it to the specified file.
|
||||
* This method runs in a separate thread.
|
||||
* @param callback A BiConsumer that accepts a Boolean indicating success or failure and a String message.
|
||||
* @param udpIsReceiving A Consumer that accepts a Boolean indicating whether UDP is currently receiving
|
||||
*/
|
||||
fun startReceiving(callback : BiConsumer<Boolean, String>, udpIsReceiving: Consumer<Boolean>) {
|
||||
var isReceiving = false
|
||||
if (isReady){
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Default)
|
||||
scope.launch(CoroutineName("UDPReceiverToFile UDP $senderIP $outputFilePath")) {
|
||||
Logger.info { "UDPReceiverToFile started, listening on ${socket?.localSocketAddress} , saving to $outputFilePath" }
|
||||
|
||||
PipedOutputStream(pipeIn).use { pipeOut ->
|
||||
while (isReceiving) {
|
||||
try{
|
||||
val xx = DatagramPacket(ByteArray(1500),1500)
|
||||
socket?.receive(xx)
|
||||
if (xx.address.hostAddress!= senderIP) continue
|
||||
if (xx.length < 1) continue
|
||||
pipeOut.write(xx.data, 0, xx.length)
|
||||
bytesReceived += xx.length
|
||||
if (!isReceiving){
|
||||
isReceiving = true
|
||||
udpIsReceiving.accept(true)
|
||||
}
|
||||
} catch (e : Exception){
|
||||
Logger.error { "Error receiving UDP packet: ${e.message}" }
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.info { "UDPReceiverToFile ended" }
|
||||
}
|
||||
|
||||
|
||||
scope.launch(CoroutineName("UDPReceiverToFile BASS $senderIP $outputFilePath")) {
|
||||
bass.BASS_SetDevice(0) // Set to No Sound device, we are not playing audio
|
||||
val streamhandle = bass.BASS_StreamCreate(samplingrate, channel, 0, streamProc, null)
|
||||
if (streamhandle!=0){
|
||||
bass.BASS_ChannelPlay(streamhandle,false)
|
||||
val encodehandle = bassenc.BASS_Encode_Start(streamhandle, outputFilePath, BASS_ENCODE_PCM, null, null)
|
||||
if (encodehandle!=0){
|
||||
Logger.info { "UDPReceiverToFile started writing to $outputFilePath" }
|
||||
callback.accept(true, "UDPReceiverToFile started successfully, writing to $outputFilePath")
|
||||
while (isReceiving) {
|
||||
try {
|
||||
delay(1000)
|
||||
} catch (e: InterruptedException) {
|
||||
Logger.error { "UDPReceiverToFile thread interrupted: ${e.message}" }
|
||||
break
|
||||
}
|
||||
}
|
||||
bassenc.BASS_Encode_Stop(encodehandle)
|
||||
bass.BASS_StreamFree(streamhandle)
|
||||
Logger.info { "UDPReceiverToFile stopped writing to $outputFilePath" }
|
||||
callback.accept(false, "UDPReceiverToFile stopped successfully, written bytes: $bytesWritten")
|
||||
|
||||
} else {
|
||||
callback.accept(false, "Failed to start encoding: ${bass.BASS_ErrorGetCode()}")
|
||||
}
|
||||
|
||||
} else {
|
||||
callback.accept(false, "Failed to create stream: ${bass.BASS_ErrorGetCode()}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} else callback.accept(false, "UDPReceiverToFile is not ready. Check if the socket was created successfully.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop UDPReceiverToFile from receiving data.
|
||||
*/
|
||||
fun stopReceiving() {
|
||||
isReceiving = false
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package audio
|
||||
|
||||
import audio.Bass.BASS_STREAM_DECODE
|
||||
import codes.Somecodes.Companion.ValidFile
|
||||
import com.sun.jna.Memory
|
||||
import java.net.DatagramPacket
|
||||
import java.net.DatagramSocket
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.SocketAddress
|
||||
import java.util.function.BiConsumer
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Deprecated("Sepertinya gak jadi pake ini")
|
||||
@Suppress("unused")
|
||||
class UDPSenderFromFile(val fileName: String, val bytesPerPackage: Int=1024, targetIP: Array<String>, targetPort: Int ) {
|
||||
val bass: Bass = Bass.Instance
|
||||
var filehandle: Int = 0
|
||||
var listSocketAddress = ArrayList<SocketAddress>()
|
||||
|
||||
var initialized: Boolean = false; private set
|
||||
var isRunning: Boolean = false; private set
|
||||
var bytesSent: Int = 0; private set
|
||||
|
||||
init {
|
||||
if (ValidFile(fileName)){
|
||||
bass.BASS_SetDevice(0)
|
||||
val handle = bass.BASS_StreamCreateFile(false, fileName, 0,0, BASS_STREAM_DECODE)
|
||||
if (handle!=0){
|
||||
// test buka file berhasil, tutup lagi
|
||||
bass.BASS_StreamFree(handle)
|
||||
//if (targetPort>0 && targetPort<65535){
|
||||
if (targetPort in 0..65535){
|
||||
if (targetIP.isNotEmpty()){
|
||||
var validIPs = true
|
||||
for(ip in targetIP){
|
||||
try{
|
||||
var so = InetSocketAddress(ip, targetPort)
|
||||
listSocketAddress.add(so)
|
||||
} catch (e : Exception){
|
||||
validIPs = false
|
||||
}
|
||||
}
|
||||
|
||||
if (validIPs){
|
||||
initialized = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun Start(callback: BiConsumer<Boolean, String>){
|
||||
if (initialized){
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Default)
|
||||
scope.launch(CoroutineName("UDPSenderFromFile $fileName")) {
|
||||
try {
|
||||
val socket = DatagramSocket()
|
||||
bass.BASS_SetDevice(0) // Set to No Sound Device
|
||||
val handle = bass.BASS_StreamCreateFile(false, fileName, 0, 0, BASS_STREAM_DECODE)
|
||||
if (handle!=0){
|
||||
isRunning = true
|
||||
bytesSent = 0
|
||||
callback.accept(true,"UDPSenderFromFile started, sending $fileName to ${listSocketAddress.size} targets")
|
||||
while(isRunning){
|
||||
val buffer = Memory(bytesPerPackage.toLong())
|
||||
val bytesRead = bass.BASS_ChannelGetData(handle, buffer, bytesPerPackage)
|
||||
if (bytesRead > 0) {
|
||||
for(so in listSocketAddress){
|
||||
val bytes = buffer.getByteArray(0, bytesRead)
|
||||
socket.send(DatagramPacket(bytes, bytes.size, so))
|
||||
bytesSent += bytes.size
|
||||
}
|
||||
|
||||
} else isRunning = false
|
||||
}
|
||||
callback.accept(false,"UDPSenderFromFile finished sending $fileName")
|
||||
bass.BASS_StreamFree(handle)
|
||||
socket.close()
|
||||
} else callback.accept(false, "Failed to open file $fileName for reading")
|
||||
} catch (e : Exception){
|
||||
callback.accept(false, "Error in UDPSenderFromFile: ${e.message}")
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else callback.accept(false, "UDP Sender not initialized, check file and target IP/Port")
|
||||
|
||||
}
|
||||
|
||||
fun Stop(){
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user