Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: unload sounds when fast refreshing on Android #13

Merged
merged 2 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions android/src/main/cpp/AudioConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,4 @@ struct LoadSoundResult {
std::optional<std::string> error;
};

struct UnloadSoundResult {
std::optional<std::string> error;
};

#endif //AUDIOPLAYBACK_AUDIOCONSTANTS_H
16 changes: 9 additions & 7 deletions android/src/main/cpp/AudioEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,15 @@ LoadSoundResult AudioEngine::loadSound(int fd, int offset, int length) {
return {.id = id, .error = std::nullopt};
}

UnloadSoundResult AudioEngine::unloadSound(const std::string &playerId) {
auto it = mPlayers.find(playerId);
if(it != mPlayers.end()) {
mPlayers.erase(it);
void AudioEngine::unloadSounds(const std::optional<std::vector<std::string>> &ids) {
if(ids.has_value()) {
for (const auto & id: ids.value()) {
auto player = mPlayers.find(id);
if(player != mPlayers.end()) {
mPlayers.erase(player);
}
}
} else {
return {.error = "Audio file could not be unloaded because it is not found"};
mPlayers.clear();
}

return {.error = std::nullopt};
}
2 changes: 1 addition & 1 deletion android/src/main/cpp/AudioEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AudioEngine : public oboe::AudioStreamDataCallback{
void seekSoundsTo(const std::vector<std::pair<std::string, double>>&);
void setSoundsVolume(const std::vector<std::pair<std::string, double>>&);
LoadSoundResult loadSound(int fd, int offset, int length);
UnloadSoundResult unloadSound(const std::string &playerId);
void unloadSounds(const std::optional<std::vector<std::string>>&);

oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override;

Expand Down
4 changes: 1 addition & 3 deletions android/src/main/cpp/audio/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ class Player : public IRenderableAudio{

public:
/**
* Construct a new Player from the given DataSource. Players can share the same data source.
* For example, you could play two identical sounds concurrently by creating 2 Players with the
* same data source.
* Construct a new Player from the given DataSource.
*
* @param source
*/
Expand Down
36 changes: 22 additions & 14 deletions android/src/main/cpp/native-lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ std::string jstringToStdString(JNIEnv* env, jstring jStr) {
return str;
}

std::vector<std::string> jniStringArrayToStringVector(JNIEnv* env, jobjectArray stringArray) {
std::vector<std::string> cppVector{};
jsize length = env->GetArrayLength(stringArray);

for(jsize i = 0; i < length; ++i) {
auto javaString = (jstring)env->GetObjectArrayElement(stringArray, i);
const char* chars = env->GetStringUTFChars(javaString, nullptr);
cppVector.emplace_back(chars);
env->ReleaseStringUTFChars(javaString, chars);
env->DeleteLocalRef(javaString);
}

return cppVector;
}

extern "C" {
JNIEXPORT jobject JNICALL
Java_com_audioplayback_AudioPlaybackModule_setupAudioStreamNative(JNIEnv *env, jobject thiz, jdouble sample_rate, jdouble channel_count) {
Expand Down Expand Up @@ -136,21 +151,14 @@ Java_com_audioplayback_AudioPlaybackModule_closeAudioStreamNative(JNIEnv *env, j
return returnValue;
}

JNIEXPORT jobject JNICALL
Java_com_audioplayback_AudioPlaybackModule_unloadSoundNative(JNIEnv *env, jobject instance, jstring playerId) {
auto result = audioEngine->unloadSound(jstringToStdString(env, playerId));

jclass structClass = env->FindClass("com/audioplayback/models/UnloadSoundResult");
jmethodID constructor = env->GetMethodID(structClass, "<init>", "(Ljava/lang/String;)V");

jstring jError = result.error.has_value() ? env->NewStringUTF(result.error->c_str()): nullptr;
jobject returnValue = env->NewObject(structClass, constructor, jError);

if(jError) {
env->DeleteLocalRef(jError);
JNIEXPORT void JNICALL
Java_com_audioplayback_AudioPlaybackModule_unloadSoundsNative(JNIEnv *env, jobject thiz,
jobjectArray ids) {
if(ids == nullptr) {
audioEngine->unloadSounds(std::nullopt);
} else {
audioEngine->unloadSounds(jniStringArrayToStringVector(env, ids));
}

return returnValue;
}


Expand Down
13 changes: 5 additions & 8 deletions android/src/main/java/com/audioplayback/AudioPlaybackModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import com.audioplayback.models.LoadSoundResult
import com.audioplayback.models.OpenAudioStreamResult
import com.audioplayback.models.PauseAudioStreamResult
import com.audioplayback.models.SetupAudioStreamResult
import com.audioplayback.models.UnloadSoundResult
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -88,12 +87,9 @@ class AudioPlaybackModule internal constructor(context: ReactApplicationContext)
}


@ReactMethod(isBlockingSynchronousMethod = true)
override fun unloadSound(id: String): WritableMap {
val result = unloadSoundNative(id)
val map = Arguments.createMap()
result.error?.let { map.putString("error", it) } ?: map.putNull("error")
return map
@ReactMethod
override fun unloadSound(id: String) {
unloadSoundsNative(arrayOf(id))
}

@ReactMethod
Expand Down Expand Up @@ -186,6 +182,7 @@ class AudioPlaybackModule internal constructor(context: ReactApplicationContext)
override fun invalidate() {
super.invalidate()
closeAudioStreamNative()
unloadSoundsNative(null)
}

private external fun setupAudioStreamNative(sampleRate: Double, channelCount: Double): SetupAudioStreamResult
Expand All @@ -197,7 +194,7 @@ class AudioPlaybackModule internal constructor(context: ReactApplicationContext)
private external fun seekSoundsToNative(ids: Array<String>, values: DoubleArray)
private external fun setSoundsVolumeNative(ids: Array<String>, values: DoubleArray)
private external fun loadSoundNative(fd: Int, fileLength: Int, fileOffset: Int): LoadSoundResult
private external fun unloadSoundNative(playerId: String): UnloadSoundResult
private external fun unloadSoundsNative(ids: Array<String>?)

// Example method
// See https://reactnative.dev/docs/native-modules-android
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ data class OpenAudioStreamResult(val error: String?)
data class PauseAudioStreamResult(val error: String?)
data class CloseAudioStreamResult(val error: String?)
data class LoadSoundResult(val error: String?, val id: String?)
data class UnloadSoundResult(val error: String?)
2 changes: 1 addition & 1 deletion android/src/oldarch/AudioPlaybackSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ abstract class AudioPlaybackSpec internal constructor(context: ReactApplicationC

abstract fun setSoundsVolume(arg: ReadableArray)

abstract fun unloadSound(id: String): WritableMap
abstract fun unloadSound(id: String)

abstract fun loadSound(uri: String, promise: Promise)
}
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-audio-playback (0.2.0-alpha.4):
- react-native-audio-playback (1.0.0):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -1818,7 +1818,7 @@ SPEC CHECKSUMS:
React-logger: addd140841248966c2547eb94836399cc1061f4d
React-Mapbuffer: 029b5332e78af8c67c4b5e65edfc717068b8eac1
React-microtasksnativemodule: f30949ee318ba90b9668de1e325b98838b9a4da2
react-native-audio-playback: 9928a4f29998a3d98f1ee63e8054bf23c97ff86b
react-native-audio-playback: 2e227d97930550dc443ae76be9a07b25f51c469e
react-native-slider: d1a9121980fc81678c6d30b82f312c778fba563c
React-nativeconfig: 470fce6d871c02dc5eff250a362d56391b7f52d6
React-NativeModulesApple: 1586448c61a7c2bd4040cc03ccde66a72037e77e
Expand Down
2 changes: 1 addition & 1 deletion src/NativeAudioPlayback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface Spec extends TurboModule {
playSounds: (arg: Array<[string, boolean]>) => void;
seekSoundsTo: (arg: Array<[string, number]>) => void;
setSoundsVolume: (arg: Array<[string, number]>) => void;
unloadSound: (id: string) => { error: string | null };
unloadSound: (id: string) => void;
loadSound: (
uri: string
) => Promise<{ id: string | null; error: string | null }>;
Expand Down
5 changes: 1 addition & 4 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,5 @@ export async function loadSound(requiredAsset: number): Promise<string> {
}

export function unloadSound(playerId: string) {
const res = AudioPlayback.unloadSound(playerId);
if (res.error) {
throw new Error(res.error);
}
AudioPlayback.unloadSound(playerId);
}
Loading