Code Monkey home page Code Monkey logo

mwengine's Introduction

MWEngine is..

...an audio engine for Android, compatible with Android 4.1 and up, using either OpenSL or AAudio (when available) as the drivers for low latency audio performance.

MWEngine provides an architecture that allows you to work with audio within a musical context. It is easy to build upon the base classes and create your own noise generating mayhem. A few keywords describing the out-of-the-box possibilities are:

  • tempo-based sequencing with support for alternative time signatures
  • on-the-fly audio synthesis
  • multi-channel audio output
  • effect chains operating on individual input/output channels
  • sample playback with real time pitch shifting
  • live recording and processing from your device's inputs (e.g. microphone)
  • bouncing output to WAV files, either live (during a performance) or "offline"

How does this relate/compare to Google Oboe ?

Though MWEngine was initially created before Oboe, its underlying audio drivers are the same as Google Oboe uses, MWEngine and Oboe are merely different abstraction layers to solve the same problem.

Additionally, MWEngine provides a complete audio sequencing and processing environment with built-in effects without you needing to write/know C(++) to use it.

What apps are using MWEngine ?

The engine has been written for both MikroWave and Kosm to provide fast live audio synthesis.

While development on aforementioned apps has (practically) been discontinued, the engine itself has over the years been continuously updated to be of use to third party app developers, such as TIZE - Beat Maker, Music Maker and Another Flamenco Compás App.

C++ ??? What about Java / Kotlin ?

Though the library is written in C++ (and can be used solely within this context), the library can be built using JNI (Java Native Interface) which makes its API expose itself to Java / Kotlin, while still executing in a native layer outside of the JVM. In other words : high performance of the engine is ensured by the native layer operations, while ease of development is ensured by delegating application logic / UI to the realm of the Android Java SDK.

Whether you intend to use MWEngine for its sample based playback or to leverage its built-in synthesizer and audio processing, you are not required to write any additional C++ code. If you however intend to create your own DSP or synthesis routines (which is fun to do!) you must write them in C++, but can rely on SWIG for making them usable in Java.

A note on garbage collection and SWIG

It is important to note that when a Java object finalizes (i.e. all its references are broken and is garbage collected), the destructors of the native objects are invoked, which can lead to unpredictable results if you happen to overlook this! As such, audio engine objects such as effects processors or events that are created on the Java side, must hold strong references during their lifecycle.

The Issue Tracker is your point of contact

Bug reports, feature requests, questions and discussions are welcome on the GitHub Issue Tracker, please do not send e-mails through the development website. Please search before posting to avoid duplicates and limit to one issue per post. For usage / implementation related questions, first consult the MWEngine Wiki.

When reporting a bug, please describe the expected behaviour and the actual result. When possible, for crashes attach stack traces and recordings for audio related glitches.

Vote on existing feature requests by using the Thumbs Up/Down reaction on the first post.

Development setup

You will need the following development kits:

And the following toolchains:

  • Gradle to run all build commands
  • CMake to build the native layer code
  • SWIG to wrap the native layer code in Java classes

If you use Android Studio you can simply open the project folder and sync the project with the build.gradle file, after which you will be prompted in case you need to install any of the above (as Android Studio can resolve and install the dependencies for you).

Though depending on your platform, you might need to install SWIG manually (as well as adding it to your path variables). SWIG is readily available from most package managers such as brew on macOS or apt-get on Linux).

Build configurations

This repository contains two modules:

  • /mwengine/ which is the core MWEngine library (native code, Java API wrappers)
  • /mwengine_example/ which contains an example Activity bundling the library into an Android app

Both are referenced in the root level settings.gradle file for a standard multi module setup.

The build configurations themselves are defined in:

  • build.gradle (the usual setup, build and deploy toolchain for Android, per module)
  • CMakeLists.txt (for the native layer code, solely in /mwengine module)

If you are uncomfortable with C development, you can ignore the makefile as all build commands are executed through Gradle.

Building the project

Upon checkout this repository does not include the Java API files (nl.igorski.mwengine.core-namespace) as these are wrappers and generated by the build. In order to generate these files, you should run the assemble task of the mwengine-module, e.g.:

:mwengine:assemble.

Upon completion, you can run usual debug/deploy targets for the mwengine_example-module to start the example application. Using Android Studio you can easily debug native code of the library inside the example Activity using breakpoints.

Building MWEngine as a standalone AAR library

While you could create your own custom app by cloning this repository and refactoring the example Activity to your needs, it will be far easier to maintain and include future MWEngine updates when you build the core MWEngine library as an Android Archive (.AAR) and reference the library within your project.

In order to do so, you run the following Gradle command:

:mwengine:assemble

Which will generate the library in both debug and release configurations, packaged as AAR files in: ./mwengine/build/outputs/aar/.

Importing the MWEngine AAR library inside your custom project

Within Android Studio you can easily do this by importing the generated .aar file by navigating through:

File > Project structure > Dependencies

Add a new Library Dependency in the Declared dependencies tab, select Jar Dependency In the Add Jar/Aar Dependency dialog, enter the path to your built AAR library /path/to/mwengine-release.aar

Your projects build.gradle file will now contain the following line:

implementation files('/path/to/mwengine-release.aar')

