Using SoundPool for games - kotlin

class SoundPlayer(context: Context) {
// For sound FX
private val soundPool: SoundPool = SoundPool(10, // Here
AudioManager.STREAM_MUSIC,
0)
companion object {
var playerExplodeID = -1
var invaderExplodeID = -1
var shootID = -1
var damageShelterID = -1
var uhID = -1
var ohID = -1
}
init {
try {
// Create objects of the 2 required classes
val assetManager = context.assets
var descriptor: AssetFileDescriptor
// Load our fx in memory ready for use
descriptor = assetManager.openFd("shoot.ogg")
shootID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("invaderexplode.ogg")
invaderExplodeID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("damageshelter.ogg")
damageShelterID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("playerexplode.ogg")
playerExplodeID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("damageshelter.ogg")
damageShelterID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("uh.ogg")
uhID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("oh.ogg")
ohID = soundPool.load(descriptor, 0)
} catch (e: IOException) {
// Print an error message to the console
Log.e("error", "failed to load sound files")
}
}
fun playSound(id: Int){
soundPool.play(id, 1f, 1f, 0, 0, 1f)
}
}
i have a problem with SoundPool cant use it is says constructor SoundPool is deprecated
i'm kinda new so don't know how to fix this (watched many videos and searched everywhere but i cant fix it)
so maybe someone can help me out tell me what to do

When something is deprecated, there always should be a hint what to use instead.
So you need to use SoundPool.Builder to create new instance of an object.
But there is one issue if you target API level that was release before SoundPool.Builder then you will get ClassNotFoundException.
So general approach is to check the API level and do things in old way before API X(when new feature was introduced), and a new way after API X:
#Suppress("DEPRECATION")
fun buildSoundPool(maxStreams: Int):SoundPool =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val attrs = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
SoundPool.Builder()
.setAudioAttributes(attrs)
.setMaxStreams(maxStreams)
.build()
} else {
SoundPool(maxStreams, AudioManager.STREAM_MUSIC, 0)
}
Then:
private val soundPool: SoundPool = buildSoundPool(10)
Also I do recommend to use my custom implementation of SoundPool because lots of platform dependent issues that was introduced in original SoundPool on different versions on Android.

Related

Kotlin multiplatform. Ktor download big file and save to local file [duplicate]

I've been spending way too much time trying to solve this problem. So the code that I posted below does work in terms of downloading a file, but the problem is, the flow has a very unexpected behaviour. The response.content.readAvailable() method call seems to block until it's completely done downloading the whole file at which point the emit progress happens, so you end up waiting a long time for the file to download, and then in a split second you get all of the progress updates. So I'm wondering if there is a way to do this where I read in a certain number of bytes at a time and then emit a progress and then repeat until the file is done downloading? Or maybe a way to hook into the readAvailable() method and update the progress that way? Any help with this would be greatly appreciated.
Here's the code I found and modified, but still does not work right:
suspend fun HttpClient.downloadFile(
output: File,
downloadUrl: String,
md5Hash: String,
) = flow {
try {
val response = get<HttpResponse> { url(downloadUrl) }
val data = ByteArray(response.contentLength()?.toInt() ?: 0)
val contentLn = response.contentLength()?.toInt() ?: 0
var offset = 0
var bytesRemaining = contentLn
do {
val chunkSize = min(maxChunkSize, bytesRemaining)
logger?.d { "Read Available:" }
val result = response.content.readAvailable(data, offset, length = chunkSize)
val progress = ((offset / contentLn.toDouble()) * 100).toInt()
emit(DownloadResult.Progress(progress))
logger?.d { "logged progress: $progress" }
// delay(6000L) this was to test my assumption that the readAvalible was blocking.
offset += chunkSize
bytesRemaining -= chunkSize
} while (result != -1)
if (response.status.isSuccess()) {
if (data.md5().hex == md5Hash) {
output.write(data)
emit(DownloadResult.Success)
} else {
emit(DownloadResult.ErrorCorruptFile)
}
} else {
emit(DownloadResult.ErrorBadResponseCode(response.status.value))
}
} catch (e: TimeoutCancellationException) {
emit(DownloadResult.ErrorRequestTimeout("Connection timed out", e))
}
}
Finally after a stupid amount of time I solved this. What you need to use is this. That gives you access to the byte channel as it is downloading.
and a very crude implementation (that I'm not yet done with) is this:
get<HttpStatement>(url = downloadUrl).execute {
var offset = 0
val byteBufferSize = 1024 * 100
val channel = it.receive<ByteReadChannel>()
val contentLen = it.contentLength()?.toInt() ?: 0
val data = ByteArray(contentLen)
do {
val currentRead = channel.readAvailable(data, offset, byteBufferSize)
val progress = if(contentLen == 0) 0 else ( offset / contentLen.toDouble() ) * 100
logger?.d { "progress: $progress" }
offset += currentRead
} while (currentRead >= 0)
}
two things to not with this solution. 1.) I'm in the context of HttpClient, so that's how I have access to get(). 2.) I'm creating a byte buffer size of 1024 * 100 in order to not let the readAvailable method block for too long, though this might not be necessary... the one nice thing about it is that it determines how frequently you will be publishing your progress updates.

