Skip to content
Open
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
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip
2 changes: 1 addition & 1 deletion mwengine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ set(target mwengine)
# architecture-specific compiler flags

if (${ANDROID_ABI} MATCHES "x86_64")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=x86-64 -msse4.2 -mpopcnt -m64")
endif()

# source folders
Expand Down
7 changes: 4 additions & 3 deletions mwengine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ buildscript {
maven { url 'https://jitpack.io' }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.3'
classpath 'com.android.tools.build:gradle:8.2.1'
}
}

android {
compileSdkVersion 32
namespace 'nl.igorski.mwengine'
compileSdk 33

defaultConfig {
// NOTE: the value of minSdkVersion is used by the native build as the target APP_PLATFORM
Expand All @@ -22,7 +23,7 @@ android {
// thus set the value of minSdkVersion to whatever fits your use case.

minSdkVersion 16
targetSdkVersion 32
targetSdkVersion 33

ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
Expand Down
3 changes: 1 addition & 2 deletions mwengine/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nl.igorski.mwengine">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-feature android:name="android.hardware.audio.output" />

Expand Down
14 changes: 13 additions & 1 deletion mwengine/src/main/cpp/audioengine.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2022 Igor Zinken - https://www.igorski.nl
* Copyright (c) 2013-2024 Igor Zinken - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand Down Expand Up @@ -77,6 +77,7 @@ namespace MWEngine {
int AudioEngine::time_sig_beat_unit = 4;
int AudioEngine::queuedTime_sig_beat_amount = time_sig_beat_amount;
int AudioEngine::queuedTime_sig_beat_unit = time_sig_beat_unit;
bool AudioEngine::broadcast_idle = false;

/* buffer read/write pointers */

Expand Down Expand Up @@ -745,6 +746,12 @@ namespace MWEngine {
if ( recordingState.bouncing && AudioEngineProps::isRendering.load() && DriverAdapter::isAAudio() ) {
render( amountOfSamples );
}

if ( broadcast_idle ) {
broadcast_idle = false;
Notifier::broadcast( Notifications::ENGINE_IDLE );
}

return AudioEngineProps::isRendering.load();
}

Expand All @@ -762,6 +769,11 @@ namespace MWEngine {
#endif
}

void AudioEngine::notifyWhenIdle()
{
broadcast_idle = true;
}

void AudioEngine::handleTempoUpdate( float aQueuedTempo, bool broadcastUpdate )
{
float ratio = 1;
Expand Down
5 changes: 4 additions & 1 deletion mwengine/src/main/cpp/audioengine.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2022 Igor Zinken - https://www.igorski.nl
* Copyright (c) 2013-2024 Igor Zinken - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand Down Expand Up @@ -49,6 +49,7 @@ class AudioEngine
static void start( Drivers::types audioDriver );
static void stop();
static void reset();
static void notifyWhenIdle(); // request to be notified when engine reaches idle state after render (fires once per request)

static AudioChannel* getInputChannel();

Expand Down Expand Up @@ -143,6 +144,8 @@ class AudioEngine
static int min_step_position; // the lowest step in the current sequence
static int max_step_position; // the maximum step in the current sequence (e.g. 15 for single measure using a 16 step sequencer - step starts at 0.)

static bool broadcast_idle; // whether to broadcast a notification when the engine is entering idle phase after render

/* buffer read/write pointers */

static int bufferPosition; // the current sequence position in samples ("playback head" offset)
Expand Down
3 changes: 2 additions & 1 deletion mwengine/src/main/cpp/definitions/notifications.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2015-2018 Igor Zinken - http://www.igorski.nl
* Copyright (c) 2015-2024 Igor Zinken - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand Down Expand Up @@ -52,6 +52,7 @@ class Notifications
/* system messages */

STATUS_BRIDGE_CONNECTED, // JNI bridge connected
ENGINE_IDLE, // engine is currently in idle state (between render cycles), this is the time to safely clean up resources

/* fatal errors */

Expand Down
17 changes: 15 additions & 2 deletions mwengine/src/main/cpp/events/baseaudioevent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,21 @@ BaseAudioEvent::~BaseAudioEvent()

void BaseAudioEvent::dispose()
{
removeFromSequencer();
destroyBuffer();
_disposed = true; // set the disposed flag as removal can be async when sequencer is running
_enabled = false;

if ( _instrument != nullptr && isSequenced ) {
_instrument->removeEvent( this, false );
} else {
stop();
}
}

void BaseAudioEvent::onRemove()
{
if ( _disposed ) {
destroyBuffer(); // if event was removed as part of disposal routine, clean the buffer now
}
}

BaseInstrument* BaseAudioEvent::getInstrument()
Expand Down
4 changes: 3 additions & 1 deletion mwengine/src/main/cpp/events/baseaudioevent.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class BaseAudioEvent
BaseAudioEvent();
virtual ~BaseAudioEvent();

void dispose(); // invoke when removing the Event from the engine
void dispose(); // invoke when removing the Event from the engine, should be done when ENGINE_IDLE is broadcast!
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or when Sequencer isn't running

void onRemove(); // invoked by the Instrument once the event has safely been removed

/**
* event volume is in a percentile (0 - 1) range
Expand Down Expand Up @@ -213,6 +214,7 @@ class BaseAudioEvent
bool _removalEnqueued;
bool _locked;
bool _updateAfterUnlock; // use in update-methods when checking for lock
bool _disposed = false;

// _destroyableBuffer indicates we can delete the buffer on destruction (true by default and
// implies that this AudioEvent holds the only reference to its buffers
Expand Down
3 changes: 3 additions & 0 deletions mwengine/src/main/cpp/instruments/baseinstrument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ bool BaseInstrument::removeEvent( BaseAudioEvent* audioEvent, bool isLiveEvent )
removeEventFromMeasureCache( audioEvent );
}
}
if ( removed ) {
audioEvent->onRemove();
}
return removed;
}

Expand Down
8 changes: 4 additions & 4 deletions mwengine/src/main/cpp/sequencer.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2022 Igor Zinken - https://www.igorski.nl
* Copyright (c) 2013-2024 Igor Zinken - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand Down Expand Up @@ -45,6 +45,7 @@ int Sequencer::registerInstrument( BaseInstrument* instrument )
for ( auto const & i : instruments ) {
if ( i == instrument ) {
wasPresent = true;
break;
}
}

Expand Down Expand Up @@ -112,9 +113,9 @@ bool Sequencer::getAudioEvents( std::vector<AudioChannel*>* channels, int buffer
}
}

if ( addLiveInstruments && instrument->hasLiveEvents() )
if ( addLiveInstruments && instrument->hasLiveEvents() ) {
collectLiveEvents( instrument );

}
channels->push_back( instrumentChannel );
}
}
Expand All @@ -133,7 +134,6 @@ void Sequencer::collectSequencedEvents( BaseInstrument* instrument, int bufferPo
if ( !instrument->hasEvents() ) {
return;
}

AudioChannel* channel = instrument->audioChannel;

auto audioEvents = instrument->getEventsForMeasure( measure );
Expand Down
7 changes: 6 additions & 1 deletion mwengine/src/main/cpp/sequencercontroller.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2022 Igor Zinken - https://www.igorski.nl
* Copyright (c) 2013-2024 Igor Zinken - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand Down Expand Up @@ -86,6 +86,11 @@ void SequencerController::setVolume( float aVolume )
AudioEngine::volume = VolumeUtil::toLog( aVolume );
}

bool SequencerController::getPlaying()
{
return Sequencer::playing;
}

void SequencerController::setPlaying( bool aIsPlaying )
{
Sequencer::playing = aIsPlaying;
Expand Down
3 changes: 2 additions & 1 deletion mwengine/src/main/cpp/sequencercontroller.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2022 Igor Zinken - http://www.igorski.nl
* Copyright (c) 2013-2024 Igor Zinken - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand Down Expand Up @@ -44,6 +44,7 @@ class SequencerController
void setTempo ( float aTempo, int aTimeSigBeatAmount, int aTimeSigBeatUnit );
void setTempoNow( float aTempo, int aTimeSigBeatAmount, int aTimeSigBeatUnit );
void setVolume ( float aVolume );
bool getPlaying ();
void setPlaying ( bool aPlaying );

/**
Expand Down
38 changes: 36 additions & 2 deletions mwengine/src/main/java/nl/igorski/mwengine/MWEngine.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2022 Igor Zinken - https://www.igorski.nl
* Copyright (c) 2013-2024 Igor Zinken - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand Down Expand Up @@ -34,6 +34,10 @@
import android.util.Log;
import android.view.WindowManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import nl.igorski.mwengine.core.*;

public final class MWEngine
Expand Down Expand Up @@ -77,6 +81,10 @@ public interface IObserver
void handleNotification( int aNotificationId, int aNotificationValue );
}

public interface VoidInterface {
public void operation();
}

private static MWEngine INSTANCE; // we only allow a single instance to be constructed for resource optimization
private IObserver _observer;

Expand All @@ -96,6 +104,8 @@ public interface IObserver

private boolean _initialCreation = true;
private boolean _isRunning = false;
private List<VoidInterface> _idleCallbacks = new ArrayList( Collections.emptyList());
private static int ENGINE_IDLE_NOTIFICATION = Notifications.ids.ENGINE_IDLE.ordinal();

/**
* The Java-side bridge to manage all native layer components
Expand Down Expand Up @@ -373,6 +383,20 @@ public void saveRecordedSnippet( int snippetBufferIndex ) {
AudioEngine.saveRecordedSnippet( snippetBufferIndex );
}

/**
* Delay execution of provided callback until the engine is idle, e.g.
* between render cycles. This can be used to safely dispose() of sequenced AudioEvents
* and deallocate referenced resources while the Sequencer is running.
*/
public void executeWhenIdle( VoidInterface callback ) {
if ( _sequencerController.getPlaying()) {
_idleCallbacks.add( callback );
AudioEngine.notifyWhenIdle();
} else {
callback.operation();
}
}

public void reset() {
AudioEngine.reset();
}
Expand Down Expand Up @@ -465,7 +489,17 @@ public static void handleBridgeConnected( int aSomething ) {
}

public static void handleNotification( int aNotificationId ) {
if ( INSTANCE != null && INSTANCE._observer != null ) INSTANCE._observer.handleNotification( aNotificationId );
if ( INSTANCE != null ) {
if ( ENGINE_IDLE_NOTIFICATION == aNotificationId ) {
for ( VoidInterface callback : INSTANCE._idleCallbacks ) {
callback.operation();
}
INSTANCE._idleCallbacks.clear();
}
if ( INSTANCE._observer != null ) {
INSTANCE._observer.handleNotification( aNotificationId );
}
}
}

public static void handleNotificationWithData( int aNotificationId, int aNotificationData ) {
Expand Down
7 changes: 4 additions & 3 deletions mwengine_example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ buildscript {
maven { url 'https://jitpack.io' }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.3'
classpath 'com.android.tools.build:gradle:8.2.1'
}
}

android {
compileSdkVersion 32
namespace 'nl.igorski.mwengine.example'
compileSdk 33

defaultConfig {
// the sdk version range below matches the one of the library. You can change these as you see fit
Expand All @@ -34,7 +35,7 @@ android {
// thus set the value of minSdkVersion to whatever fits your use case.

minSdkVersion 16
targetSdkVersion 32
targetSdkVersion 33
versionCode 1
versionName "1.0.0"

Expand Down
3 changes: 1 addition & 2 deletions mwengine_example/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nl.igorski.mwengine.example">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<!-- required audio related permissions are provided by manifest inside MWEngine library -->

Expand Down
Loading