In the build.gradle for your application, be sure to add the following entries under the defaultConfig and dependencies sections:

android {
    defaultConfig {
        // project specific custom stuff here...
        ndk {
            // these values must match the ABI's defined in mwengine/build.gradle
            // to prevent UnsatisfiedLinkError when this app tries to serve an unsupported architecture
            abiFilters "armeabi-v7a", "arm64-v8a", "x86_64"
        }
    }
}
dependencies {
    implementation project(":mwengine-release")
    // project specific custom stuff here...
}

FAQ / Troubleshooting

The contents of this repository should result in a stable library and example application. If you experience issues with the setup, consult the Troubleshooting Wiki page.

Documentation

You can view the Wiki (which documents all of the engine's actors as well as a variety of real world use cases) here:

https://github.com/igorski/MWEngine/wiki

Note that you can also view the contents of the header files to get more details about the inner workings of each class.

Unit tests

The library comes with unit tests (/src/main/cpp/tests/), written using the Googletest C++ testing framework.

To enable unit testing upon each build / during development:

  • update local.properties to include the line: enable_tests=true

Note: adb must be available in your global path settings and the attached device / emulator must have the x86_64 CPU architecture (see CMakeLists.txt).

Demo

The repository contains an example Activity that is ready to deploy onto any Android device/emulator supporting ARM-, ARMv7-, x86- architecture and running Android 4.1 or higher. The example will demonstrate how to quickly get a musical sequence going using the library.

To install the demo: first build the library as described above, and then run the build script to deploy the .APK onto an attached device/emulator (note that older emulated devices can only operate at a sample rate of 8 kHz!).

Contributors

MWEngine has received welcome contributions (either suggestions on improving the API or proposal of new features, solving of bugs, etc.) from the following developers :

  • Andrey Stavrinov (@hypeastrum)
  • Toufik Zitouni & Robert Avellar (Tize)
  • Koert Gaaikema (@koertgaaikema)
  • Matt Logan (@mattlogan)
  • Thomas Flasche (@harthorst)
  • Rickard Östergård (@rckrdstrgrd)
  • Aran Arunakiri

mwengine's People

Contributors

igorski avatar robtize avatar teotigraphix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mwengine's Issues

Sometime sound is distorted at the beginning of the loop

I have a sequence of sample events running in an infinite loop. Sometimes I hear a distortion when the loop returns to the beginning. Sometimes not. This happens randomly. I don't change speed nor do I do anything else. It's just the engine running.

Do you have any hints about that? I tried the limiter but it does not help here.

Position notification improvements

  1. When sequencer started, first notification will be about position 1. So, in demo app position changed from 1 to 15, and then from 0 to 15. If I need some events to sync with audio, notifications about 0 position at start simplifies client code.

  2. If I need to sync with audio with best precision, I need also position start offset in buffer. I've implemented this feature in my fork (songsterr@86ad204). We can talk about it. Now API is not so good, but can be improved in java part of lib.

Tips for improving stability when playing audio in background app

Hello,

I'm working on an Android metronome app using SampleEvents and the SequencerController. It's working quite well when the Android app is in the foreground, but when the app goes to the background the stability of the audio stream suffers a bit. The tempo will vary a bit at times, and some notes get dropped. This gets much worse when performing CPU-intensive actions in another app, like scrolling around on in Google Maps for example.

I think I've noticed some improvements by doing two things so far:

  1. Increasing the buffer size, up to 24 times the value returned by getRecommendedBufferSize
  2. Using smaller audio samples

Are there any other things you can recommended to improve audio stability, especially when MWEngine is running in a background Android app?

Thank you!

Unit Tests - Running / Develop

Hey,

I use Windows for dev, is there any advice you can give for a setup? I am trying to figure out how to use Android Studio as the IDE with code completion/error checking and run a single test or the whole suite.

  • Do you just use a text editor and "run" in place?
  • How do you run 1 test at a time when you are doing TDD (mind you I do this but not as religion)?
  • I have tried to understand how AS works with NDK testing but it doesn't seem to jive with what you have going.

Sequencer accidentally stopped after engine stop and start again

setPlaying( false );

If stop the engine and dispose it in java (for example on activity stop), destructor of SequencerController will be called later with GC. So if we stop the activity and the open it again, next scenario became possible:

  1. Run audio engine
  2. Stop activity, stop and dispose audio engine
  3. Resume activity, init and start audio engine again
  4. Play sound
  5. SequencerController destructor called (for old object), but it affects global audio engine namespace and sound will be stopped

Loading wav files.

Hi,

I'm working on a small Android project, and this engine seems to fit my needs !

I've been playing with it and after some changes I was able to compile it.

Now I'm trying to load some wav files to simply loop / sequence. But I can't get it done somehow.
I couldn't load them from the assets or res or local filesystem of my phone.

Is there a more hands on example that I can follow, or can someone point me in the correct direction ?

Koert.

AudioChannel mixVolume issues

the mixVolume property of the AudioChannel should allow its level to be balanced against all other channels. The property however, seems to do nothing... (for now instrument volumes can be usedto control channel volume).

Oboe integration

Was wondering now that it's production ready if you were going to integrate it.

I have a couple demos I have compiling that is working with the stream and callbacks.

I wouldn't know how to get it in this project correctly at this point and time of my learning. :)

example app crashes when screen is rotated

I run the example app, started the sequencer and rotated the screen.

Logcat output:

09-29 21:02:29.943 24187 24214 D MWENGINE: seq. position: 11, buffer offset: 216, elapsed samples: 66456
09-29 21:02:29.964 24187 24214 E libOpenSLES: frameworks/wilhelm/src/itf/IBufferQueue.c:57: pthread_mutex_lock_timeout_np returned 110
09-29 21:02:29.975 24187 24187 D MWENGINE: initing MWEngineActivity
09-29 21:02:29.975 24187 24187 D AndroidRuntime: Shutting down VM
09-29 21:02:29.976 24187 24187 E AndroidRuntime: FATAL EXCEPTION: main
09-29 21:02:29.976 24187 24187 E AndroidRuntime: Process: nl.igorski.example, PID: 24187
09-29 21:02:29.976 24187 24187 E AndroidRuntime: java.lang.Error: MWEngine already instantiated
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at nl.igorski.lib.audio.MWEngine.(MWEngine.java:110)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at nl.igorski.example.MWEngineActivity.init(MWEngineActivity.java:80)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at nl.igorski.example.MWEngineActivity.onCreate(MWEngineActivity.java:68)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:6285)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2417)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2524)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4176)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.app.ActivityThread.access$1000(ActivityThread.java:154)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.os.Looper.loop(Looper.java:234)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:5526)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
09-29 21:02:29.976 24187 24187 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
09-29 21:02:29.981 925 3341 D ActivityManager: New dropbox entry: nl.igorski.example, data_app_crash, a024b8ad-0efb-4d27-a227-485d05b4bc49
09-29 21:02:29.984 925 3341 W ActivityManager: Force finishing activity nl.igorski.example/.MWEngineActivity
09-29 21:02:30.009 23810 24256 W ContextImpl: Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1271 android.content.ContextWrapper.bindService:604 com.sonyericsson.crashmonitor.MiscTaAdapter.open:90 com.sonyericsson.crashmonitor.service.CrashMonitorService.onInit:127 com.sonyericsson.crashmonitor.service.CrashMonitorService.onHandleIntent:192
09-29 21:02:30.018 925 2731 I OpenGLRenderer: Initialized EGL, version 1.4
09-29 21:02:30.040 24187 24214 D MWENGINE: seq. position: 12, buffer offset: 954, elapsed samples: 71994
09-29 21:02:30.095 16523 20542 E AsyncOperation: serviceID=40, operation=LogOperation
09-29 21:02:30.095 16523 20542 E AsyncOperation: OperationException[Status{statusCode=Storage full, resolution=null}]
09-29 21:02:30.095 16523 20542 E AsyncOperation: at oqf.a(:com.google.android.gms@[email protected] (040306-211705629):87)
09-29 21:02:30.095 16523 20542 E AsyncOperation: at wwk.run(:com.google.android.gms@[email protected] (040306-211705629):29)
09-29 21:02:30.095 16523 20542 E AsyncOperation: at bdbk.run(:com.google.android.gms@[email protected] (040306-211705629):2)
09-29 21:02:30.095 16523 20542 E AsyncOperation: at qcr.run(:com.google.android.gms@[email protected] (040306-211705629):21)
09-29 21:02:30.095 16523 20542 E AsyncOperation: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
09-29 21:02:30.095 16523 20542 E AsyncOperation: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
09-29 21:02:30.095 16523 20542 E AsyncOperation: at qiv.run(:com.google.android.gms@[email protected] (040306-211705629))
09-29 21:02:30.095 16523 20542 E AsyncOperation: at java.lang.Thread.run(Thread.java:818)
09-29 21:02:30.098 16523 20542 E AsyncOperation: serviceID=40, operation=LogOperation
09-29 21:02:30.098 16523 20542 E AsyncOperation: OperationException[Status{statusCode=Storage full, resolution=null}]
09-29 21:02:30.098 16523 20542 E AsyncOperation: at oqf.a(:com.google.android.gms@[email protected] (040306-211705629):87)
09-29 21:02:30.098 16523 20542 E AsyncOperation: at wwk.run(:com.google.android.gms@[email protected] (040306-211705629):29)
09-29 21:02:30.098 16523 20542 E AsyncOperation: at bdbk.run(:com.google.android.gms@[email protected] (040306-211705629):2)
09-29 21:02:30.098 16523 20542 E AsyncOperation: at qcr.run(:com.google.android.gms@[email protected] (040306-211705629):21)
09-29 21:02:30.098 16523 20542 E AsyncOperation: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
09-29 21:02:30.098 16523 20542 E AsyncOperation: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
09-29 21:02:30.098 16523 20542 E AsyncOperation: at qiv.run(:com.google.android.gms@[email protected] (040306-211705629))
09-29 21:02:30.098 16523 20542 E AsyncOperation: at java.lang.Thread.run(Thread.java:818)
09-29 21:02:30.122 925 1020 I WindowManager: Screen frozen for +271ms due to Window{f2a59ad u0 NavigationBar}
09-29 21:02:30.161 24187 24214 D MWENGINE: seq. position: 13, buffer offset: 732, elapsed samples: 77532
09-29 21:02:30.283 24187 24214 D MWENGINE: seq. position: 14, buffer offset: 510, elapsed samples: 83070
09-29 21:02:30.400 24187 24214 D MWENGINE: seq. position: 15, buffer offset: 288, elapsed samples: 88608
09-29 21:02:30.401 24187 24214 D MWENGINE: seq. position: 0, buffer offset: 292, elapsed samples: 0

