SoundPool - Setting Rate (Pitch) while playing audio - kotlin

I am trying to change the rate of an mp3 while playing (looped) using the following code.
I can start, pause and stop it no problem, but cant change the rate attribute, any ideas?
Thanks.
val soundPool: SoundPool
var mp3ID: Int = 0
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
soundPool = SoundPool.Builder()
.setAudioAttributes(audioAttributes)
.setMaxStreams(4)
.build()
// Load the MP3
mp3ID = soundPool.load(requireContext(), R.raw.test3s, 1)
// Buttons
// Play
bnd.btnPlay.setOnClickListener {
soundPool.play(mp3ID, 1f, 1f, 1, -1, 1f)
}
// Play half rate
bnd.btnHalf.setOnClickListener {
soundPool.setRate(mp3ID, 0.5f)
}
// Play double rate
bnd.btnTwo.setOnClickListener {
soundPool.setRate(mp3ID, 2.0f)
}

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.

How can I download a large file with Ktor and Kotlin with a progress indicator?

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.

Using SoundPool for games

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.

What is the optimal render loop in Dart 2?

I am looking for ideas regarding an optimal/minimal structure for the inner render loop in Dart 2, for a 2d game (if that part matters).
Clarification / Explanation: Every framework / language has an efficient way to:
1) Deal with time.
2) Render to the screen (via memory, a canvas, an image, or whatever).
For an example, here is someone that answered this for the C# language. Being new to Flutter / Dart, my first attempt (below), is failing to work and as of right now, I can not tell where the problem is.
I have searched high and low without finding any help on this, so if you can assist, you have my eternal gratitude.
There is a post on Reddit by ‘byu/inu-no-policemen’ (a bit old). I used this to start. I suspect that it is crushing the garbage collector or leaking memory.
This is what I have so far, but it crashes pretty quickly (at least in the debugger):
import 'dart:ui';
import 'dart:typed_data';
import 'dart:math' as math;
import 'dart:async';
main() async {
var deviceTransform = new Float64List(16)
..[0] = 1.0 // window.devicePixelRatio
..[5] = 1.0 // window.devicePixelRatio
..[10] = 1.0
..[15] = 1.0;
var previous = Duration.zero;
var initialSize = await Future<Size>(() {
if (window.physicalSize.isEmpty) {
var completer = Completer<Size>();
window.onMetricsChanged = () {
if (!window.physicalSize.isEmpty) {
completer.complete(window.physicalSize);
}
};
return completer.future;
}
return window.physicalSize;
});
var world = World(initialSize.width / 2, initialSize.height / 2);
window.onBeginFrame = (now) {
// we rebuild the screenRect here since it can change
var screenRect = Rect.fromLTWH(0.0, 0.0, window.physicalSize.width, window.physicalSize.height);
var recorder = PictureRecorder();
var canvas = Canvas(recorder, screenRect);
var delta = previous == Duration.zero ? Duration.zero : now - previous;
previous = now;
var t = delta.inMicroseconds / Duration.microsecondsPerSecond;
world.update(t);
world.render(t, canvas);
var builder = new SceneBuilder()
..pushTransform(deviceTransform)
..addPicture(Offset.zero, recorder.endRecording())
..pop();
window.render(builder.build());
window.scheduleFrame();
};
window.scheduleFrame();
window.onPointerDataPacket = (packet) {
var p = packet.data.first;
world.input(p.physicalX, p.physicalY);
};
}
class World {
static var _objectColor = Paint()..color = Color(0xa0a0a0ff);
static var _s = 200.0;
static var _obejectRect = Rect.fromLTWH(-_s / 2, -_s / 2, _s, _s);
static var _rotationsPerSecond = 0.25;
var _turn = 0.0;
double _x;
double _y;
World(this._x, this._y);
void input(double x, double y) { _x = x; _y = y; }
void update(double t) { _turn += t * _rotationsPerSecond; }
void render(double t, Canvas canvas) {
var tau = math.pi * 2;
canvas.translate(_x, _y);
canvas.rotate(tau * _turn);
canvas.drawRect(_obejectRect, _objectColor);
}
}
Well, after a month of beating my face against this, I finally figured out the right question and that got me to this:
Flutter Layers / Raw
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This example shows how to perform a simple animation using the raw interface
// to the engine.
import 'dart:math' as math;
import 'dart:typed_data';
import 'dart:ui' as ui;
void beginFrame(Duration timeStamp) {
// The timeStamp argument to beginFrame indicates the timing information we
// should use to clock our animations. It's important to use timeStamp rather
// than reading the system time because we want all the parts of the system to
// coordinate the timings of their animations. If each component read the
// system clock independently, the animations that we processed later would be
// slightly ahead of the animations we processed earlier.
// PAINT
final ui.Rect paintBounds = ui.Offset.zero & (ui.window.physicalSize / ui.window.devicePixelRatio);
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder, paintBounds);
canvas.translate(paintBounds.width / 2.0, paintBounds.height / 2.0);
// Here we determine the rotation according to the timeStamp given to us by
// the engine.
final double t = timeStamp.inMicroseconds / Duration.microsecondsPerMillisecond / 1800.0;
canvas.rotate(math.pi * (t % 1.0));
canvas.drawRect(ui.Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0),
ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0));
final ui.Picture picture = recorder.endRecording();
// COMPOSITE
final double devicePixelRatio = ui.window.devicePixelRatio;
final Float64List deviceTransform = Float64List(16)
..[0] = devicePixelRatio
..[5] = devicePixelRatio
..[10] = 1.0
..[15] = 1.0;
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder()
..pushTransform(deviceTransform)
..addPicture(ui.Offset.zero, picture)
..pop();
ui.window.render(sceneBuilder.build());
// After rendering the current frame of the animation, we ask the engine to
// schedule another frame. The engine will call beginFrame again when its time
// to produce the next frame.
ui.window.scheduleFrame();
}
void main() {
ui.window.onBeginFrame = beginFrame;
ui.window.scheduleFrame();
}

