android support done + fixes bw_{note_queue,voice_alloc}

This commit is contained in:
Stefano D'Angelo 2023-07-30 09:48:56 +02:00
parent df554f2572
commit e82731e1f0
11 changed files with 317 additions and 24 deletions

2
TODO
View File

@ -86,6 +86,8 @@ code:
* fix vst3 mapped values (visible in Ableton Live) and short names * fix vst3 mapped values (visible in Ableton Live) and short names
* polish examples (ranges, etc.) * polish examples (ranges, etc.)
* dump data structures (see note queue) and indicate error precisely * dump data structures (see note queue) and indicate error precisely
* process (multi) no update
* examples cpu usage
build system: build system:
* make makefiles handle paths with spaces etc * make makefiles handle paths with spaces etc

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.orastron.@NAME@"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.orastron.@NAME@">
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-sdk android:minSdkVersion="26" /> <!-- for androidx core --> <uses-sdk android:minSdkVersion="26" /> <!-- for androidx core and AAudio -->
<uses-sdk android:targetSdkVersion="34" /> <uses-sdk android:targetSdkVersion="34" />
<application android:label="@NAME@"> <application android:label="@NAME@">
<activity android:name=".MainActivity" android:label="@NAME@" android:exported="true"> <activity android:name=".MainActivity" android:label="@NAME@" android:exported="true">

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.orastron.@NAME@">
<uses-sdk android:minSdkVersion="29" /> <!-- for AMidi -->
<uses-sdk android:targetSdkVersion="34" />
<application android:label="@NAME@">
<activity android:name=".MainActivity" android:label="@NAME@" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,160 @@
package com.orastron.@NAME@;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebSettings;
import android.webkit.WebChromeClient;
import android.webkit.WebViewClient;
import android.webkit.JavascriptInterface;
import android.content.Context;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import android.media.midi.MidiManager;
import android.media.midi.MidiManager.DeviceCallback;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceInfo.PortInfo;
import android.media.midi.MidiDevice;
import java.util.ArrayList;
import android.util.Log;
public class MainActivity extends Activity {
static {
System.loadLibrary("@NAME@");
}
public native boolean nativeAudioStart();
public native void nativeAudioStop();
public native float nativeGetParameter(int i);
public native void nativeSetParameter(int i, float v);
public native void addMidiPort(MidiDevice d, int p);
public native void removeMidiPort(MidiDevice d, int p);
private WebView webView;
public class WebAppInterface {
private MidiManager midiManager;
private MidiDeviceCallback midiDeviceCallback;
public ArrayList<MidiDevice> midiDevices = new ArrayList<MidiDevice>();
public void addMidiDevices(MidiDeviceInfo[] devices) {
for (int i = 0; i < devices.length; i++) {
if (devices[i].getOutputPortCount() == 0)
continue;
midiManager.openDevice(devices[i],
new MidiManager.OnDeviceOpenedListener() {
@Override
public void onDeviceOpened(MidiDevice device) {
PortInfo[] ports = device.getInfo().getPorts();
for (int i = 0; i < ports.length; i++)
if (ports[i].getType() == PortInfo.TYPE_OUTPUT)
addMidiPort(device, ports[i].getPortNumber());
WebAppInterface.this.midiDevices.add(device);
}
}, null);
}
}
public void removeMidiDevices(MidiDeviceInfo[] devices) {
for (int i = 0; i < midiDevices.size(); i++) {
MidiDevice device = midiDevices.get(i);
int id = device.getInfo().getId();
int j = 0;
for (; j < devices.length; j++)
if (id == devices[j].getId())
break;
if (j == devices.length)
continue;
PortInfo[] ports = device.getInfo().getPorts();
for (j = 0; j < ports.length; j++)
if (ports[j].getType() == PortInfo.TYPE_OUTPUT)
removeMidiPort(device, ports[j].getPortNumber());
midiDevices.remove(i);
}
}
public void removeAllMidiDevices() {
for (int i = 0; i < midiDevices.size(); i++) {
MidiDevice device = midiDevices.get(i);
PortInfo[] ports = device.getInfo().getPorts();
for (int j = 0; j < ports.length; j++)
if (ports[j].getType() == PortInfo.TYPE_OUTPUT)
removeMidiPort(device, ports[j].getPortNumber());
}
midiDevices.clear();
}
public class MidiDeviceCallback extends MidiManager.DeviceCallback {
@Override
public void onDeviceAdded(MidiDeviceInfo device) {
WebAppInterface.this.addMidiDevices(new MidiDeviceInfo[]{device});
}
@Override
public void onDeviceRemoved(MidiDeviceInfo device) {
WebAppInterface.this.removeMidiDevices(new MidiDeviceInfo[]{device});
}
}
@JavascriptInterface
public boolean hasAudioPermission() {
return MainActivity.this.checkCallingOrSelfPermission(android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
}
@JavascriptInterface
public void requestAudioPermission() {
ActivityCompat.requestPermissions(MainActivity.this, new String[] { android.Manifest.permission.RECORD_AUDIO }, 0);
}
@JavascriptInterface
public boolean audioStart() {
midiManager = (MidiManager)getSystemService(Context.MIDI_SERVICE);
addMidiDevices(midiManager.getDevices());
midiDeviceCallback = new MidiDeviceCallback();
midiManager.registerDeviceCallback(midiDeviceCallback, null);
return nativeAudioStart();
}
@JavascriptInterface
public void audioStop() {
nativeAudioStop();
midiManager.unregisterDeviceCallback(midiDeviceCallback);
removeAllMidiDevices();
}
@JavascriptInterface
public float getParameter(int i) {
return nativeGetParameter(i);
}
@JavascriptInterface
public void setParameter(int i, float v) {
nativeSetParameter(i, v * 0.001f);
}
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
webView = new WebView(this);
setContentView(webView);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient());
webSettings.setDomStorageEnabled(true);
webView.addJavascriptInterface(new WebAppInterface(), "Android");
webView.loadUrl("file:///android_asset/index.html");
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults.length > 0)
webView.loadUrl("javascript:gotAudioPermission()");
}
}