Use CMake to generate Makefiles

By moving the library Makefiles to CMake, it will be easier to integrate building of the native code within Android Studio (and relying on Gradle as the single build system, rather than having to manually execute shell scripts). CMake will also cater fine to those who work outside of AS.

Some inspiration can be found here

setTempo creates invalid boundaries if loop is not starting at buffer position 0

if a loop is running from start > 0 to end > start (set by setLoopRange()) and setTempo() is called I observed:

  1. buffer positions of audio events are scaled according to ratio (oldTempo/newTempo)
  2. min_buffer_position stays untouched (but should be scaled too?)
  3. max_buffer_position is samples_per_bar * amount_of_bars bout should be min_buffer_position + samples_per_bar * amount_of_bars

Maybe I'm not using it correctly. But changing handleTempoUpdate() to:
float ratio = tempo / aQueuedTempo;
....
min_buffer_position = min_buffer_position * ratio;
max_buffer_position = ( min_buffer_position + samples_per_bar * amount_of_bars ) - 1;

Helped me.

Arpeggiator - How To

Hey!

I have been studying this code and cannot seem to understand how or what triggers the Arppegiator.

Simple setup;

       synth1.setArpeggiatorActive(true);
        synth1.getArpeggiator().setAmountOfSteps(16);
        synth1.getArpeggiator().setShiftForStep(0, 1);
        synth1.getArpeggiator().setShiftForStep(1, 1);
        synth1.getArpeggiator().setShiftForStep(2, 5);
        synth1.getArpeggiator().setShiftForStep(3, 1);
        synth1.getArpeggiator().setShiftForStep(4, 1);
        synth1.getArpeggiator().setShiftForStep(5, 7);
        synth1.getArpeggiator().setShiftForStep(6, 1);
        synth1.getArpeggiator().setShiftForStep(7, 1);
        synth1.getArpeggiator().setShiftForStep(8, 10);
        synth1.getArpeggiator().setShiftForStep(9, 1);
        synth1.getArpeggiator().setShiftForStep(10, 1);
        synth1.getArpeggiator().setShiftForStep(11, 6);
        synth1.getArpeggiator().setShiftForStep(12, 1);
        synth1.getArpeggiator().setShiftForStep(13, 1);
        synth1.getArpeggiator().setShiftForStep(14, 1);
        synth1.getArpeggiator().setShiftForStep(15, 1);

