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
Signed by: crueter
GPG key ID: A5A7629F109C8FD1
4081 changed files with 1185566 additions and 45 deletions

View file

@ -0,0 +1,67 @@
**DrumThumper**
==========
Oboe playback sample app.
## Abstract
**DrumThumper** is a "Drum Pad" app which demonstrates best-practices for low-latency audio playback using the Android **Oboe** API.
**DrumThumper** consists of a set of trigger pad widgets and an optional UI for controlling the level and stereo placement of each of the virtual drums.
The audio samples are stored in application resources as WAV data. This is parsed and loaded (by routines in **parselib**) into memory blocks.
The audio samples are mixed and played by routines in **iolib**.
**DrumThumper** is written in a combination of Kotlin for the UI and JNI/C++ for the player components (to demonstrate accessing native code from a Kotlin or Java application).
## Scope
**DrumThumper** is designed with the following goals in mind:
* To demonstrate the most efficient means of playing audio with the lowest possible latency.
* To demonstrate how to play multiple sources of audio mixed into a single Oboe stream.
* To demonstrate the life-cycle of an Oboe audio stream and it's relationship to the application lifecycle.
* To demonstrate the correct handling of playback errors and output device connection/disconnection.
Secondarily, **DrumThumper** demonstrates:
* Using Android "assets" for audio data.
* The mechanism for calling native (C/C++) audio functionality from a Kotlin/Java app.
* A mechanism for sharing binary data between a Kotlin/Java app with the native (C/C++) layer.
* A mechanism for parsing/loading one type (WAV) of audio data.
* How to control the relative levels (gain) of audio sources mixed into an output stream.
* How to locate a mono data source in a stereo output stream.
* How to use the Oboe resampler to resample source audio to the device playback rate, and therefore not incur this overhead at playback time.
To keep things simple, **DrumThumper** specifically does not:
* Does not support audio samples in other than 16-bit PCM Samples. It does not support different PCM formats.
* Does not support non-WAV audio data (such as AIFF).
* Does not support compressed audio data.
**DrumThumper** now supports different sample rates for the source samples.
**DrumThumper** now supports mono and stereo sources.
## DrumThumper project structure
### Kotlin App Layer
Contains classes for the application logic and defines the methods for accessing the native data and player functionality.
### Native (C++) layer
Contains the implementation of the `native` (JNI) methods defined in the `DrumPlayer` (Kotlin) class.
### Dependent Libraries
* **iolib**
Classes for playing audio data.
* **parselib**
Classes for parsing and loading audio data from WAV resources.
## App
* DrumPlayer.kt
The Kotlin class which provides the audio playback functionality by interfacing with the native (C++) libraries.
* DrumThumperActivity.kt
The main application logic.
* TriggerPad.kt
An Android View subclass which implements the "trigger pad" UI widgets
## Native-interface (JNI)
* DrumPlayerJNI.cpp
This is where all the access to the native functionality is implemented.
## Screenshots
![drumthumper-screenshot](drumthumper-screenshot.png)

View file

@ -0,0 +1,52 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
defaultConfig {
// Usually the applicationId follows the same scheme as the application package name,
// however, this sample will be published on the Google Play Store which will not allow an
// applicationId starting with "com.google" as this is reserved for official Google
// products. The current owner of the DrumThumper sample apps on Google Play is Paul McLean,
// who publishes using the application Id prefix of "com.plausiblesoftware".
applicationId "com.plausiblesoftware.drumthumper"
minSdkVersion 23
compileSdkVersion 35
targetSdkVersion 35
versionCode 2
versionName "1.01"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_18
targetCompatibility JavaVersion.VERSION_18
}
kotlinOptions {
jvmTarget = '18'
}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
namespace 'com.plausiblesoftware.drumthumper'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "androidx.core:core-ktx:$core_version"
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation project(path: ':iolib')
implementation project(path: ':parselib')
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

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.plausiblesoftware.drumthumper.DrumThumperActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,74 @@
#
# Copyright 2019 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.
#
cmake_minimum_required(VERSION 3.4.1)
# Pull in parselib
set (PARSELIB_DIR ../../../../parselib)
#message("PARSELIB_DIR = " + ${PARSELIB_DIR})
# Pull in iolib
set (IOLIB_DIR ../../../../iolib)
#message("IOLIB_DIR = " + ${IOLIB_DIR})
# Set the path to the Oboe library directory
set (OBOE_DIR ../../../../../)
#message("OBOE_DIR = " + ${OBOE_DIR})
add_subdirectory(${OBOE_DIR} ./oboe-bin)
# include folders
include_directories(
${OBOE_DIR}/include
${CMAKE_CURRENT_LIST_DIR}
)
include(${PARSELIB_DIR}/src/main/cpp/CMakeLists.txt)
include(${IOLIB_DIR}/src/main/cpp/CMakeLists.txt)
# Include the WavLib headers and shared sample code
include_directories(
${PARSELIB_DIR}/src/main/cpp
${IOLIB_DIR}/src/main/cpp)
# App specific sources
set (APP_SOURCES
DrumPlayerJNI.cpp
)
# Build the drumthumper (native) library
add_library(drumthumper SHARED
${APP_SOURCES}
)
# Enable optimization flags: if having problems with source level debugging,
# disable -Ofast ( and debug ), re-enable after done debugging.
target_compile_options(drumthumper PRIVATE -Wall -Werror "$<$<CONFIG:RELEASE>:-Ofast>")
target_link_libraries( # Specifies the target library.
drumthumper
-Wl,--whole-archive
iolib
parselib
-Wl,--no-whole-archive
oboe
# Links the target library to the log library
# included in the NDK.
log)
target_link_options(drumthumper PRIVATE "-Wl,-z,max-page-size=16384")

