Move dead submodules in-tree

Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
swurl 2025-05-31 02:33:02 -04:00
parent c0cceff365
commit 6c655321e6
No known key found for this signature in database
GPG key ID: A5A7629F109C8FD1
4081 changed files with 1185566 additions and 45 deletions

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.google.oboe.samples.soundboard.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.4.1)
### INCLUDE OBOE LIBRARY ###
# Set the path to the Oboe library directory
set (OBOE_DIR ../../../../..)
# Add the Oboe library as a subproject. Since Oboe is an out-of-tree source library we must also
# specify a binary directory
add_subdirectory(${OBOE_DIR} ./oboe-bin)
# Include the Oboe headers
include_directories(${OBOE_DIR}/include ${OBOE_DIR}/samples/shared ${OBOE_DIR}/samples/debug-utils)
### END OBOE INCLUDE SECTION ###
add_library( soundboard SHARED
native-lib.cpp
SoundBoardEngine.cpp
)
target_link_libraries(soundboard log oboe )
target_link_options(soundboard PRIVATE "-Wl,-z,max-page-size=16384")
# Enable optimization flags: if having problems with source level debugging,
# disable -Ofast ( and debug ), re-enable it after done debugging.
target_compile_options(soundboard PRIVATE -Wall -Werror -Ofast)

View file

@ -0,0 +1,130 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <memory>
#include "SoundBoardEngine.h"
/**
* Main audio engine for the SoundBoard sample. It is responsible for:
*
* - Creating the callback object which will be supplied when constructing the audio stream
* - Creating the playback stream, including setting the callback object
* - Creating `Synth` which will render the audio inside the callback
* - Starting the playback stream
* - Restarting the playback stream when `restart()` is called by the callback object
*
* @param numSignals
*/
SoundBoardEngine::SoundBoardEngine(int32_t numSignals) {
createCallback(numSignals);
}
SoundBoardEngine::~SoundBoardEngine() {
if (mStream) {
LOGE("SoundBoardEngine destructor was called without calling stop()."
"Please call stop() to ensure stream resources are not leaked.");
stop();
}
}
void SoundBoardEngine::noteOff(int32_t noteIndex) {
mSynth->noteOff(noteIndex);
}
void SoundBoardEngine::noteOn(int32_t noteIndex) {
mSynth->noteOn(noteIndex);
}
void SoundBoardEngine::tap(bool isDown) {
mSynth->tap(isDown);
}
void SoundBoardEngine::restart() {
stop();
start();
}
// Create the playback stream
oboe::Result SoundBoardEngine::createPlaybackStream() {
oboe::AudioStreamBuilder builder;
return builder.setSharingMode(oboe::SharingMode::Exclusive)
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
->setFormat(oboe::AudioFormat::Float)
->setDataCallback(mDataCallback)
->setErrorCallback(mErrorCallback)
->openStream(mStream);
}
// Create the callback and set its thread affinity to the supplied CPU core IDs
void SoundBoardEngine::createCallback(int32_t numSignals){
mDataCallback = std::make_shared<DefaultDataCallback>();
// Create the error callback, we supply ourselves as the parent so that we can restart the stream
// when it's disconnected
mErrorCallback = std::make_shared<DefaultErrorCallback>(*this);
mNumSignals = numSignals;
}
bool SoundBoardEngine::start() {
// It is possible for a stream's device to become disconnected during stream open or between
// stream open and stream start.
// If the stream fails to start, close the old stream and try again.
bool didStart = false;
int tryCount = 0;
do {
if (tryCount > 0) {
usleep(20 * 1000); // Sleep between tries to give the system time to settle.
}
didStart = attemptStart();
} while (!didStart && tryCount++ < 3);
if (!didStart) {
LOGE("Failed at starting the stream");
}
return didStart;
}
bool SoundBoardEngine::attemptStart() {
auto result = createPlaybackStream();
if (result == Result::OK) {
// Create our synthesizer audio source using the properties of the stream
mSynth = Synth::create(mStream->getSampleRate(), mStream->getChannelCount(), mNumSignals);
mDataCallback->reset();
mDataCallback->setSource(std::dynamic_pointer_cast<IRenderableAudio>(mSynth));
result = mStream->start();
if (result == Result::OK) {
return true;
} else {
LOGW("Failed attempt at starting the playback stream. Error: %s", convertToText(result));
return false;
}
} else {
LOGW("Failed attempt at creating the playback stream. Error: %s", convertToText(result));
return false;
}
}
bool SoundBoardEngine::stop() {
if(mStream && mStream->getState() != oboe::StreamState::Closed) {
mStream->stop();
mStream->close();
}
mStream.reset();
return true;
}