I get what it's doing but I can't get it to do it. :) I have sequenced 1 audio event for a synth for 8 steps in a 16 step grid for tests.

I also have the sequencer controller playing.

_engine.dispose lets crash app when called 2 times

How to reproduce:

  1. start the example app (hitting play button not necessary)
  2. send app in background
  3. bring the app back in foreground (everything seems still fine)
  4. send it in background again
  5. bring it back in foreground
  6. now the gui is not responding anymore and the app crashes after some seconds

Phone: Sony Z3 Compact
OS: 6.0.1, BN: 23.5.A.1.291

Audio Driver for Windows

Hey,

I know you read the title and said, huh? :)

What I was wondering is if it's basically a process of writing a new driver that hooked into something like FMOD library to get this running on Windows.

I use multi-platform libraries for some apps and was curious if I could get this running on the desktop for testing. (UI is multi-platform)

It seems from looking through the source code, even with a new driver it's invasive to the framework? What I mean is you have flags for SL and AA Audio, those are kind of hard coded in the engine it seems?

Thanks

C++ Convention - 0 used as null pointer

Is this legacy in the code that you use 0 as nullptr?

I am coding convention OCD sometimes and I have read not using nullptr is bad form.

Here again, I am just getting into the deeper nuances of C++ now and I want to do things correct.

Build Project

hi guy,

I'm Nguyen, i enjoy the project very much. Howerver, i download it, and import, i get some errors about project when building project. There are images for project in Eclipse and Android Studio
image
image

I downloaded Android NDK.
Can you help me about this or guide me to step by step build project?
Thanks!

Sample - Runtime Permissions

I am pretty sure a comment or something should be added that as of Nov 1st, all apps must be compiled with API 26 or greater to be released on the play store.

The sample app does not handle run-time file access and crashes on non debug devices.

This is not asking to add the code since it's a sample but some people may scratch their heads.

Unable to control measure subdivision

aStepsPerBar parameter in updateMeasures method seem to has no effect.
Subdivision seems to be controlled by AudioEngine::beat_subdivision which is not modifiable from API.
Sequencer always notifies about 16 steps.

local.properties should not be included in version control

local.properties: You can put information about the SDK location but also, your sensitive data like username and password of your repo access and the location of your Key to sign the Apks.

You have hard coded values for the sdk location that are not universal. This file needs to be gitignored as well so users like myself can override properties locally.

Logger - com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered

Hey,

I cannot for the life of me figure out where this log is. The log is being produced as you can see many times a second.

