mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 08:44:04 +02:00
Add basic testing app for Android.
This is only very basic right now. Will be expanded on later.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# For more information about using CMake with Android Studio, read the
|
||||
# documentation: https://d.android.com/studio/projects/add-native-code.html.
|
||||
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
|
||||
|
||||
# Sets the minimum CMake version required for this project.
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
|
||||
# Since this is the top level CMakeLists.txt, the project name is also accessible
|
||||
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
|
||||
# build script scope).
|
||||
project("miniaudiotester")
|
||||
|
||||
# Creates and names a library, sets it as either STATIC
|
||||
# or SHARED, and provides the relative paths to its source code.
|
||||
# You can define multiple libraries, and CMake builds them for you.
|
||||
# Gradle automatically packages shared libraries with your APK.
|
||||
#
|
||||
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
|
||||
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
|
||||
# is preferred for the same purpose.
|
||||
#
|
||||
# In order to load a library into your app from Java/Kotlin, you must call
|
||||
# System.loadLibrary() and pass the name of the library defined here;
|
||||
# for GameActivity/NativeActivity derived applications, the same library name must be
|
||||
# used in the AndroidManifest.xml file.
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED
|
||||
# List C/C++ source files with relative paths to this CMakeLists.txt.
|
||||
native-lib.cpp)
|
||||
|
||||
# Specifies libraries CMake should link to your target library. You
|
||||
# can link libraries from various origins, such as libraries defined in this
|
||||
# build script, prebuilt third-party libraries, or Android system libraries.
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
# List libraries link to the target library
|
||||
android
|
||||
log)
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
#define MA_DEBUG_OUTPUT
|
||||
#include "../../../../../../../miniaudio.c" /* Android projects have very deep folder structures... */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BACKEND_AUTO,
|
||||
BACKEND_AAUDIO,
|
||||
BACKEND_OPENSL
|
||||
} backend_choice_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_device device;
|
||||
ma_waveform waveform;
|
||||
bool hasDevice;
|
||||
bool hasError; /* Will be set to true if something went wrong. */
|
||||
std::string errorMessage; /* Will be an empty string if there is no error message. */
|
||||
} audio_state_t;
|
||||
|
||||
static void audio_state_set_error(audio_state_t* pAudioState, const char* pMessage)
|
||||
{
|
||||
assert(pAudioState != nullptr);
|
||||
|
||||
pAudioState->hasError = true;
|
||||
pAudioState->errorMessage = pMessage;
|
||||
}
|
||||
|
||||
|
||||
static void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
|
||||
{
|
||||
auto* pAudioState = (audio_state_t*)pDevice->pUserData;
|
||||
assert(pAudioState != nullptr);
|
||||
|
||||
ma_waveform_read_pcm_frames(&pAudioState->waveform, pFramesOut, frameCount, nullptr);
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_io_miniaud_miniaudiotester_MainActivity_UninitializeAudio(JNIEnv *env, jobject, jlong audioState)
|
||||
{
|
||||
auto* pAudioState = (audio_state_t*)audioState;
|
||||
if (pAudioState == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pAudioState->hasDevice) {
|
||||
ma_device_uninit(&pAudioState->device);
|
||||
ma_waveform_uninit(&pAudioState->waveform);
|
||||
pAudioState->hasDevice = false;
|
||||
}
|
||||
|
||||
pAudioState->hasError = false;
|
||||
pAudioState->errorMessage = "";
|
||||
|
||||
return (jlong)pAudioState;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_io_miniaud_miniaudiotester_MainActivity_PlayAudio(JNIEnv *env, jobject, jlong audioState, int backend)
|
||||
{
|
||||
auto* pAudioState = (audio_state_t*)audioState;
|
||||
ma_result result;
|
||||
|
||||
if (pAudioState == nullptr) {
|
||||
pAudioState = new audio_state_t;
|
||||
pAudioState->hasDevice = false;
|
||||
pAudioState->hasError = false;
|
||||
}
|
||||
|
||||
/* If we don't have a device, create one. */
|
||||
if (!pAudioState->hasDevice) {
|
||||
ma_context_config contextConfig = ma_context_config_init();
|
||||
ma_backend pBackends[1];
|
||||
size_t backendCount;
|
||||
|
||||
if (backend == BACKEND_AUTO) {
|
||||
backendCount = 0;
|
||||
} else {
|
||||
backendCount = 1;
|
||||
if (backend == BACKEND_AAUDIO) {
|
||||
pBackends[0] = ma_backend_aaudio;
|
||||
} else if (backend == BACKEND_OPENSL) {
|
||||
pBackends[0] = ma_backend_opensl;
|
||||
} else {
|
||||
backendCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
|
||||
deviceConfig.dataCallback = data_callback;
|
||||
deviceConfig.pUserData = pAudioState;
|
||||
|
||||
result = ma_device_init_ex((backendCount == 0) ? nullptr : pBackends, backendCount, &contextConfig, &deviceConfig, &pAudioState->device);
|
||||
if (result != MA_SUCCESS) {
|
||||
audio_state_set_error(pAudioState, (std::string("Failed to initialize device. ") + ma_result_description(result)).c_str());
|
||||
pAudioState->hasDevice = false;
|
||||
}
|
||||
|
||||
/* Before starting the device we will need a waveform object. This should never fail to initialize. */
|
||||
ma_waveform_config waveformConfig = ma_waveform_config_init(pAudioState->device.playback.format, pAudioState->device.playback.channels, pAudioState->device.sampleRate, ma_waveform_type_sine, 0.2, 400);
|
||||
ma_waveform_init(&waveformConfig, &pAudioState->waveform);
|
||||
|
||||
pAudioState->hasDevice = true;
|
||||
}
|
||||
|
||||
/* At this point we should have a device. Start it. */
|
||||
result = ma_device_start(&pAudioState->device);
|
||||
if (result != MA_SUCCESS) {
|
||||
audio_state_set_error(pAudioState, (std::string("Failed to start device. ") + ma_result_description(result)).c_str());
|
||||
}
|
||||
|
||||
return (jlong)pAudioState;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_io_miniaud_miniaudiotester_MainActivity_PauseAudio(JNIEnv *env, jobject, jlong audioState)
|
||||
{
|
||||
auto* pAudioState = (audio_state_t*)audioState;
|
||||
if (pAudioState == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!pAudioState->hasError) {
|
||||
if (pAudioState->hasDevice) {
|
||||
ma_result result = ma_device_stop(&pAudioState->device);
|
||||
if (result != MA_SUCCESS) {
|
||||
audio_state_set_error(pAudioState, ma_result_description(result));
|
||||
}
|
||||
} else {
|
||||
audio_state_set_error(pAudioState, "Trying to pause audio, but there is no device.");
|
||||
}
|
||||
}
|
||||
|
||||
return (jlong)pAudioState;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_io_miniaud_miniaudiotester_MainActivity_HasAudioError(JNIEnv *env, jobject, jlong audioState)
|
||||
{
|
||||
auto* pAudioState = (audio_state_t*)audioState;
|
||||
if (pAudioState == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return pAudioState->hasError;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_io_miniaud_miniaudiotester_MainActivity_GetAudioError(JNIEnv *env, jobject, jlong audioState)
|
||||
{
|
||||
auto* pAudioState = (audio_state_t*)audioState;
|
||||
if (pAudioState == nullptr) {
|
||||
return env->NewStringUTF("Out of memory");
|
||||
}
|
||||
|
||||
return env->NewStringUTF(pAudioState->errorMessage.c_str());
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_miniaud_miniaudiotester_MainActivity_DeleteAudioState(JNIEnv *env, jobject thiz, jlong audioState)
|
||||
{
|
||||
Java_io_miniaud_miniaudiotester_MainActivity_UninitializeAudio(env, thiz, audioState);
|
||||
delete (audio_state_t*)audioState;
|
||||
}
|
||||
Reference in New Issue
Block a user