View file

@ -0,0 +1,65 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SOUNDBOARD_ENGINE_H
#define SOUNDBOARD_ENGINE_H
#include <oboe/Oboe.h>
#include <vector>
#include "Synth.h"
#include <DefaultDataCallback.h>
#include <TappableAudioSource.h>
#include <IRestartable.h>
#include <DefaultErrorCallback.h>
using namespace oboe;
class SoundBoardEngine : public IRestartable {
public:
SoundBoardEngine(int32_t numSignals);
virtual ~SoundBoardEngine();
void noteOff(int32_t noteIndex);
void noteOn(int32_t noteIndex);
void tap(bool isDown);
// from IRestartable
virtual void restart() override;
bool start();
bool stop();
private:
int32_t mNumSignals;
std::shared_ptr<AudioStream> mStream;
std::shared_ptr<Synth> mSynth;
std::shared_ptr<DefaultDataCallback> mDataCallback;
std::shared_ptr<DefaultErrorCallback> mErrorCallback;
bool attemptStart();
oboe::Result createPlaybackStream();
void createCallback(int32_t numSignals);
};
#endif //SOUNDBOARD_ENGINE_H

View file

@ -0,0 +1,94 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SOUNDBOARD_SYNTH_H
#define SOUNDBOARD_SYNTH_H
#include <array>
#include <TappableAudioSource.h>
#include <SynthSound.h>
#include <Mixer.h>
#include <MonoToStereo.h>
constexpr float kOscBaseFrequency = 196.00; // Start at G3
constexpr float kOscFrequencyMultiplier = 1.05946309436;
constexpr float kOscBaseAmplitude = 0.20;
constexpr float kOscAmplitudeMultiplier = 0.96;
class Synth : public IRenderableAudio, public ITappable {
public:
static ::std::shared_ptr<Synth> create(const int32_t sampleRate, const int32_t channelCount, const int32_t numSignals) {
return ::std::make_shared<Synth>(sampleRate, channelCount, numSignals);
}
Synth(const int32_t sampleRate, const int32_t channelCount, const int32_t numSignals) {
mNumSignals = numSignals;
mOscs = std::make_unique<SynthSound[]>(numSignals);
float curFrequency = kOscBaseFrequency;
float curAmplitude = kOscBaseAmplitude;
for (int i = 0; i < numSignals; ++i) {
mOscs[i].setSampleRate(sampleRate);
mOscs[i].setFrequency(curFrequency);
curFrequency *= kOscFrequencyMultiplier;
mOscs[i].setAmplitude(curAmplitude);
curAmplitude *= kOscAmplitudeMultiplier;
mMixer.addTrack(&mOscs[i]);
}
if (channelCount == oboe::ChannelCount::Stereo) {
mOutputStage = &mConverter;
} else {
mOutputStage = &mMixer;
}
}
void noteOff(int32_t noteIndex) {
mOscs[noteIndex].noteOff();
}
void noteOn(int32_t noteIndex) {
mOscs[noteIndex].noteOn();
}
void tap(bool isOn) override {
for (int i = 0; i < mNumSignals; i++) {
if (isOn) {
mOscs[i].noteOn();
} else {
mOscs[i].noteOff();
}
}
};
// From IRenderableAudio
void renderAudio(float *audioData, int32_t numFrames) override {
mOutputStage->renderAudio(audioData, numFrames);
};
virtual ~Synth() {
}
private:
// Rendering objects
int32_t mNumSignals;
std::unique_ptr<SynthSound[]> mOscs;
Mixer mMixer;
MonoToStereo mConverter = MonoToStereo(&mMixer);
IRenderableAudio *mOutputStage; // This will point to either the mixer or converter, so it needs to be raw
};
#endif //SOUNDBOARD_SYNTH_H