View file

@ -0,0 +1,172 @@
/*
* Copyright 2019 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <android/log.h>
// parselib includes
#include <stream/MemInputStream.h>
#include <wav/WavStreamReader.h>
#include <player/OneShotSampleSource.h>
#include <player/SimpleMultiPlayer.h>
static const char* TAG = "DrumPlayerJNI";
// JNI functions are "C" calling convention
#ifdef __cplusplus
extern "C" {
#endif
using namespace iolib;
using namespace parselib;
static SimpleMultiPlayer sDTPlayer;
/**
* Native (JNI) implementation of DrumPlayer.setupAudioStreamNative()
*/
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_setupAudioStreamNative(
JNIEnv* env, jobject, jint numChannels) {
__android_log_print(ANDROID_LOG_INFO, TAG, "%s", "init()");
sDTPlayer.setupAudioStream(numChannels);
}
JNIEXPORT void JNICALL
Java_com_plausiblesoftware_drumthumper_DrumPlayer_startAudioStreamNative(
JNIEnv *env, jobject thiz) {
sDTPlayer.startStream();
}
/**
* Native (JNI) implementation of DrumPlayer.teardownAudioStreamNative()
*/
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_teardownAudioStreamNative(JNIEnv* , jobject) {
__android_log_print(ANDROID_LOG_INFO, TAG, "%s", "deinit()");
// we know in this case that the sample buffers are all 1-channel, 44.1K
sDTPlayer.teardownAudioStream();
}
/**
* Native (JNI) implementation of DrumPlayer.allocSampleDataNative()
*/
/**
* Native (JNI) implementation of DrumPlayer.loadWavAssetNative()
*/
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_loadWavAssetNative(
JNIEnv* env, jobject, jbyteArray bytearray, jint index, jfloat pan) {
int len = env->GetArrayLength (bytearray);
unsigned char* buf = new unsigned char[len];
env->GetByteArrayRegion (bytearray, 0, len, reinterpret_cast<jbyte*>(buf));
MemInputStream stream(buf, len);
WavStreamReader reader(&stream);
reader.parse();
reader.getNumChannels();
SampleBuffer* sampleBuffer = new SampleBuffer();
sampleBuffer->loadSampleData(&reader);
OneShotSampleSource* source = new OneShotSampleSource(sampleBuffer, pan);
sDTPlayer.addSampleSource(source, sampleBuffer);
delete[] buf;
}
/**
* Native (JNI) implementation of DrumPlayer.unloadWavAssetsNative()
*/
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_unloadWavAssetsNative(JNIEnv* env, jobject) {
sDTPlayer.unloadSampleData();
}
/**
* Native (JNI) implementation of DrumPlayer.trigger()
*/
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_trigger(JNIEnv* env, jobject, jint index) {
sDTPlayer.triggerDown(index);
}
/**
* Native (JNI) implementation of DrumPlayer.trigger()
*/
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_stopTrigger(JNIEnv* env, jobject, jint index) {
sDTPlayer.triggerUp(index);
}
/**
* Native (JNI) implementation of DrumPlayer.getOutputReset()
*/
JNIEXPORT jboolean JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_getOutputReset(JNIEnv*, jobject) {
return sDTPlayer.getOutputReset();
}
/**
* Native (JNI) implementation of DrumPlayer.clearOutputReset()
*/
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_clearOutputReset(JNIEnv*, jobject) {
sDTPlayer.clearOutputReset();
}
/**
* Native (JNI) implementation of DrumPlayer.restartStream()
*/
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_restartStream(JNIEnv*, jobject) {
sDTPlayer.resetAll();
if (sDTPlayer.openStream() && sDTPlayer.startStream()){
__android_log_print(ANDROID_LOG_INFO, TAG, "openStream successful");
} else {
__android_log_print(ANDROID_LOG_ERROR, TAG, "openStream failed");
}
}
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_setPan(
JNIEnv *env, jobject thiz, jint index, jfloat pan) {
sDTPlayer.setPan(index, pan);
}
JNIEXPORT jfloat JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_getPan(
JNIEnv *env, jobject thiz, jint index) {
return sDTPlayer.getPan(index);
}
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_setGain(
JNIEnv *env, jobject thiz, jint index, jfloat gain) {
sDTPlayer.setGain(index, gain);
}
JNIEXPORT jfloat JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_getGain(
JNIEnv *env, jobject thiz, jint index) {
return sDTPlayer.getGain(index);
}
JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_setLoopMode(
JNIEnv *env, jobject thiz, jint index, jboolean isLoopMode) {
sDTPlayer.setLoopMode(index, isLoopMode);
}
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,116 @@
/*
* Copyright 2019 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.plausiblesoftware.drumthumper
import android.content.res.AssetManager
import android.util.Log
import java.io.IOException
class DrumPlayer {
companion object {
// Sample attributes
val NUM_PLAY_CHANNELS: Int = 2 // The number of channels in the player Stream.
// Stereo Playback, set to 1 for Mono playback
// Sample Buffer IDs
val BASSDRUM: Int = 0
val SNAREDRUM: Int = 1
val CRASHCYMBAL: Int = 2
val RIDECYMBAL: Int = 3
val MIDTOM: Int = 4
val LOWTOM: Int = 5
val HIHATOPEN: Int = 6
val HIHATCLOSED: Int = 7
// initial pan position for each drum sample
val PAN_BASSDRUM: Float = 0f // Dead Center
val PAN_SNAREDRUM: Float = -0.75f // Mostly Left
val PAN_CRASHCYMBAL: Float = -0.75f // Mostly Left
val PAN_RIDECYMBAL: Float = 1.0f // Hard Right
val PAN_MIDTOM: Float = 0.25f // A little Right
val PAN_LOWTOM: Float = 0.75f // Mostly Right
val PAN_HIHATOPEN: Float = -1.0f // Hard Left
val PAN_HIHATCLOSED: Float = -1.0f // Hard Left
// Logging Tag
val TAG: String = "DrumPlayer"
}
fun setupAudioStream() {
setupAudioStreamNative(NUM_PLAY_CHANNELS)
}
fun startAudioStream() {
startAudioStreamNative()
}
fun teardownAudioStream() {
teardownAudioStreamNative()
}
// asset-based samples
fun loadWavAssets(assetMgr: AssetManager) {
loadWavAsset(assetMgr, "KickDrum.wav", BASSDRUM, PAN_BASSDRUM)
loadWavAsset(assetMgr, "SnareDrum.wav", SNAREDRUM, PAN_SNAREDRUM)
loadWavAsset(assetMgr, "CrashCymbal.wav", CRASHCYMBAL, PAN_CRASHCYMBAL)
loadWavAsset(assetMgr, "RideCymbal.wav", RIDECYMBAL, PAN_RIDECYMBAL)
loadWavAsset(assetMgr, "MidTom.wav", MIDTOM, PAN_MIDTOM)
loadWavAsset(assetMgr, "LowTom.wav", LOWTOM, PAN_LOWTOM)
loadWavAsset(assetMgr, "HiHat_Open.wav", HIHATOPEN, PAN_HIHATOPEN)
loadWavAsset(assetMgr, "HiHat_Closed.wav", HIHATCLOSED, PAN_HIHATCLOSED)
}
fun unloadWavAssets() {
unloadWavAssetsNative()
}
private fun loadWavAsset(assetMgr: AssetManager, assetName: String, index: Int, pan: Float) {
try {
val assetFD = assetMgr.openFd(assetName)
val dataStream = assetFD.createInputStream()
val dataLen = assetFD.getLength().toInt()
val dataBytes = ByteArray(dataLen)
dataStream.read(dataBytes, 0, dataLen)
loadWavAssetNative(dataBytes, index, pan)
assetFD.close()
} catch (ex: IOException) {
Log.i(TAG, "IOException$ex")
}
}
private external fun setupAudioStreamNative(numChannels: Int)
private external fun startAudioStreamNative()
private external fun teardownAudioStreamNative()
private external fun loadWavAssetNative(wavBytes: ByteArray, index: Int, pan: Float)
private external fun unloadWavAssetsNative()
external fun trigger(drumIndex: Int)
external fun stopTrigger(drumIndex: Int)
external fun setPan(index: Int, pan: Float)
external fun getPan(index: Int): Float
external fun setGain(index: Int, gain: Float)
external fun getGain(index: Int): Float
external fun setLoopMode(index: Int, isLoopMode: Boolean)
external fun getOutputReset() : Boolean
external fun clearOutputReset()
external fun restartStream()
}

View file

@ -0,0 +1,365 @@
/*
* Copyright 2019 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.plausiblesoftware.drumthumper
import android.content.Context
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.LinearLayout
import android.widget.SeekBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.time.LocalDateTime
import java.util.Timer
import kotlin.concurrent.schedule
class DrumThumperActivity : AppCompatActivity(),
TriggerPad.DrumPadTriggerListener,
SeekBar.OnSeekBarChangeListener,
View.OnClickListener {
private val TAG = "DrumThumperActivity"
private var mAudioMgr: AudioManager? = null
private var mDrumPlayer = DrumPlayer()
private val mUseDeviceChangeFallback = false
private val mSwitchTimerMs = 500L
private var mDevicesInitialized = false
private var mDeviceListener: DeviceListener = DeviceListener()
private var mMixControlsShowing = false
// Store the loop mode states for each drum
private val mLoopModes = mutableMapOf(
DrumPlayer.BASSDRUM to false,
DrumPlayer.SNAREDRUM to false,
DrumPlayer.CRASHCYMBAL to false,
DrumPlayer.RIDECYMBAL to false,
DrumPlayer.MIDTOM to false,
DrumPlayer.LOWTOM to false,
DrumPlayer.HIHATOPEN to false,
DrumPlayer.HIHATCLOSED to false
)
// Store the button references
private val mLoopButtons = mutableMapOf<Int, Button>()
init {
// Load the library containing the a native code including the JNI functions
System.loadLibrary("drumthumper")
}
inner class DeviceListener: AudioDeviceCallback() {
private fun logDevices(label: String, devices: Array<AudioDeviceInfo> ) {
Log.i(TAG, label + " " + devices.size)
for(device in devices) {
Log.i(TAG, " " + device.getProductName().toString()
+ " type:" + device.getType()
+ " source:" + device.isSource()
+ " sink:" + device.isSink())
}
}
override fun onAudioDevicesAdded(addedDevices: Array<AudioDeviceInfo> ) {
// Note: This will get called when the callback is installed.
if (mDevicesInitialized) {
logDevices("onAudioDevicesAdded", addedDevices)
// This is not the initial callback, so devices have changed
Toast.makeText(applicationContext, "Added Device", Toast.LENGTH_LONG).show()
resetOutput()
}
mDevicesInitialized = true
}
override fun onAudioDevicesRemoved(removedDevices: Array<AudioDeviceInfo> ) {
logDevices("onAudioDevicesRemoved", removedDevices)
Toast.makeText(applicationContext, "Removed Device", Toast.LENGTH_LONG).show()
resetOutput()
}
private fun resetOutput() {
Log.i(TAG, "resetOutput() time:" + LocalDateTime.now() + " native reset:" + mDrumPlayer.getOutputReset())
if (mDrumPlayer.getOutputReset()) {
// the (native) stream has been reset by the onErrorAfterClose() callback
mDrumPlayer.clearOutputReset()
} else {
// give the (native) stream a chance to close it.
val timer = Timer("stream restart timer time:" + LocalDateTime.now(),
false)
// schedule a single event
timer.schedule(mSwitchTimerMs) {
if (!mDrumPlayer.getOutputReset()) {
// still didn't get reset, so lets do it ourselves
Log.i(TAG, "restartStream() time:" + LocalDateTime.now())
mDrumPlayer.restartStream()
}
}
}
}
}
//
// UI Helpers
//
val GAIN_FACTOR = 100.0f
val MAX_PAN_POSITION = 200.0f
val HALF_PAN_POSITION = MAX_PAN_POSITION / 2.0f
private fun gainPosToGainVal(pos: Int) : Float {
// map 0 -> 200 to 0.0f -> 2.0f
return pos.toFloat() / GAIN_FACTOR
}
private fun gainValToGainPos(value: Float) : Int {
return (value * GAIN_FACTOR).toInt()
}
private fun panPosToPanVal(pos: Int) : Float {
// map 0 -> 200 to -1.0f -> 1..0f
return (pos.toFloat() - HALF_PAN_POSITION) / HALF_PAN_POSITION
}
private fun panValToPanPos(value: Float) : Int {
// map -1.0f -> 1.0f to 0 -> 200
return ((value * HALF_PAN_POSITION) + HALF_PAN_POSITION).toInt()
}
private fun showMixControls(show : Boolean) {
mMixControlsShowing = show
val showFlag = if (mMixControlsShowing) View.VISIBLE else View.GONE
findViewById<LinearLayout>(R.id.kickMixControls).setVisibility(showFlag)
findViewById<LinearLayout>(R.id.snareMixControls).setVisibility(showFlag)
findViewById<LinearLayout>(R.id.hihatOpenMixControls).setVisibility(showFlag)
findViewById<LinearLayout>(R.id.hihatClosedMixControls).setVisibility(showFlag)
findViewById<LinearLayout>(R.id.midTomMixControls).setVisibility(showFlag)
findViewById<LinearLayout>(R.id.lowTomMixControls).setVisibility(showFlag)
findViewById<LinearLayout>(R.id.rideMixControls).setVisibility(showFlag)
findViewById<LinearLayout>(R.id.crashMixControls).setVisibility(showFlag)
findViewById<Button>(R.id.mixCtrlBtn).text =
if (mMixControlsShowing) "Hide Mix Controls" else "Show Mix Controls"
}
private fun connectMixSliders(panSliderId : Int, gainSliderId : Int, drumIndex : Int) {
val panSeekbar = findViewById<SeekBar>(panSliderId)
panSeekbar.setOnSeekBarChangeListener(this)
panSeekbar.progress = panValToPanPos(mDrumPlayer.getPan(drumIndex))
val gainSeekbar = findViewById<SeekBar>(gainSliderId)
gainSeekbar.setOnSeekBarChangeListener(this)
gainSeekbar.progress = gainValToGainPos(mDrumPlayer.getGain(drumIndex))
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAudioMgr = getSystemService(Context.AUDIO_SERVICE) as AudioManager
}
override fun onStart() {
super.onStart()
mDrumPlayer.setupAudioStream()
mDrumPlayer.loadWavAssets(getAssets())
mDrumPlayer.startAudioStream()
if (mUseDeviceChangeFallback) {
mAudioMgr!!.registerAudioDeviceCallback(mDeviceListener, null)
}
}
override fun onResume() {
super.onResume()
// UI
setContentView(R.layout.drumthumper_activity)
// "Kick" drum
findViewById<TriggerPad>(R.id.kickPad).addListener(this)
connectMixSliders(R.id.kickPan, R.id.kickGain, DrumPlayer.BASSDRUM)
mLoopButtons[DrumPlayer.BASSDRUM] = findViewById(R.id.kickLoopBtn)
mLoopButtons[DrumPlayer.BASSDRUM]?.setOnClickListener(this)
// Snare drum
findViewById<TriggerPad>(R.id.snarePad).addListener(this)
connectMixSliders(R.id.snarePan, R.id.snareGain, DrumPlayer.SNAREDRUM)
mLoopButtons[DrumPlayer.SNAREDRUM] = findViewById(R.id.snareLoopBtn)
mLoopButtons[DrumPlayer.SNAREDRUM]?.setOnClickListener(this)
// Mid tom
findViewById<TriggerPad>(R.id.midTomPad).addListener(this)
connectMixSliders(R.id.midTomPan, R.id.midTomGain, DrumPlayer.MIDTOM)
mLoopButtons[DrumPlayer.MIDTOM] = findViewById(R.id.midTomLoopBtn)
mLoopButtons[DrumPlayer.MIDTOM]?.setOnClickListener(this)
// Low tom
findViewById<TriggerPad>(R.id.lowTomPad).addListener(this)
connectMixSliders(R.id.lowTomPan, R.id.lowTomGain, DrumPlayer.LOWTOM)
mLoopButtons[DrumPlayer.LOWTOM] = findViewById(R.id.lowTomLoopBtn)
mLoopButtons[DrumPlayer.LOWTOM]?.setOnClickListener(this)
// Open hihat
findViewById<TriggerPad>(R.id.hihatOpenPad).addListener(this)
connectMixSliders(R.id.hihatOpenPan, R.id.hihatOpenGain, DrumPlayer.HIHATOPEN)
mLoopButtons[DrumPlayer.HIHATOPEN] = findViewById(R.id.hihatOpenLoopBtn)
mLoopButtons[DrumPlayer.HIHATOPEN]?.setOnClickListener(this)
// Closed hihat
findViewById<TriggerPad>(R.id.hihatClosedPad).addListener(this)
connectMixSliders(R.id.hihatClosedPan, R.id.hihatClosedGain, DrumPlayer.HIHATCLOSED)
mLoopButtons[DrumPlayer.HIHATCLOSED] = findViewById(R.id.hihatClosedLoopBtn)
mLoopButtons[DrumPlayer.HIHATCLOSED]?.setOnClickListener(this)
// Ride cymbal
findViewById<TriggerPad>(R.id.ridePad).addListener(this)
connectMixSliders(R.id.ridePan, R.id.rideGain, DrumPlayer.RIDECYMBAL)
mLoopButtons[DrumPlayer.RIDECYMBAL] = findViewById(R.id.rideLoopBtn)
mLoopButtons[DrumPlayer.RIDECYMBAL]?.setOnClickListener(this)
// Crash cymbal
findViewById<TriggerPad>(R.id.crashPad).addListener(this)
connectMixSliders(R.id.crashPan, R.id.crashGain, DrumPlayer.CRASHCYMBAL)
mLoopButtons[DrumPlayer.CRASHCYMBAL] = findViewById(R.id.crashLoopBtn)
mLoopButtons[DrumPlayer.CRASHCYMBAL]?.setOnClickListener(this)
findViewById<Button>(R.id.mixCtrlBtn).setOnClickListener(this)
showMixControls(false)
}
override fun onStop() {
if (mUseDeviceChangeFallback) {
mAudioMgr!!.unregisterAudioDeviceCallback(mDeviceListener)
}
mDrumPlayer.teardownAudioStream()
mDrumPlayer.unloadWavAssets()
super.onStop()
}
//
// DrumPad.DrumPadTriggerListener
//
override fun triggerDown(pad: TriggerPad) {
// trigger the sound based on the pad
when (pad.id) {
R.id.kickPad -> mDrumPlayer.trigger(DrumPlayer.BASSDRUM)
R.id.snarePad -> mDrumPlayer.trigger(DrumPlayer.SNAREDRUM)
R.id.midTomPad -> mDrumPlayer.trigger(DrumPlayer.MIDTOM)
R.id.lowTomPad -> mDrumPlayer.trigger(DrumPlayer.LOWTOM)
R.id.hihatOpenPad -> {
mDrumPlayer.trigger(DrumPlayer.HIHATOPEN)
// For a real drum set, hi-hat can play either the open or the closed sound at any
// given time.
mDrumPlayer.stopTrigger(DrumPlayer.HIHATCLOSED)
}
R.id.hihatClosedPad -> {
mDrumPlayer.trigger(DrumPlayer.HIHATCLOSED)
// For a real drum set, hi-hat can play either the open or the closed sound at any
// given time.
mDrumPlayer.stopTrigger(DrumPlayer.HIHATOPEN)
}
R.id.ridePad -> mDrumPlayer.trigger(DrumPlayer.RIDECYMBAL)
R.id.crashPad -> mDrumPlayer.trigger(DrumPlayer.CRASHCYMBAL)
}
}
override fun triggerUp(pad: TriggerPad) {
// NOP
}
//
// SeekBar.OnSeekBarChangeListener
//
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
when (seekBar!!.id) {
// BASSDRUM
R.id.kickGain -> mDrumPlayer.setGain(DrumPlayer.BASSDRUM, gainPosToGainVal(progress))
R.id.kickPan -> mDrumPlayer.setPan(DrumPlayer.BASSDRUM, panPosToPanVal(progress))
// SNAREDRUM
R.id.snareGain -> mDrumPlayer.setGain(DrumPlayer.SNAREDRUM, gainPosToGainVal(progress))
R.id.snarePan -> mDrumPlayer.setPan(DrumPlayer.SNAREDRUM, panPosToPanVal(progress))
// MIDTOM
R.id.midTomGain -> mDrumPlayer.setGain(DrumPlayer.MIDTOM, gainPosToGainVal(progress))
R.id.midTomPan -> mDrumPlayer.setPan(DrumPlayer.MIDTOM, panPosToPanVal(progress))
// LOWTOM
R.id.lowTomGain -> mDrumPlayer.setGain(DrumPlayer.LOWTOM, gainPosToGainVal(progress))
R.id.lowTomPan -> mDrumPlayer.setPan(DrumPlayer.LOWTOM, panPosToPanVal(progress))
// HIHATOPEN
R.id.hihatOpenGain -> mDrumPlayer.setGain(DrumPlayer.HIHATOPEN, gainPosToGainVal(progress))
R.id.hihatOpenPan -> mDrumPlayer.setPan(DrumPlayer.HIHATOPEN, panPosToPanVal(progress))
// HIHATCLOSED
R.id.hihatClosedGain -> mDrumPlayer.setGain(DrumPlayer.HIHATCLOSED, gainPosToGainVal(progress))
R.id.hihatClosedPan -> mDrumPlayer.setPan(DrumPlayer.HIHATCLOSED, panPosToPanVal(progress))
// RIDECYMBAL
R.id.rideGain -> mDrumPlayer.setGain(DrumPlayer.RIDECYMBAL, gainPosToGainVal(progress))
R.id.ridePan -> mDrumPlayer.setPan(DrumPlayer.RIDECYMBAL, panPosToPanVal(progress))
// CRASHCYMBAL
R.id.crashGain -> mDrumPlayer.setGain(DrumPlayer.CRASHCYMBAL, gainPosToGainVal(progress))
R.id.crashPan -> mDrumPlayer.setPan(DrumPlayer.CRASHCYMBAL, panPosToPanVal(progress))
}
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
// NOP
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
// NOP
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.mixCtrlBtn -> showMixControls(!mMixControlsShowing)
R.id.kickLoopBtn -> handleLoopButtonClick(DrumPlayer.BASSDRUM)
R.id.snareLoopBtn -> handleLoopButtonClick(DrumPlayer.SNAREDRUM)
R.id.midTomLoopBtn -> handleLoopButtonClick(DrumPlayer.MIDTOM)
R.id.lowTomLoopBtn -> handleLoopButtonClick(DrumPlayer.LOWTOM)
R.id.hihatOpenLoopBtn -> handleLoopButtonClick(DrumPlayer.HIHATOPEN)
R.id.hihatClosedLoopBtn -> handleLoopButtonClick(DrumPlayer.HIHATCLOSED)
R.id.rideLoopBtn -> handleLoopButtonClick(DrumPlayer.RIDECYMBAL)
R.id.crashLoopBtn -> handleLoopButtonClick(DrumPlayer.CRASHCYMBAL)
}
}
private fun handleLoopButtonClick(drumIndex: Int) {
// Toggle the loop mode
mLoopModes[drumIndex] = !mLoopModes[drumIndex]!!
mDrumPlayer.setLoopMode(drumIndex, mLoopModes[drumIndex]!!)
// Update the button appearance
val button = mLoopButtons[drumIndex]
button?.setTextColor(if (mLoopModes[drumIndex]!!) getColor(R.color.red) else getColor(R.color.black))
}
}

View file

@ -0,0 +1,205 @@
/*
* Copyright 2019 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.plausiblesoftware.drumthumper
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
class TriggerPad: View {
private val mDrawRect = RectF()
private val mPaint = Paint()
private val mUpColor = Color.LTGRAY
private val mDownColor = Color.DKGRAY
private var mIsDown = false
private var mText = "DrumPad"
private var mTextSizeSp = 28.0f
private val mTextColor = Color.BLACK
val DISPLAY_MASK = 0x00000003
val DISPLAY_RECT = 0x00000000
val DISPLAY_CIRCLE = 0x00000001
val DISPLAY_ROUND_RECT = 0x00000002
private var mDisplayFlags = DISPLAY_ROUND_RECT
interface DrumPadTriggerListener {
fun triggerDown(pad: TriggerPad)
fun triggerUp(pad: TriggerPad)
}
var mListeners = ArrayList<DrumPadTriggerListener>()
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
extractAttributes(attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int): super(context, attrs, defStyle) {
extractAttributes(attrs)
}
//
// Attributes
//
private fun extractAttributes(attrs: AttributeSet) {
val xmlns = "http://schemas.android.com/apk/res/android"
val textVal = attrs.getAttributeValue(xmlns, "text")
if (textVal != null) {
mText = textVal
}
}
//
// Layout Routines
//
private fun calcTextSizeInPixels(): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
mTextSizeSp,
resources.displayMetrics
)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
val padLeft = paddingLeft
val padRight = paddingRight
val padTop = paddingTop
val padBottom = paddingBottom
mDrawRect.set(padLeft.toFloat(),
padTop.toFloat(),
w - padRight.toFloat(),
h - padBottom.toFloat())
// mTextSize = mDrawRect.bottom / 4.0f
}
override fun onMeasure (widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = MeasureSpec.getSize(widthMeasureSpec)
val padTop = paddingTop
val padBottom = paddingBottom
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
var height = MeasureSpec.getSize(heightMeasureSpec)
val textSizePixels = calcTextSizeInPixels()
when (heightMode) {
MeasureSpec.AT_MOST -> run {
// mText = "AT_MOST"
val newHeight = (textSizePixels.toInt() * 2) + padTop + padBottom
height = minOf(height, newHeight) }
MeasureSpec.EXACTLY -> run {
/*mText = "EXACTLY"*/ }
MeasureSpec.UNSPECIFIED -> run {
// mText = "UNSPECIFIED"
height = textSizePixels.toInt() }
}
setMeasuredDimension(width, height)
}
//
// Drawing Routines
//
override fun onDraw(canvas: Canvas) {
// Face
if (mIsDown) {
mPaint.color = mDownColor
} else {
mPaint.color = mUpColor
}
when (mDisplayFlags and DISPLAY_MASK) {
DISPLAY_RECT -> canvas.drawRect(mDrawRect, mPaint)
DISPLAY_CIRCLE -> run {
val midX = mDrawRect.left + mDrawRect.width() / 2.0f
val midY = mDrawRect.top + mDrawRect.height() / 2.0f
val radius = minOf(mDrawRect.height() / 2.0f, mDrawRect.width() / 2.0f)
canvas.drawCircle(midX, midY, radius - 5.0f, mPaint)
}
DISPLAY_ROUND_RECT -> run {
val rad = minOf(mDrawRect.width() / 8.0f, mDrawRect.height() / 8.0f)
canvas.drawRoundRect(mDrawRect, rad, rad, mPaint)
}
}
// Text
val midX = mDrawRect.width() / 2
mPaint.textSize = calcTextSizeInPixels()
val textWidth = mPaint.measureText(mText)
mPaint.color = mTextColor
val textSizePixels = calcTextSizeInPixels()
canvas.drawText(mText, mDrawRect.left + midX - textWidth / 2,
mDrawRect.bottom/2 + textSizePixels/2, mPaint)
}
//
// Input Routines
//
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.actionMasked == MotionEvent.ACTION_DOWN ||
event.actionMasked == MotionEvent.ACTION_POINTER_DOWN) {
mIsDown = true
triggerDown()
invalidate()
return true
} else if (event.actionMasked == MotionEvent.ACTION_UP) {
mIsDown = false
triggerUp()
invalidate()
return true
}
return false
}
//
// Event Listeners
//
fun addListener(listener: DrumPadTriggerListener) {
mListeners.add(listener)
}
private fun triggerDown() {
for( listener in mListeners) {
listener.triggerDown(this)
}
}
private fun triggerUp() {
for( listener in mListeners) {
listener.triggerUp(this)
}
}
}

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