Kotlin ListView IndexOutOfBoundsException

(new to kotlin) I'm making my own music app but i'm having an error that i don't understand :
in the bit of code where i try to access a random view (shuffle), i get the java.lang.IndexOutOfBoundsException: Index:96 Size:11.
96 in this example is the view in the listview that i'm trying to access.
It happens in this line : var iView = listViewMusic.get(idShuffle)
Same if i use listViewMusic[idShuffle]
EDIT : Turns out that 11 is only the 11 visible items on screen at any given moment, even if the list contains hundreds of items. When i use listViewMusic.smoothscrolltoposition(idShuffle) it works, but the size of 11 now relates to the 11 on screen after the scrolling
The function playNext() inside the activity, called when clicking on the shuffle button:
fun playNext(){
try {
// Find the order of the next song to play
var idShuffle = musicAdapter!!.getIdofItem(listMusicShuffled.get(musicNextToPlay).title)
// Find the right record in the list
//var iView = listViewMusic[idShuffle]
//toastIt(applicationContext, "adapter count ${listViewMusic.adapter.count}")
//toastIt(applicationContext, "listview count ${listViewMusic.count}")
var iView = listViewMusic.get(idShuffle) //throws the error
// Play it
playOrPause(iView)
// Prepare the next track
musicNextToPlay += 1
if (musicNextToPlay >= listMusicShuffled.size) {
musicNextToPlay = -1
}
} catch (e:Exception) {toastException(applicationContext, "playnext", e) }
}
The part of the function onCreate that fills in the listViewMusic:
// Retrieve the data
val mediaStoreUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor = contentResolver.query(mediaStoreUri, null, "", null, null)
//Browse the data
listMusic = mutableListOf()
val listMusicJson = getMusicFromJson()
while (cursor!!.moveToNext()) {
val musicName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
val musicArtist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST))
val musicUrl = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))
val musicType:String =
if (musicArtist.contains("lmdmf", true)) { "LMDMF" }
else if (musicName.contains("slack", true)) { "SLACK" }
else { "MUSIC" }
listMusic.add(
MusicTrack(
musicName,
musicPlayCount,
musicUrl,
musicType
)
)
}
cursor.close()
musicAdapter = MusicAdapter(listMusic.sortedWith(compareBy<MusicTrack>{ it.title }).filter { it.type == "MUSIC"}.toMutableList())
listViewMusic.adapter = musicAdapter

Setting local variables inside a function for unit testing kotlin