I don't understand why it's calling stop() on AudioTrack, where ever this is, is it actually AudioTrack?

2018-11-07 14:27:09.874 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:09.889 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:09.906 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:09.921 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:09.934 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:09.949 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:09.964 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:09.979 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:09.995 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:10.010 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:10.024 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:10.040 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:10.055 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:10.070 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:10.085 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:10.100 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered
2018-11-07 14:27:10.114 24656-24680/com.teotigraphix.mwenginetest1 D/AudioTrack: stop() called with 720 frames delivered

AudioEngine.pause() causes native crash

This only happens on Cat S60. I call _engine.pause() when app goes into background.
Model: Cat S60
Android Version: 6.0.1

This also happens with the MWEngine Example App

********** Crash dump: **********
Build fingerprint: 'Cat/CatS60/CatS60:6.0.1/MMB29M/LTE_S0201121.0_S60_0.030.01:user/release-keys'
pid: 15359, tid: 15432, name: Thread-17525 >>> com.harthorst.compas <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xdeadbaad
Stack frame #00 pc 0000000000047548 /system/lib64/libc.so (dlfree+408)
Stack frame #1 pc 0000000000019568 /system/lib64/libc.so (free+20)
Stack frame #2 pc 0000000000041238 /data/app/com.harthorst.compas-2/lib/arm64/libmwengine.so (_ZN13DriverAdapter7destroyEv+36)
Stack frame #3 pc 0000000000042280 /data/app/com.harthorst.compas-2/lib/arm64/libmwengine.so (_ZN11AudioEngine5startEv+496)
Stack frame #4 pc 0000000000129ea8 /system/lib64/libart.so (art_quick_generic_jni_trampoline+152)
Stack frame #5 pc 0000000000120518 /system/lib64/libart.so (art_quick_invoke_static_stub+600)
Stack frame #6 pc 00000000001301c4 /system/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+344)
Stack frame #7 pc 00000000004d23f0 /system/lib64/libart.so (artInterpreterToCompiledCodeBridge+212)
Stack frame #8 pc 00000000002abb00 /system/lib64/libart.so (_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE+480)
Stack frame #9 pc 00000000000dd548 /system/lib64/libart.so (ZN3art11interpreter15ExecuteGotoImplILb0ELb0EEENS_6JValueEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameES2+22200)
Stack frame #10 pc 0000000000289908 /system/lib64/libart.so (artInterpreterToInterpreterBridge+220)
Stack frame #11 pc 00000000002abb00 /system/lib64/libart.so (_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE+480)
Stack frame #12 pc 00000000000dd548 /system/lib64/libart.so (ZN3art11interpreter15ExecuteGotoImplILb0ELb0EEENS_6JValueEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameES2+22200)
Stack frame #13 pc 0000000000289510 /system/lib64/libart.so (_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadEPKNS_7DexFile8CodeItemEPNS_11ShadowFrameE+96)
Stack frame #14 pc 000000000053ef00 /system/lib64/libart.so (artQuickToInterpreterBridge+632)
Stack frame #15 pc 0000000000129fe4 /system/lib64/libart.so (art_quick_to_interpreter_bridge+100)
Stack frame #16 pc 0000000000120264 /system/lib64/libart.so (art_quick_invoke_stub+580)
Stack frame #17 pc 000000000013011c /system/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+176)
Stack frame #18 pc 00000000004312e8 /system/lib64/libart.so (_ZN3art35InvokeVirtualOrInterfaceWithJValuesERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_jmethodIDP6jvalue+460)
Stack frame #19 pc 000000000045f994 /system/lib64/libart.so (_ZN3art6Thread14CreateCallbackEPv+744)
Stack frame #20 pc 0000000000067784 /system/lib64/libc.so (_ZL15__pthread_startPv+52)
Stack frame #21 pc 000000000001c604 /system/lib64/libc.so (__start_thread+16)

SampleEvent seems to play files not in correct speed

What I did:
final SampleEvent drumEvent = new SampleEvent( _sampler );
drumEvent.setSample( SampleManager.getSample( "2"));
//drumEvent.setPlaybackRate(0.9f);
drumEvent.setVolume(1f);
drumEvent.play();

The played tone is a little bit higher than it should be. When I set the playback rate to 0.9 it's almost perfect.
I tried resampling the wav file (44100 <-> 48k) but that did not help.

Tested on Sony Xperia Z3 Compact

Stress Testing DSP

Hi,

I was curious if you have tried to break this on devices to see how many oscillators etc. you can have going.

This is more just an informative question.

If you haven't I guess I will, I have a Pixel and Samsung tablet that I will test on (and my other beta testers).

Custom c++ observer is not receiving notifications

When trying to use a custom observer in c++, no notification is received.

The problem seems to be in two places.

  1. It's not possible to override the observer functions handleNotification(int aNotificationType) and handleNotification(int aNotificationType, int aValue) because these are not declared as virtual in observer.h. When these functions later are called the parent functions will be invoked, the virtual declaration is needed to achieve late binding of these functions.

  2. The vector list of observers in notifier.cpp will never be filled. This line:
    std::vector<Observer*> observers = it->second;
    will create a copy of the observer vector, and subsequent calls to observers.push_back( aObserver ); will be executed on the copy and not the vector in the map. Calling push_back directly on it->second will work.