XNA 4.0 Camera and object handling on screen

For developing a side-scrolling platform 2D game I want to implement a moving camera class, the reason of using the class instead of moving the whole map is that I'll have to use too many objects at once witch will cause a lag. I cannot let that happen.
There's a nice algorithm for handling the camera, when player is moving further than the width of the screen then camera moves on players direction until he is once again in the middle of the screen, I've been working several days for making this algorithm work however there's been no success.
// Main
public class Camera
{
protected float _zoom;
protected Matrix _transform;
protected Matrix _inverseTransform;
//The zoom scalar (1.0f = 100% zoom level)
public float Zoom
{
get { return _zoom; }
set { _zoom = value; }
}
// Camera View Matrix Property
public Matrix Transform
{
get { return _transform; }
set { _transform = value; }
}
// Inverse of the view matrix,
// can be used to get
// objects screen coordinates
// from its object coordinates
public Matrix InverseTransform
{
get { return _inverseTransform; }
}
public Vector2 Pos;
// Constructor
public Camera()
{
_zoom = 2.4f;
Pos = new Vector2(0, 0);
}
// Update
public void Update(GameTime gameTime)
{
//Clamp zoom value
_zoom = MathHelper.Clamp(_zoom, 0.0f, 10.0f);
//Create view matrix
_transform = Matrix.CreateScale(new Vector3(_zoom, _zoom, 1)) *
Matrix.CreateTranslation(Pos.X, Pos.Y, 0);
//Update inverse matrix
_inverseTransform = Matrix.Invert(_transform);
}
}
This is the camera class I made for handling the screen, it's main purpose is to resize the screen, more precisely to zoom in and out whenever I want to change my screen, (Title screen, Playing screen, Game over, and like that.)
Moving the camera is quite simple with keys, like this.
if (keyState.IsKeyDown(Keys.D))
Cam.Pos.X -= 20;
if (keyState.IsKeyDown(Keys.A))
Cam.Pos.X += 20;
if (keyState.IsKeyDown(Keys.S))
Cam.Pos.Y -= 20;
if (keyState.IsKeyDown(Keys.W))
Cam.Pos.Y += 20;
And ofc. the drawing method witch apply the camera.
spriteBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend, null, null, null, null, Cam.Transform);
Here comes the part when I stop, so what I want to do is make something like 2 2D rooms. By Room I mean the place where I usually place objects. like this "Vector2(74, 63)" So I want to create a place where I could draw items that would stick to the screen and wouldn't move, and make the screen bounds that would make my algorithm to work, witch will be always on screen and as an addition it will check if one of the borders of the screen "room" reaches the certain coordinates of the map "room".
I think that the reason for that would be obvious because I don't want player to move camera outside the map when he reaches the wall, otherwise the player would already see a part of the next map where he will be transformed.
The reason of drawing both maps next to each other is again to reduce the loading time so player wouldn't have to wait for playing the next map.
Alright, so I've run into more troubles than I expected so I'll add extra information and will start with the player class:
// Main
public class Player
{
public Texture2D AureliusTexture;
public Vector2 position;
public Vector2 velocity;
public Vector2 PosForTheCam; // Variable that holds value for moving the camera
protected Vector2 dimensions;
protected CollisionPath attachedPath;
const float GRAVITY = 18.0f;
const float WALK_VELOCITY = 120f;
const float JUMP_VELOCITY = -425.0f;
// Constructor
public Player()
{
dimensions = new Vector2(23, 46);
position = new Vector2(50, 770);
}
public void Update(float deltaSeconds, List<CollisionPath> collisionPaths)
{
#region Input handling
KeyboardState keyState = Keyboard.GetState();
if (keyState.IsKeyDown(Keys.Left))
{
velocity.X = -WALK_VELOCITY;
}
else if (keyState.IsKeyDown(Keys.Right))
{
velocity.X = WALK_VELOCITY;
}
else
{
velocity.X = 0;
}
if (attachedPath != null && keyState.IsKeyDown(Keys.Space))
{
velocity.Y = JUMP_VELOCITY;
attachedPath = null;
}
velocity.Y += GRAVITY;
#endregion
#region Region of handling the camera based on Player
PosForTheCam.X = velocity.X;
#endregion
#region Collision checking
if (velocity.Y >= 0)
{
if (attachedPath != null)
{
position.X += velocity.X * deltaSeconds;
position.Y = attachedPath.InterpolateY(position.X) - dimensions.Y / 2;
velocity.Y = 0;
if (position.X < attachedPath.MinimumX || position.X > attachedPath.MaximumX)
{
attachedPath = null;
}
}
else
{
Vector2 footPosition = position + new Vector2(0, dimensions.Y / 2);
Vector2 expectedFootPosition = footPosition + velocity * deltaSeconds;
CollisionPath landablePath = null;
float landablePosition = float.MaxValue;
foreach (CollisionPath path in collisionPaths)
{
if (expectedFootPosition.X >= path.MinimumX && expectedFootPosition.X <= path.MaximumX)
{
float pathOldY = path.InterpolateY(footPosition.X);
float pathNewY = path.InterpolateY(expectedFootPosition.X);
if (footPosition.Y <= pathOldY && expectedFootPosition.Y >= pathNewY && pathNewY < landablePosition)
{
landablePath = path;
landablePosition = pathNewY;
}
}
}
if (landablePath != null)
{
velocity.Y = 0;
footPosition.Y = landablePosition;
attachedPath = landablePath;
position.X += velocity.X * deltaSeconds;
position.Y = footPosition.Y - dimensions.Y / 2;
}
else
{
position = position + velocity * deltaSeconds;
}
}
}
else
{
position += velocity * deltaSeconds;
attachedPath = null;
}
#endregion
}
}
So I state it clear that I asked my friend to do most of it because I wanted to handle the gravity and the slopes so we made it work similar like in Unity. And he happened to know how to do that.
And so I'll add the Update method that handles the camera from the Main Class.
MM.Update(gameTime); // Map Managher update function for map handling
Cam.Update(gameTime); // Camera update
Cam.Zoom = 2.4f; // Sets the zoom level for the title screen
// Takes the start position for camera in map and then turns off the update
// so the camera position can be changed. Else it would just keep an infinite
// loop and we couldn't change the camera.
if (StartInNewRoom)
{
Cam.Pos = MM.CameraPosition; // Applys the camera position value from the map manager class
StartInNewRoom = false;
}
I am unsure how to handle the camera, like I used your method and the result often ended up that camera moves by itself or it doesn't move at all.
If you don't want objects to move with the camera like a HUD you need a second spriteBatch.Begin() without your camera matrix which you draw after your actual scene.
To make the camera not move out of the map you could use some kind of collision detection. Just calculate the right border of your camera. It depends where the origin of your camera is.
Is your camera matrix working like this? Because the position should be negative or it will move in the wrong direction.
This is how mine looks like.
return Matrix.CreateTranslation(new Vector3(-camera.position.X, -camera.position.Y, 0)) *
Matrix.CreateRotationZ(Rotation) * Matrix.CreateScale(Zoom) *
Matrix.CreateTranslation(new Vector3(Viewport.Width * 0.5f, Viewport.Height * 0.5f, 0));
Viewport.Width/Height * 0.5 centers you camera.
You can also apply this behind your Pos.X/Y
To Camera follows player
public void Update(Player player)
{
//Clamp zoom value
_zoom = MathHelper.Clamp(_zoom, 0.0f, 10.0f);
//Create view matrix
_transform = Matrix.CreateScale(new Vector3(_zoom, _zoom, 1)) *
Matrix.CreateTranslation(player.Pos.X, player.Pos.Y, 0);
//Update inverse matrix
_inverseTransform = Matrix.Invert(_transform);
}