I'm trying to testing a function in android.For the function to be tested ,have to set local variable inside the function. Heres the code:
fun needToBeTested(serviceContext: Context):Int
{
val manager = serviceContext.getSystemService(Context.USB_SERVICE) as UsbManager
val deviceList = manager.deviceList
if(deviceList.size != 0){
val deviceIterator = deviceList.values.iterator()
while (deviceIterator.hasNext()) {
val device = deviceIterator.next()
if ((device != null)&&(device.deviceClass == 255)) { // USB_CLASS_VENDOR_SPEC - HUB
val permitToRead:Boolean = manager.hasPermission(device)
if(permitToRead){
setUpConnection(device,serviceContext)
} else{
manager.requestPermission(device, permissionIntent)
}
deviceConnectionStatus = 1
} else if((device != null) &&(device.deviceClass == 0)){ // USB_CLASS_PER_INTERFACE - USB STICK
}
}
} else (deviceList.size == 0){
connectionStatus = 0
}
}
Test class
class Test {
val mockContext = ApplicationProvider.getApplicationContext<Context>()
#Test
fun needTobeTested_returnOne(){
asserThat(sample.needToBeTested(mockContext)==1) //successcase
}
}
For testing this class,need to set the value of deviceList.Is there any way to set the value of 'x' from the test class for testing this function. Im new to android and testing.Any help would be really helpful.

NoSuchElementException java.lang.Scanner

I have no idea what the error is, I am having a hard time adapting to this language, any help thank you very much.
Error:
Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Scanner.throwFor(Scanner.java:937)
at java.base/java.util.Scanner.next(Scanner.java:1594)
at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
at Packing.<init>(Packing.kt:100)
at PackingKt.main(Packing.kt:7)
at PackingKt.main(Packing.kt)
My code:
import java.io.InputStream
import java.util.Scanner
fun main() {
val input = Scanner(InputStream.nullInputStream())
val packing1 = Packing(input)
val packing2 = Packing(input)
val packing3 = Packing(input)
var total = 0
var min = 0
val combinations = ArrayList<String>()
for(a in 1..3){
for(b in 1..3){
for(c in 1..3){
//here is a piece of code
}
}
combinations.sort()
println("${combinations.get(0)} $min")
}
}
class Packing {
var brownBottles = 0
var greenBottles = 0
var clearBottles = 0
constructor (input : Scanner){
brownBottles = input.nextInt() //this is the line 100
greenBottles = input.nextInt()
clearBottles = input.nextInt()
}
}
The idea is to enter values by console that initialize the variables of my objects.
I would just use
val input = Scanner(System.`in`)
If you enter 9 integers in the console the initialization of the Packing objects should work.
The nullInputStream() makes no sense to me. It's not possible to read from the console with that.
The combinations list is empty so it throws an exception accessing it here
println("${combinations.get(0)} $min")

Best way to translate this java code into kotlin

URL url = new URL(urlSpec);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
InputStream in = connection.getInputStream();
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
I am especially curious about this part
while(bytesRead = in.read(buffer))
We know that asigements are treated as statements in kotlin while in java they are treated as expressions, so this construct is only possible in java.
What is best way to translate this java code into kotlin?
Instead of translating the code literally, make use of Kotlin's stdlib which offers a number of useful extension functions. Here's one version
val text = URL(urlSpec).openConnection().inputStream.bufferedReader().use { it.readText() }
To answer the original question: You're right, assignments are not treated as expressions. Therefore you will need to separate the assignment and the comparison. Take a look at the implementation in the stdlib for an example:
public fun Reader.copyTo(out: Writer, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
var charsCopied: Long = 0
val buffer = CharArray(bufferSize)
var chars = read(buffer)
while (chars >= 0) {
out.write(buffer, 0, chars)
charsCopied += chars
chars = read(buffer)
}
return charsCopied
}
Source: https://github.com/JetBrains/kotlin/blob/a66fc9043437d2e75f04feadcfc63c61b04bd196/libraries/stdlib/src/kotlin/io/ReadWrite.kt#L114
You could use apply block to execute the assignment:
val input= connection.getInputStream();
var bytesRead = 0;
val buffer = ByteArray(1024)
while (input.read(buffer).apply { bytesRead = this } > 0) {
out.write(buffer, 0, bytesRead);
}
You could use something like this
This operation may be little heavy as a function is created each iteration.
val url = URL("urlSpec")
val connection = url.openConnection() as HttpURLConnection
val `in` = connection.inputStream
val buffer = ByteArray(1024)
var bytesRead: Int? = null
while ({ bytesRead = `in`.read(buffer); bytesRead }() != null) {
out.write(buffer, 0, bytesRead!!)
}
out.close()