Synth Legato - Portamento

I was playing around today and realized we can set frequencies real-time and you also mentioned 'legato' in the code comments.

You would need a place on audio event for flags, since you need to specify which event IS legato.

This would also have to be mono synth as well.

So as with most of my other issues, I am just looking for some conversation and direction.

sometimes sound is interrupted for a few ms

there are some sound glitches from time to time. in the log I see:

IBufferQueue.c:57: pthread_mutex_lock_timeout_np returned 110

Google says that's a general problem but maybe you have some hints to prevent this. My app is using up to 5% of cpu power and I'm working with SampleEvents only.

Crash on startup

Hello, I'm trying to import mwengine to my project.

I copied c++ and java source as same directory structure and swig build script and build was successful.

When I try to start the activity from my app, it crashes.

The activity and asserts resources are almost same as sample. (MWEngineActivity.java, hihat.wav, clap.wav)

The logcat said

12-20 15:07:31.230 30870-30870/net.gerosyab.guitaroid A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x4 in tid 30870 (osyab.guitaroid)
12-20 15:07:31.230 3313-3477/? D/audio_hw_primary: out_set_parameters: enter: kvpairs: routing=2
12-20 15:07:31.230 3313-3477/? D/AudioFlinger: setCurDevice() 0x2
                                               [ 12-20 15:07:31.233  3121: 3121 W/         ]
                                               debuggerd: handling request: pid=30870 uid=10568 gid=10568 tid=30870
12-20 15:07:31.234 30870-31001/net.gerosyab.guitaroid V/MWENGINE: STARTED engine
12-20 15:07:31.235 3313-3477/? V/audio_hw_primary: start_output_stream+, out->device : 00000002 , out->type = 1
12-20 15:07:31.235 30870-31002/net.gerosyab.guitaroid I/AudioTrack: Skip ramp
12-20 15:07:31.235 3313-3477/? D/audio_hw_primary: start_output_stream jack_skip_check false
12-20 15:07:31.236 30870-31002/net.gerosyab.guitaroid I/AudioTrack: Skip ramp
12-20 15:07:31.262 3313-3477/? D/audio_hw_primary: start_output_stream (0xeeaa89a0) out->pcm_device:6 out->config.rate:48000 out->config.format:0 out->period_size:240
12-20 15:07:31.262 3313-3477/? D/audio_hw_primary: start_output_stream (0xeeaa89a0) out->config.rate:48000 out->config.format:0 out->period_size:240
12-20 15:07:31.262 3313-3477/? V/audio_hw_primary: select_devices output_scenario:0 input_scenario:-1 out_snd_device 0x2 in_snd_device:0x0
12-20 15:07:31.263 3313-3477/? I/audio_route: > audio_route_reset :
12-20 15:07:31.263 3313-3477/? I/audio_route: > audio_route_apply_path : "media-speaker"
12-20 15:07:31.375 3313-3477/? V/audio_hw_primary: select_devices() output_route "media-speaker" 
12-20 15:07:31.375 3313-3477/? I/audio_route: > audio_route_apply_path : "gain-media-speaker"
12-20 15:07:31.375 3313-3477/? V/audio_hw_primary: select_devices() output_gain "gain-media-speaker" 
12-20 15:07:31.375 3313-3477/? I/audio_route: > audio_route_update_mixer : +
12-20 15:07:31.376 3313-3477/? I/audio_route: > audio_route_update_mixer : changed(0) -
12-20 15:07:31.376 3313-3477/? I/audio_hw_primary: select_devices - 
12-20 15:07:31.376 3313-3477/? V/audio_hw_primary: start_output_stream-
12-20 15:07:31.405 3313-3477/? D/AudioFlinger: mixer(0xed883040) Spend too much time to write: delta 170(effect 0, stage 0)
12-20 15:07:31.423 31003-31003/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
12-20 15:07:31.424 31003-31003/? A/DEBUG: Build fingerprint: 'samsung/herolteskt/herolteskt:7.0/NRD90M/G930SKSU1DQJ1:user/release-keys'
12-20 15:07:31.424 31003-31003/? A/DEBUG: Revision: '8'
12-20 15:07:31.424 31003-31003/? A/DEBUG: ABI: 'arm64'
12-20 15:07:31.424 31003-31003/? A/DEBUG: pid: 30870, tid: 30870, name: osyab.guitaroid  >>> net.gerosyab.guitaroid <<<
12-20 15:07:31.424 31003-31003/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4
12-20 15:07:31.424 31003-31003/? A/DEBUG:     x0   00000074fb896620  x1   0000000000000000  x2   00000074fb896620  x3   0000007fd1c68f08
12-20 15:07:31.424 31003-31003/? A/DEBUG:     x4   0000000000000000  x5   0000000000000001  x6   0000000100000000  x7   0000000000000000
12-20 15:07:31.424 31003-31003/? A/DEBUG:     x8   0000000000000001  x9   0000000000000000  x10  0000000000430000  x11  0000000000000000
12-20 15:07:31.424 31003-31003/? A/DEBUG:     x12  000000000000018c  x13  0000000000000048  x14  0000007520d1d470  x15  76205267d1c3e4e7
12-20 15:07:31.424 31003-31003/? A/DEBUG:     x16  00000074fbcdde88  x17  00000074fbc1f00c  x18  0000000071666f10  x19  00000074fb896620
12-20 15:07:31.424 31003-31003/? A/DEBUG:     x20  0000007510d1ab68  x21  0000000000000000  x22  0000000000000000  x23  000000751b2258cc
12-20 15:07:31.424 31003-31003/? A/DEBUG:     x24  0000000000000014  x25  d77d57c4ab9dcb4c  x26  000000751c8c7a98  x27  d77d57c4ab9dcb4c
12-20 15:07:31.424 31003-31003/? A/DEBUG:     x28  0000007fd1c68f10  x29  0000007fd1c68ed0  x30  000000751c2b0494
12-20 15:07:31.425 31003-31003/? A/DEBUG:     sp   0000007fd1c68eb0  pc   00000074fbc1f030  pstate 0000000020000000
12-20 15:07:31.442 31003-31003/? A/DEBUG: backtrace:
12-20 15:07:31.442 31003-31003/? A/DEBUG:     #00 pc 0000000000046030  /data/app/net.gerosyab.guitaroid-2/lib/arm64/libmwengine.so (_ZN11SampleEvent9setSampleEP11AudioBuffer+36)
12-20 15:07:31.442 31003-31003/? A/DEBUG:     #01 pc 00000000000db490  /system/lib64/libart.so (art_quick_generic_jni_trampoline+144)
12-20 15:07:31.442 31003-31003/? A/DEBUG:     #02 pc 00000000000d2168  /system/lib64/libart.so (art_quick_invoke_static_stub+600)
12-20 15:07:31.442 31003-31003/? A/DEBUG:     #03 pc 00000000000dec20  /system/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+256)
12-20 15:07:31.442 31003-31003/? A/DEBUG:     #04 pc 0000000000291020  /system/lib64/libart.so (_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPKNS_7DexFile8CodeItemEPNS_11ShadowFrameEPNS_6JValueE+312)
12-20 15:07:31.443 31003-31003/? A/DEBUG:     #05 pc 0000000000289fe0  /system/lib64/libart.so (_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE+592)
12-20 15:07:31.443 31003-31003/? A/DEBUG:     #06 pc 000000000055d9cc  /system/lib64/libart.so (MterpInvokeStatic+356)
12-20 15:07:31.443 31003-31003/? A/DEBUG:     #07 pc 00000000000c4a14  /system/lib64/libart.so (ExecuteMterpImpl+14612)