View File

@ -17,7 +17,11 @@ KOTLINX_COROUTINES_CORE_JVM_FILE := ${KOTLIN_DIR}/kotlinx-coroutines-core-jvm-1.
JAVAC := javac JAVAC := javac
KEYTOOL := keytool KEYTOOL := keytool
CXX := ${ANDROID_NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang++ ifdef SYNTH
CXX := ${ANDROID_NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi29-clang++
else
CXX := ${ANDROID_NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi26-clang++
endif
ADB := ${HOME}/Android/Sdk/platform-tools/adb ADB := ${HOME}/Android/Sdk/platform-tools/adb
APKSIGNER := ${BUILD_TOOLS_DIR}/apksigner APKSIGNER := ${BUILD_TOOLS_DIR}/apksigner
ZIPALIGN := ${BUILD_TOOLS_DIR}/zipalign ZIPALIGN := ${BUILD_TOOLS_DIR}/zipalign
@ -43,6 +47,10 @@ LDFLAGS := \
-llog \ -llog \
-landroid -landroid
ifdef SYNTH
LDFLAGS += -lamidi
endif
SOURCES_COMMON := \ SOURCES_COMMON := \
build/gen/jni.cpp build/gen/jni.cpp
@ -57,6 +65,14 @@ JARS := \
JNI_NAME := $(shell echo ${NAME} | sed 's:_:_1:g') JNI_NAME := $(shell echo ${NAME} | sed 's:_:_1:g')
ifdef SYNTH
ANDROID_MANIFEST_SOURCE := ${COMMON_DIR}/AndroidManifest-synth.xml
MAIN_ACTIVITY_SOURCE := ${COMMON_DIR}/MainActivity-synth.java
else
ANDROID_MANIFEST_SOURCE := ${COMMON_DIR}/AndroidManifest-fx.xml
MAIN_ACTIVITY_SOURCE := ${COMMON_DIR}/MainActivity-fx.java
endif
all: build/${NAME}.apk all: build/${NAME}.apk
build/${NAME}.apk: build/gen/${NAME}.aligned.apk build/apk/lib/armeabi-v7a/lib${NAME}.so build/gen/keystore.jks build/${NAME}.apk: build/gen/${NAME}.aligned.apk build/apk/lib/armeabi-v7a/lib${NAME}.so build/gen/keystore.jks
@ -71,11 +87,19 @@ build/gen/${NAME}.aligned.apk: build/gen/${NAME}.unsigned.apk
build/gen/${NAME}.unsigned.apk: build/apk/classes.dex build/gen/AndroidManifest.xml build/assets/index.html build/assets/config.js build/apk/lib/armeabi-v7a/lib${NAME}.so| build/gen build/gen/${NAME}.unsigned.apk: build/apk/classes.dex build/gen/AndroidManifest.xml build/assets/index.html build/assets/config.js build/apk/lib/armeabi-v7a/lib${NAME}.so| build/gen
${AAPT} package -f -M build/gen/AndroidManifest.xml -A build/assets -I ${JAR_FILE} -I ${ANDROIDX_CORE_FILE} -I ${ANDROIDX_LIFECYCLE_COMMON_FILE} -I ${ANDROIDX_VERSIONEDPARCELABLE_FILE} -I ${KOTLIN_STDLIB_FILE} -I ${KOTLINX_COROUTINES_CORE_FILE} -I ${KOTLINX_COROUTINES_CORE_JVM_FILE} -F $@ build/apk ${AAPT} package -f -M build/gen/AndroidManifest.xml -A build/assets -I ${JAR_FILE} -I ${ANDROIDX_CORE_FILE} -I ${ANDROIDX_LIFECYCLE_COMMON_FILE} -I ${ANDROIDX_VERSIONEDPARCELABLE_FILE} -I ${KOTLIN_STDLIB_FILE} -I ${KOTLINX_COROUTINES_CORE_FILE} -I ${KOTLINX_COROUTINES_CORE_JVM_FILE} -F $@ build/apk
ifdef SYNTH
build/apk/classes.dex: build/apk/my_classes.jar build/apk/classes.dex: build/apk/my_classes.jar
cd build/apk && ${BUILD_TOOLS_DIR}/d8 --min-api 21 ../../$^ ${JARS} && cd ../.. cd build/apk && ${D8} --min-api 29 ../../$^ ${JARS} && cd ../..
build/apk/my_classes.jar: build/obj/com/orastron/${NAME}/MainActivity.class build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface.class build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface$$MidiDeviceCallback.class build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface$$1.class | build/apk
${D8} build/obj/com/orastron/${NAME}/MainActivity.class 'build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface.class' 'build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface$$MidiDeviceCallback.class' 'build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface$$1.class' --min-api 29 --output $@ --no-desugaring
else
build/apk/classes.dex: build/apk/my_classes.jar
cd build/apk && ${D8} --min-api 26 ../../$^ ${JARS} && cd ../..
build/apk/my_classes.jar: build/obj/com/orastron/${NAME}/MainActivity.class build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface.class | build/apk build/apk/my_classes.jar: build/obj/com/orastron/${NAME}/MainActivity.class build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface.class | build/apk
${D8} build/obj/com/orastron/${NAME}/MainActivity.class 'build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface.class' --output $@ --no-desugaring ${D8} build/obj/com/orastron/${NAME}/MainActivity.class 'build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface.class' --min-api 26 --output $@ --no-desugaring
endif
build/apk/lib/armeabi-v7a/lib${NAME}.so: ${SOURCES} | build/apk/lib/armeabi-v7a build/apk/lib/armeabi-v7a/lib${NAME}.so: ${SOURCES} | build/apk/lib/armeabi-v7a
${CXX} $^ ${CXXFLAGS} ${LDFLAGS} -o $@ ${CXX} $^ ${CXXFLAGS} ${LDFLAGS} -o $@
@ -88,10 +112,10 @@ build/obj/com/orastron/${NAME}/MainActivity$$WebAppInterface.class: build/obj/co
build/obj/com/orastron/${NAME}/MainActivity.class: build/gen/com/orastron/${NAME}/MainActivity.java | build/obj build/obj/com/orastron/${NAME}/MainActivity.class: build/gen/com/orastron/${NAME}/MainActivity.java | build/obj
${JAVAC} -classpath "${JAR_FILE}:${ANDROIDX_CORE_FILE}:${ANDROIDX_LIFECYCLE_COMMON_FILE}:${ANDROIDX_VERSIONEDPARCELABLE_FILE}:${KOTLIN_STDLIB_FILE}:${KOTLINX_COROUTINES_CORE_FILE}:${KOTLINX_COROUTINES_CORE_JVM_FILE}" -d build/obj build/gen/com/orastron/${NAME}/MainActivity.java ${JAVAC} -classpath "${JAR_FILE}:${ANDROIDX_CORE_FILE}:${ANDROIDX_LIFECYCLE_COMMON_FILE}:${ANDROIDX_VERSIONEDPARCELABLE_FILE}:${KOTLIN_STDLIB_FILE}:${KOTLINX_COROUTINES_CORE_FILE}:${KOTLINX_COROUTINES_CORE_JVM_FILE}" -d build/obj build/gen/com/orastron/${NAME}/MainActivity.java
build/gen/com/orastron/${NAME}/MainActivity.java: ${COMMON_DIR}/MainActivity.java | build/gen/com/orastron/${NAME} build/gen/com/orastron/${NAME}/MainActivity.java: ${MAIN_ACTIVITY_SOURCE} | build/gen/com/orastron/${NAME}
cat $^ | sed s:@NAME@:${NAME}:g > $@ cat $^ | sed s:@NAME@:${NAME}:g > $@
build/gen/AndroidManifest.xml: ${COMMON_DIR}/AndroidManifest.xml | build/gen/com/orastron/${NAME} build/gen/AndroidManifest.xml: ${ANDROID_MANIFEST_SOURCE} | build/gen/com/orastron/${NAME}
cat $^ | sed s:@NAME@:${NAME}:g > $@ cat $^ | sed s:@NAME@:${NAME}:g > $@
build/assets/index.html: ${COMMON_DIR}/index.html | build/assets build/assets/index.html: ${COMMON_DIR}/index.html | build/assets

View File

@ -1,8 +1,12 @@
#include <algorithm> #include <algorithm>
#include <mutex> #include <mutex>
#include <vector>
#include <jni.h> #include <jni.h>
#define MINIAUDIO_IMPLEMENTATION #define MINIAUDIO_IMPLEMENTATION
#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS
#define MA_ENABLE_AAUDIO
#include <miniaudio.h> #include <miniaudio.h>
#include <amidi/AMidi.h>
#include "config.h" #include "config.h"
#define BLOCK_SIZE 32 #define BLOCK_SIZE 32
@ -20,16 +24,64 @@ std::mutex mutex;
#ifdef P_MEM_REQ #ifdef P_MEM_REQ
void *mem; void *mem;
#endif #endif
#ifdef P_NOTE_ON
struct PortData {
AMidiDevice *device;
int portNumber;
AMidiOutputPort *port;
};
std::vector<PortData> midiPorts;
#define MIDI_BUFFER_SIZE 1024
uint8_t midiBuffer[MIDI_BUFFER_SIZE];
#endif
static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) {
(void)pDevice; (void)pDevice;
#if NUM_CHANNELS_IN == 0 #if NUM_CHANNELS_IN == 0
(void)pInput; (void)pInput;
#endif #else
const float *x = reinterpret_cast<const float *>(pInput); const float *x = reinterpret_cast<const float *>(pInput);
#endif
float *y = reinterpret_cast<float *>(pOutput); float *y = reinterpret_cast<float *>(pOutput);
if (mutex.try_lock()) {
for (int i = 0; i < NUM_PARAMETERS; i++)
if (config_parameters[i].out)
paramValues[i] = P_GET_PARAMETER(&instance, i);
else
P_SET_PARAMETER(&instance, i, paramValues[i]);
#ifdef P_NOTE_ON
for (std::vector<PortData>::iterator it = midiPorts.begin(); it != midiPorts.end(); it++) {
int32_t opcode;
size_t numBytes;
while (AMidiOutputPort_receive(it->port, &opcode, midiBuffer, MIDI_BUFFER_SIZE, &numBytes, NULL) > 0) {
if (opcode != AMIDI_OPCODE_DATA)
continue;
switch (midiBuffer[0] & 0xf0) {
case 0x90:
P_NOTE_ON(&instance, midiBuffer[1], midiBuffer[2]);
break;
case 0x80:
P_NOTE_OFF(&instance, midiBuffer[1]);
break;
#ifdef P_PITCH_BEND
case 0xe0:
P_PITCH_BEND(&instance, midiBuffer[2] << 7 | midiBuffer[1]);
break;
#endif
#ifdef P_MOD_WHEEL
case 0xb0:
if (midiBuffer[1] == 1)
P_MOD_WHEEL(&instance, midiBuffer[2]);
break;
#endif
}
}
}
#endif
mutex.unlock();
}
ma_uint32 i = 0; ma_uint32 i = 0;
while (i < frameCount) { while (i < frameCount) {
ma_uint32 n = std::min(frameCount - i, static_cast<ma_uint32>(BLOCK_SIZE)); ma_uint32 n = std::min(frameCount - i, static_cast<ma_uint32>(BLOCK_SIZE));
@ -42,7 +94,11 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
bufs[k][j] = x[l]; bufs[k][j] = x[l];
#endif #endif
#if NUM_CHANNELS_IN != 0
P_PROCESS(&instance, inBufs, outBufs, n); P_PROCESS(&instance, inBufs, outBufs, n);
#else
P_PROCESS(&instance, NULL, outBufs, n);
#endif
l = NUM_CHANNELS_OUT * i; l = NUM_CHANNELS_OUT * i;
for (ma_uint32 j = 0; j < n; j++) for (ma_uint32 j = 0; j < n; j++)
@ -51,15 +107,6 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
i += n; i += n;
} }
if (mutex.try_lock()) {
for (int i = 0; i < NUM_PARAMETERS; i++)
if (config_parameters[i].out)
paramValues[i] = P_GET_PARAMETER(&instance, i);
else
P_SET_PARAMETER(&instance, i, paramValues[i]);
mutex.unlock();
}
} }
extern "C" extern "C"
@ -173,3 +220,39 @@ Java_com_orastron_@JNI_NAME@_MainActivity_nativeSetParameter(JNIEnv* env, jobjec
paramValues[i] = v; paramValues[i] = v;
mutex.unlock(); mutex.unlock();
} }
#ifdef P_NOTE_ON
extern "C"
JNIEXPORT void JNICALL
Java_com_orastron_@JNI_NAME@_MainActivity_addMidiPort(JNIEnv* env, jobject thiz, jobject d, jint p) {
(void)thiz;
PortData data;
AMidiDevice_fromJava(env, d, &data.device);
data.portNumber = p;
mutex.lock();
if (AMidiOutputPort_open(data.device, p, &data.port) == AMEDIA_OK)
midiPorts.push_back(data);
mutex.unlock();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_orastron_@JNI_NAME@_MainActivity_removeMidiPort(JNIEnv* env, jobject thiz, jobject d, jint p) {
(void)thiz;
AMidiDevice *device;
AMidiDevice_fromJava(env, d, &device);
mutex.lock();
for (std::vector<PortData>::iterator it = midiPorts.begin(); it != midiPorts.end(); ) {
PortData data = *it;
if (data.device != device || data.portNumber != p) {
it++;
continue;
}
AMidiOutputPort_close(data.port);
it = midiPorts.erase(it);
}
mutex.unlock();
}
#endif

View File

@ -0,0 +1,7 @@
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
NAME := bw_example_synth_poly
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_poly.c
SYNTH := yes
include ${ROOT_DIR}/../../common/android/android.mk

View File

@ -2,5 +2,6 @@ ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
NAME := bw_example_synth_simple NAME := bw_example_synth_simple
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_simple.c SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_simple.c
SYNTH := yes
include ${ROOT_DIR}/../../common/android/android.mk include ${ROOT_DIR}/../../common/android/android.mk

View File

@ -186,11 +186,11 @@ static inline char bw_note_queue_is_valid(const bw_note_queue *BW_RESTRICT queue
return 0; return 0;
for (int i = 0; i < (int)queue->n_events; i++) { for (int i = 0; i < (int)queue->n_events; i++) {
bw_note_queue_event *ev = queue->events + i; const bw_note_queue_event *ev = queue->events + i;
if (ev->note >= 128 || !bw_is_finite(ev->status.velocity) || ev->status.velocity > 1.f) if (ev->note >= 128 || !bw_is_finite(ev->status.velocity) || ev->status.velocity > 1.f)
return 0; return 0;
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
bw_note_queue_event *ev2 = queue->events + j; const bw_note_queue_event *ev2 = queue->events + j;
if (ev2->note == ev->note) if (ev2->note == ev->note)
return 0; return 0;
} }
@ -198,7 +198,7 @@ static inline char bw_note_queue_is_valid(const bw_note_queue *BW_RESTRICT queue
int cnt = 0; int cnt = 0;
for (int i = 0; i < 128; i++) { for (int i = 0; i < 128; i++) {
bw_note_queue_status *st = queue->status + i; const bw_note_queue_status *st = queue->status + i;
if (st->pressed) if (st->pressed)
cnt++; cnt++;
if (!bw_is_finite(st->velocity) || st->velocity > 1.f) if (!bw_is_finite(st->velocity) || st->velocity > 1.f)

View File

@ -29,6 +29,8 @@
* <ul> * <ul>
* <li>Version <strong>0.6.0</strong>: * <li>Version <strong>0.6.0</strong>:
* <ul> * <ul>
* <li>Now using <code>BW_SIZE_T</code> to count voices in
* <code>bw_voice_alloc()</code>.
* <li>Added debugging code.</li> * <li>Added debugging code.</li>
* <li>Removed dependency on bw_config.</li> * <li>Removed dependency on bw_config.</li>
* </ul> * </ul>
@ -116,7 +118,7 @@ void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *
for (unsigned char i = 0; i < queue->n_events; i++) { for (unsigned char i = 0; i < queue->n_events; i++) {
bw_note_queue_event *ev = queue->events + i; bw_note_queue_event *ev = queue->events + i;
for (int j = 0; j < n_voices; j++) for (BW_SIZE_T j = 0; j < n_voices; j++)
if (!opts->is_free(voices[j]) && opts->get_note(voices[j]) == ev->note) { if (!opts->is_free(voices[j]) && opts->get_note(voices[j]) == ev->note) {
if (!ev->status.pressed || ev->went_off) if (!ev->status.pressed || ev->went_off)
opts->note_off(voices[j], ev->status.velocity); opts->note_off(voices[j], ev->status.velocity);
@ -126,7 +128,7 @@ void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *
} }
if (ev->status.pressed) { if (ev->status.pressed) {
for (int j = 0; j < n_voices; j++) for (BW_SIZE_T j = 0; j < n_voices; j++)
if (opts->is_free(voices[j])) { if (opts->is_free(voices[j])) {
opts->note_on(voices[j], ev->note, ev->status.velocity); opts->note_on(voices[j], ev->note, ev->status.velocity);
goto next_event; goto next_event;
@ -134,7 +136,7 @@ void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *
int k = -1; int k = -1;
int v = ev->note; int v = ev->note;
for (int j = 0; j < n_voices; j++) { for (BW_SIZE_T j = 0; j < n_voices; j++) {
int n = opts->get_note(voices[j]); int n = opts->get_note(voices[j]);
if (!queue->status[n].pressed && (k < 0 || (opts->priority == bw_voice_alloc_priority_low ? n > v : n < v))) { if (!queue->status[n].pressed && (k < 0 || (opts->priority == bw_voice_alloc_priority_low ? n > v : n < v))) {
v = n; v = n;
@ -146,7 +148,7 @@ void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *
continue; continue;
} }
for (int j = 0; j < n_voices; j++) { for (BW_SIZE_T j = 0; j < n_voices; j++) {
int n = opts->get_note(voices[j]); int n = opts->get_note(voices[j]);
if (opts->priority == bw_voice_alloc_priority_low ? n > v : n < v) { if (opts->priority == bw_voice_alloc_priority_low ? n > v : n < v) {
v = n; v = n;