View file

@ -0,0 +1,611 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:orientation="vertical"
tools:context="com.plausiblesoftware.drumthumper.DrumThumperActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="128dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/kickPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Kick" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/kickMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/kickPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/kickGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/kickLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/snarePad"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Snare" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/snareMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/snarePan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/snareGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/snareLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/hihatOpenPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Open Hat" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/hihatOpenMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/hihatOpenPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/hihatOpenGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/hihatOpenLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/hihatClosedPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Closed Hat" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/hihatClosedMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/hihatClosedPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/hihatClosedGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/hihatClosedLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="128dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/midTomPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Mid Tom" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/midTomMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/midTomPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/midTomGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/midTomLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/lowTomPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Low Tom" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/lowTomMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/lowTomPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/lowTomGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/lowTomLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/ridePad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Ride" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/rideMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/ridePan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/rideGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/rideLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/crashPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Crash" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/crashMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/crashPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/crashGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/crashLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hide/Show Mix Controls"
android:id="@+id/mixCtrlBtn"/>
</LinearLayout>

View file

@ -0,0 +1,623 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:orientation="vertical"
tools:context="com.plausiblesoftware.drumthumper.DrumThumperActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="128dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/kickPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Kick" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/kickMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/kickPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/kickGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/kickLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/snarePad"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Snare" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/snareMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/snarePan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/snareGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/snareLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="128dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/hihatOpenPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Open Hat" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/hihatOpenMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/hihatOpenPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/hihatOpenGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/hihatOpenLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/hihatClosedPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Closed Hat" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/hihatClosedMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/hihatClosedPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/hihatClosedGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/hihatClosedLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="128dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/midTomPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Mid Tom" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/midTomMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/midTomPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/midTomGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/midTomLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/lowTomPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Low Tom" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/lowTomMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/lowTomPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/lowTomGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/lowTomLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="128dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/ridePad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Ride" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/rideMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/ridePan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/rideGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/rideLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_weight=".5"
android:orientation="vertical">
<com.plausiblesoftware.drumthumper.TriggerPad
android:id="@+id/crashPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
android:text="Crash" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/crashMixControls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="P:"/>
<SeekBar
android:id="@+id/crashPan"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"/>
<SeekBar
android:id="@+id/crashGain"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="5dp"
android:max="200" />
</LinearLayout>
<Button
android:id="@+id/crashLoopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:minHeight="0dp"
android:minWidth="0dp"
android:padding="0dp"
android:text="Loop"
android:textSize="12sp"
android:layout_marginStart="60dp"
android:layout_marginEnd="60dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hide/Show Mix Controls"
android:id="@+id/mixCtrlBtn"/>
</LinearLayout>

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="@drawable/_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="@drawable/_ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
<color name="black">#FF000000</color>
<color name="red">#BB0000</color>
</resources>

View file

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

View file

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