I added more information to logcat. And I think I found the occuring point.

12-20 16:01:45.806 9054-9054/net.gerosyab.guitaroid D/MWENGINE: initing MWEngineActivity
12-20 16:01:45.816 9054-9054/net.gerosyab.guitaroid V/MWENGINE: JNI INITED OK
12-20 16:01:45.825 9054-9538/net.gerosyab.guitaroid D/MWENGINE: STARTING NATIVE AUDIO RENDER THREAD
12-20 16:01:45.826 9054-9538/net.gerosyab.guitaroid D/MWENGINE: STARTING NATIVE THREAD @ 48000 Hz using 240 samples per buffer
12-20 16:01:45.826 9054-9538/net.gerosyab.guitaroid V/MWENGINE: STARTING engine
12-20 16:01:45.837 9054-9054/net.gerosyab.guitaroid V/MWENGINE: WaveReader::Error not a valid WAVE file
12-20 16:01:45.838 9054-9054/net.gerosyab.guitaroid V/MWENGINE: WaveReader::Error not a valid WAVE file
12-20 16:01:45.841 9054-9054/net.gerosyab.guitaroid D/mwSampleEvent: SampleEvent Construnctor Entry1
12-20 16:01:45.841 9054-9054/net.gerosyab.guitaroid D/mwSampleEvent: SampleEvent Construnctor Entry3
12-20 16:01:45.841 9054-9054/net.gerosyab.guitaroid D/mwSampleEvent: SampleEvent.setSample() 1
12-20 16:01:45.842 9054-9054/net.gerosyab.guitaroid D/mwSampleEvent: SampleEvent.setSample() this : nl.igorski.lib.audio.mwengine.SampleEvent@327abf6
12-20 16:01:45.842 9054-9054/net.gerosyab.guitaroid D/mwSampleEvent: SampleEvent.setSample() sampleBuffer : null
12-20 16:01:45.842 9054-9054/net.gerosyab.guitaroid D/mwNDK: Java_nl_igorski_lib_audio_mwengine_MWEngineCoreJNI_SampleEvent_1setSample start
12-20 16:01:45.842 9054-9054/net.gerosyab.guitaroid D/mwNDK: 111
12-20 16:01:45.842 9054-9054/net.gerosyab.guitaroid D/mwNDK: 222
12-20 16:01:45.845 9054-9538/net.gerosyab.guitaroid V/MWENGINE: STARTED engine

Wav files are same as in the sample's asserts folder but don't know why the WaveReader says not a valid WAVE file.

Anyway the location is "Java_nl_igorski_lib_audio_mwengine_MWEngineCoreJNI_SampleEvent_1setSample()" in "java_interface_wrapper.cpp"