View file

@ -0,0 +1,91 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <string>
#include <vector>
#include "SoundBoardEngine.h"
extern "C" {
/**
* Start the audio engine
*
* @param env
* @param instance
* @param jCpuIds - CPU core IDs which the audio process should affine to
* @return a pointer to the audio engine. This should be passed to other methods
*/
JNIEXPORT jlong JNICALL
Java_com_google_oboe_samples_soundboard_MainActivity_startEngine(JNIEnv *env, jobject /*unused*/,
jint jNumSignals) {
LOGD("numSignals : %d", static_cast<int>(jNumSignals));
SoundBoardEngine *engine = new SoundBoardEngine(jNumSignals);
if (!engine->start()) {
LOGE("Failed to start SoundBoard Engine");
delete engine;
engine = nullptr;
} else {
LOGD("Engine Started");
}
return reinterpret_cast<jlong>(engine);
}
JNIEXPORT void JNICALL
Java_com_google_oboe_samples_soundboard_MainActivity_stopEngine(JNIEnv *env, jobject instance,
jlong jEngineHandle) {
auto engine = reinterpret_cast<SoundBoardEngine*>(jEngineHandle);
if (engine) {
engine->stop();
delete engine;
} else {
LOGD("Engine invalid, call startEngine() to create");
}
}
JNIEXPORT void JNICALL
Java_com_google_oboe_samples_soundboard_MainActivity_native_1setDefaultStreamValues(JNIEnv *env,
jclass type,
jint sampleRate,
jint framesPerBurst) {
oboe::DefaultStreamValues::SampleRate = (int32_t) sampleRate;
oboe::DefaultStreamValues::FramesPerBurst = (int32_t) framesPerBurst;
}
JNIEXPORT void JNICALL
Java_com_google_oboe_samples_soundboard_NoteListener_noteOff(JNIEnv *env, jobject thiz,
jlong engine_handle, jint noteIndex) {
auto *engine = reinterpret_cast<SoundBoardEngine*>(engine_handle);
if (engine) {
engine->noteOff(noteIndex);
} else {
LOGE("Engine handle is invalid, call createEngine() to create a new one");
}
}
JNIEXPORT void JNICALL
Java_com_google_oboe_samples_soundboard_NoteListener_noteOn(JNIEnv *env, jobject thiz,
jlong engine_handle, jint noteIndex) {
auto *engine = reinterpret_cast<SoundBoardEngine*>(engine_handle);
if (engine) {
engine->noteOn(noteIndex);
} else {
LOGE("Engine handle is invalid, call createEngine() to create a new one");
}
}
} // extern "C"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -0,0 +1,152 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.oboe.samples.soundboard
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.graphics.Point
import android.graphics.Rect
import android.media.AudioManager
import android.os.Build
import android.os.Bundle
import android.view.WindowInsets
import android.view.WindowManager
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import kotlin.math.min
class MainActivity : AppCompatActivity() {
private external fun startEngine(numSignals: Int): Long
private external fun stopEngine(engineHandle: Long)
private external fun native_setDefaultStreamValues(sampleRate: Int, framesPerBurst: Int)
companion object {
private const val DIMENSION_MIN_SIZE = 6
private const val DIMENSION_MAX_SIZE = 8
private var mNumColumns : Int = 0;
private var mNumRows : Int = 0;
private var mTiles = ArrayList<Rect>()
private var mBorders = ArrayList<Rect>()
private var mEngineHandle: Long = 0
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("soundboard")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onResume() {
super.onResume()
setup()
}
override fun onPause() {
stopEngine(mEngineHandle)
super.onPause()
}
private fun setup() {
setDefaultStreamValues(this)
calculateAndSetRectangles(this)
mEngineHandle = startEngine(mNumRows * mNumColumns)
createMusicTiles(this)
}
private fun setDefaultStreamValues(context: Context) {
val myAudioMgr = context.getSystemService(AUDIO_SERVICE) as AudioManager
val sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
val defaultSampleRate = sampleRateStr.toInt()
val framesPerBurstStr =
myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
val defaultFramesPerBurst = framesPerBurstStr.toInt()
native_setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst)
}
private fun calculateAndSetRectangles(context: Context) {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val display = windowManager.defaultDisplay
val size = Point()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics = windowManager.currentWindowMetrics
val windowInsets = windowMetrics.windowInsets
val insets = windowInsets.getInsetsIgnoringVisibility(
WindowInsets.Type.navigationBars() or WindowInsets.Type.statusBars())
val width = windowMetrics.bounds.width() - insets.left - insets.right
val height = windowMetrics.bounds.height() - insets.top - insets.bottom
size.set(width, height)
} else {
display.getSize(size) // Use getSize to exclude navigation bar if visible
}
val width = size.x
val height = size.y
if (height > width) {
mNumColumns = DIMENSION_MIN_SIZE
mNumRows = min(DIMENSION_MIN_SIZE * height / width, DIMENSION_MAX_SIZE)
} else {
mNumRows = DIMENSION_MIN_SIZE
mNumColumns = min(DIMENSION_MIN_SIZE * width / height, DIMENSION_MAX_SIZE)
}
val tileLength = min(height / mNumRows, width / mNumColumns)
val xStartLocation = (width - tileLength * mNumColumns) / 2
val yStartLocation = (height - tileLength * mNumRows) / 2
mTiles = ArrayList<Rect>()
for (i in 0 until mNumRows) {
for (j in 0 until mNumColumns) {
val rectangle = Rect(
xStartLocation + j * tileLength,
yStartLocation + i * tileLength,
xStartLocation + j * tileLength + tileLength,
yStartLocation + i * tileLength + tileLength
)
mTiles.add(rectangle)
}
}
mBorders = ArrayList<Rect>()
// Top border
mBorders.add(Rect(0, 0, width, yStartLocation))
// Bottom border
mBorders.add(Rect(0, yStartLocation + tileLength * mNumRows, width, height))
// Left border
mBorders.add(Rect(0, 0, xStartLocation, height))
// Right border
mBorders.add(Rect(xStartLocation + tileLength * mNumColumns, 0, width, height))
}
private fun createMusicTiles(context: Context) {
setContentView(MusicTileView(this, mTiles, mBorders, NoteListener(mEngineHandle),
ScreenChangeListener { setup() }))
}
class ScreenChangeListener(private var mFunc: () -> Unit) : MusicTileView.ConfigChangeListener {
override fun onConfigurationChanged() {
mFunc()
}
}
}

View file

@ -0,0 +1,171 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.oboe.samples.soundboard
import android.content.Context
import android.content.res.Configuration
import android.graphics.*
import android.util.SparseArray
import android.view.MotionEvent
import android.view.View
import androidx.core.content.ContextCompat
class MusicTileView(
context: Context?,
private val mTiles: ArrayList<Rect>,
private val mBorders: ArrayList<Rect>,
tileListener: TileListener,
configChangeListener: ConfigChangeListener
) : View(context) {
private val mIsPressedPerRectangle: BooleanArray = BooleanArray(mTiles.size)
private val mPaint: Paint = Paint()
private val mLocationsOfFingers: SparseArray<PointF> = SparseArray()
private val mTileListener: TileListener
private val mConfigChangeListener : ConfigChangeListener
interface ConfigChangeListener {
fun onConfigurationChanged()
}
interface TileListener {
fun onTileOn(index: Int)
fun onTileOff(index: Int)
}
private fun getIndexFromLocation(pointF: PointF): Int {
for (i in mTiles.indices) {
if (pointF.x > mTiles[i].left && pointF.x < mTiles[i].right && pointF.y > mTiles[i].top && pointF.y < mTiles[i].bottom) {
return i
}
}
return -1
}
override fun onDraw(canvas: Canvas) {
for (i in mTiles.indices) {
mPaint.style = Paint.Style.FILL
if (mIsPressedPerRectangle[i]) {
mPaint.color = ContextCompat.getColor(context, R.color.colorPrimary)
} else {
mPaint.color = Color.BLACK
}
canvas.drawRect(mTiles[i], mPaint)
// white border
mPaint.style = Paint.Style.STROKE
mPaint.strokeWidth = 10f
mPaint.color = Color.WHITE
canvas.drawRect(mTiles[i], mPaint)
}
for (i in mBorders.indices) {
mPaint.style = Paint.Style.FILL
mPaint.color = ContextCompat.getColor(context, R.color.colorPrimaryDark)
canvas.drawRect(mBorders[i], mPaint)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
val pointerIndex = event.actionIndex
val pointerId = event.getPointerId(pointerIndex)
val maskedAction = event.actionMasked
var didImageChange = false
when (maskedAction) {
MotionEvent.ACTION_MOVE -> {
// Create an array to check for finger changes as multiple fingers may be on the
// same tile. This two-pass algorithm records the overall difference before changing
// the actual tiles.
val notesChangedBy = IntArray(mTiles.size)
run {
val size = event.pointerCount
var i = 0
while (i < size) {
val point = mLocationsOfFingers[event.getPointerId(i)]
if (point != null) {
val prevIndex = getIndexFromLocation(point)
point.x = event.getX(i)
point.y = event.getY(i)
val newIndex = getIndexFromLocation(point)
if (newIndex != prevIndex) {
if (prevIndex != -1) {
notesChangedBy[prevIndex]--
}
if (newIndex != -1) {
notesChangedBy[newIndex]++
}
}
}
i++
}
}
// Now go through the rectangles to see if they have changed
var i = 0
while (i < mTiles.size) {
if (notesChangedBy[i] > 0) {
mIsPressedPerRectangle[i] = true
mTileListener.onTileOn(i)
didImageChange = true
} else if (notesChangedBy[i] < 0) {
mIsPressedPerRectangle[i] = false
mTileListener.onTileOff(i)
didImageChange = true
}
i++
}
}
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
val f = PointF()
f.x = event.getX(pointerIndex)
f.y = event.getY(pointerIndex)
mLocationsOfFingers.put(pointerId, f)
val curIndex = getIndexFromLocation(f)
if (curIndex != -1) {
mIsPressedPerRectangle[curIndex] = true
mTileListener.onTileOn(curIndex)
didImageChange = true
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_CANCEL -> {
val curIndex =
getIndexFromLocation(mLocationsOfFingers[event.getPointerId(pointerIndex)])
if (curIndex != -1) {
mIsPressedPerRectangle[curIndex] = false
mTileListener.onTileOff(curIndex)
didImageChange = true
}
mLocationsOfFingers.remove(pointerId)
}
}
// Calling invalidate() will force onDraw() to be called
if (didImageChange) {
invalidate()
}
return true
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
mConfigChangeListener.onConfigurationChanged()
}
init {
mTileListener = tileListener
mConfigChangeListener = configChangeListener
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.oboe.samples.soundboard
import com.google.oboe.samples.soundboard.MusicTileView.TileListener
class NoteListener(private var mEngineHandle: Long) : TileListener {
private external fun noteOn(engineHandle: Long, noteIndex: Int)
private external fun noteOff(engineHandle: Long, noteIndex: Int)
override fun onTileOn(index: Int) {
noteOn(mEngineHandle, index)
}
override fun onTileOff(index: Int) {
noteOff(mEngineHandle, index)
}
}

View file

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#700000</color>
<color name="colorPrimaryDark">#280000</color>
<color name="colorAccent">#D81B60</color>
</resources>

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">soundboard</string>
</resources>

View file

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>