When setSample() method in SampleEvent.cpp is called, it crashed.

Could you give me any advice?

NDK build version

Hi,

I was just curious what version of the NDK you are building this with?

Mike

Ableton Link integration with the sequencer

Hi,

This is really just a question if you have thought about this implementation?

I am going to try it myself but I thought I would get your input first to see if there were any problems you see or even that you haven't thought about it. :) (either way)

Mike

native crash at audioengine.cpp line 357

This happens randomly. I'm using SampleEvents which are deleted and added while the sequencer is running.

********** Crash dump: **********
Build fingerprint: 'Sony/D5803/D5803:6.0.1/23.5.A.1.291/2769308465:user/release-keys'
pid: 24168, tid: 24218, name: Thread-118809 >>> com.harthorst.compas <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x9
Stack frame #00 pc 0003060c /data/app/com.harthorst.compas-2/lib/arm/libmwengine.so (_ZN11AudioEngine6renderEi+811): Routine AudioEngine::render(int) at /Users/thf/tmp/compas-app/mwengine/src/main/cpp/./audioengine.cpp:357
Stack frame #1 pc 00030187 /data/app/com.harthorst.compas-2/lib/arm/libmwengine.so (_ZN11AudioEngine5startEv+306): Routine AudioEngine::start() at /Users/thf/tmp/compas-app/mwengine/src/main/cpp/./audioengine.cpp:156
Stack frame #2 pc 00d4203d /data/app/com.harthorst.compas-2/oat/arm/base.odex (offset 0x934000) (void nl.igorski.lib.audio.mwengine.MWEngineCoreJNI.start()+72)
Stack frame #3 pc 010faf89 /data/app/com.harthorst.compas-2/oat/arm/base.odex (offset 0x934000) (void nl.igorski.lib.audio.mwengine.MWEngineCore.start()+84)
Stack frame #4 pc 00d12959 /data/app/com.harthorst.compas-2/oat/arm/base.odex (offset 0x934000) (void nl.igorski.lib.audio.MWEngine.run()+852)
Stack frame #5 pc 000e6401 /system/lib/libart.so (art_quick_invoke_stub_internal+64)
Stack frame #6 pc 00403305 /system/lib/libart.so (art_quick_invoke_stub+188)
Stack frame #7 pc 00102774 [stack:24218]

Failed to build example

I've installed NDK and SWIG, cloned master branch
Running build.sh gives me the following errors

jni/utilities/bufferutility.cpp:186:5: error: use of undeclared identifier
      'memset'; did you mean 'wmemset'?
    memset( out, 0, aBufferSize * sizeof( SAMPLE_TYPE )); // zero bits s...
    ^~~~~~
    wmemset
/Users/rostopira/android/sdk/ndk-bundle/sysroot/usr/include/wchar.h:159:10: note: 
      'wmemset' declared here
wchar_t* wmemset(wchar_t* __dst, wchar_t __wc, size_t __n);
         ^
jni/utilities/bufferutility.cpp:186:13: error: cannot initialize a parameter of
      type 'wchar_t *' with an lvalue of type 'double *'
    memset( out, 0, aBufferSize * sizeof( SAMPLE_TYPE )); // zero bits s...
            ^~~
/Users/rostopira/android/sdk/ndk-bundle/sysroot/usr/include/wchar.h:159:27: note: 
      passing argument to parameter '__dst' here
wchar_t* wmemset(wchar_t* __dst, wchar_t __wc, size_t __n);
                          ^
2 errors generated.
make: *** [obj/local/x86_64/objs/mwengine/utilities/bufferutility.o] Error 1

Feature request: add volume control to SampleEvent

I need to control volume of each (sample) event, to make accents. With current API I can achieve it with many samples with adjusted volume in buffers (memory overhead) or many instruments with different volumes (not flexible, code overhead).
I suggest to add volume control to all events, like in SynthEvent.

Sequencer events

Again excuse my noobness but I am trying to wrap my head around the sequencer implementation.

I guess giving an example is easier then asking the question.

I am trying to figure out how I would maintain patterns(with step length) per channel and piano roll per channel, then be able to real-time sequence the pattern data along with the piano roll of each sequencer data model that holds the individual events.

I think I can pretty much wrap my head around the pattern stuff.

  1. Is this sequencer just a container for any event and it's the client's responsibility to separate the data on the client side and somehow shove events into the main sequencer as needed?

  2. How do you setup non quantized beats without using a grid?

Is the MWEngine suitable for multitrack daw?

Hi,
I'm trying to implement a ordinary multitrack daw.

E.g i have 12 Tracks,
Each tracks have different time range with seconds
(track1: 0-45, track2: 10-30,..., track12: 120-150)

Can the MWengine play files with time manipulation?
thanks.

Bounced WAVs should be merged.

When bouncing the engine's audio output, as long as there is content to be generated, the engine will write a .WAV file at the length of the specified maximum buffer size. At the end of the operation this leaves us with a lot of split .WAV files on the device. These should be merged into a single file.

Automation Parameters

Hi,

I don't know if the current framework can handle it but having an automation framework that can use a table or linked list for key/value pairs that real-time adjust synth parameters or audio event parameters.

Have you daydreamed about this before in context of MWEngine?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.