Skip to content

Commit

Permalink
fix: drum-machine do not rely on instrument order (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
michalsek authored Nov 6, 2024
1 parent c4a9fe9 commit 3280750
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 39 deletions.
Empty file.
4 changes: 2 additions & 2 deletions apps/common-app/src/examples/DrumMachine/DrumMachine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,15 @@ const DrumMachine: React.FC = () => {
{player.isPlaying && (
<NotesHighlight
progressSV={player.progressSV}
playingNotes={player.playingNotes}
playingInstruments={player.playingInstruments}
/>
)}
</Canvas>
<PlayButton
onPress={onPlayPress}
canvasRect={canvasRect}
isPlaying={player.isPlaying}
playingNotes={player.playingNotes}
playingInstruments={player.playingInstruments}
/>
</View>
</GestureDetector>
Expand Down
24 changes: 10 additions & 14 deletions apps/common-app/src/examples/DrumMachine/NotesHighlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@ import React, { memo } from 'react';
import { Circle, Paint } from '@shopify/react-native-skia';
import { SharedValue, useDerivedValue } from 'react-native-reanimated';

import { Instrument, PlayingInstruments } from './types';
import { cPoint, buttonRadius } from './constants';
import { getPointCX, getPointCY } from './utils';
import instruments from './instruments';
import { Instrument } from './types';

interface NotesHighlightProps {
progressSV: SharedValue<number>;
playingNotes: SharedValue<boolean[]>;
playingInstruments: SharedValue<PlayingInstruments>;
}

interface NoteHighlightProps {
index: number;
instrument: Instrument;
progressSV: SharedValue<number>;
playingNotes: SharedValue<boolean[]>;
playingInstruments: SharedValue<PlayingInstruments>;
}

const NoteHighlight: React.FC<NoteHighlightProps> = (props) => {
const { instrument, progressSV, playingNotes } = props;
const { instrument, progressSV, playingInstruments } = props;

const angle = useDerivedValue(
() => progressSV.value * Math.PI * 2 - Math.PI / 2
Expand All @@ -34,11 +33,13 @@ const NoteHighlight: React.FC<NoteHighlightProps> = (props) => {
);

const r = useDerivedValue(() =>
playingNotes.value[props.index] ? buttonRadius * 1.5 : buttonRadius
playingInstruments.value[instrument.name]
? buttonRadius * 1.5
: buttonRadius
);

const color = useDerivedValue(() =>
playingNotes.value[props.index]
playingInstruments.value[instrument.name]
? `${instrument.color}55`
: `${instrument.color}00`
);
Expand All @@ -53,13 +54,8 @@ const NoteHighlight: React.FC<NoteHighlightProps> = (props) => {

const NotesHighlight: React.FC<NotesHighlightProps> = (props) => (
<>
{instruments.map((instrument, index) => (
<NoteHighlight
index={index}
key={instrument.name}
instrument={instrument}
{...props}
/>
{instruments.map((instrument) => (
<NoteHighlight key={instrument.name} instrument={instrument} {...props} />
))}
</>
);
Expand Down
14 changes: 7 additions & 7 deletions apps/common-app/src/examples/DrumMachine/PlayButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,32 @@ import { Icon } from '@swmansion/icons';
import { Pressable, StyleSheet } from 'react-native';

import { colors } from '../../styles';
import type { XYWHRect } from './types';
import type { PlayingInstruments, XYWHRect } from './types';
import { size } from './constants';

interface PlayButtonProps {
isPlaying?: boolean;
onPress: () => void;

canvasRect: XYWHRect;
playingNotes: SharedValue<boolean[]>;
playingInstruments: SharedValue<PlayingInstruments>;
}

interface PlayButtonInnerProps {
pressed: boolean;
isPlaying?: boolean;
playingNotes: SharedValue<boolean[]>;
playingInstruments: SharedValue<PlayingInstruments>;
}

const timingOptions = {
duration: 25,
};

const PlayButtonInner: React.FC<PlayButtonInnerProps> = (props) => {
const { pressed, isPlaying, playingNotes } = props;
const { pressed, isPlaying, playingInstruments } = props;

const containerStyle = useAnimatedStyle(() => {
const shouldPlay = playingNotes.value[2];
const shouldPlay = playingInstruments.value.kick;

return {
transform: [
Expand All @@ -58,7 +58,7 @@ const PlayButtonInner: React.FC<PlayButtonInnerProps> = (props) => {
};

const PlayButton: React.FC<PlayButtonProps> = (props) => {
const { canvasRect, onPress, isPlaying, playingNotes } = props;
const { canvasRect, onPress, isPlaying, playingInstruments } = props;

return (
<Pressable
Expand All @@ -75,7 +75,7 @@ const PlayButton: React.FC<PlayButtonProps> = (props) => {
<PlayButtonInner
pressed={pressed}
isPlaying={isPlaying}
playingNotes={playingNotes}
playingInstruments={playingInstruments}
/>
)}
</Pressable>
Expand Down
4 changes: 3 additions & 1 deletion apps/common-app/src/examples/DrumMachine/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Dimensions } from 'react-native';
import type { XYPoint } from './types';
import type { InstrumentName, XYPoint } from './types';

export const screenSize = Dimensions.get('screen');
export const size = Math.min(screenSize.width, screenSize.height);
Expand All @@ -19,3 +19,5 @@ export const cPoint: XYPoint = {
x: size / 2,
y: size / 2,
};

export const instruments: InstrumentName[] = ['kick', 'clap', 'hi-hat'];
2 changes: 2 additions & 0 deletions apps/common-app/src/examples/DrumMachine/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ export interface XYPoint {
x: number;
y: number;
}

export type PlayingInstruments = Record<InstrumentName, boolean>;
36 changes: 24 additions & 12 deletions apps/common-app/src/examples/DrumMachine/usePlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { AudioContext } from 'react-native-audio-api';
import { useSharedValue } from 'react-native-reanimated';
import { useRef, useState, useCallback, useLayoutEffect } from 'react';

import type { InstrumentName, Pattern } from './types';
import { numBeats } from './constants';
import type { InstrumentName, Pattern, PlayingInstruments } from './types';
import { numBeats, instruments } from './constants';

type PlayNoteMethod = (name: InstrumentName, time: number) => void;

Expand All @@ -18,6 +18,16 @@ interface PlayerOptions {
setup: (AudioContext: AudioContext) => SetupResponse;
}

function getPlayingInstruments(
iName?: InstrumentName,
iValue: boolean = false
) {
return instruments.reduce((acc, name) => {
acc[name] = iName === name ? iValue : false;
return acc;
}, {} as PlayingInstruments);
}

function r<T>(ref: React.MutableRefObject<T>) {
return ref.current;
}
Expand All @@ -35,8 +45,8 @@ export default function usePlayer(options: PlayerOptions) {
const progressSV = useSharedValue(0);
const currentBeat = useSharedValue(0);

const playingNotes = useSharedValue<boolean[]>(
Array(patterns.length).fill(false)
const playingInstruments = useSharedValue<PlayingInstruments>(
getPlayingInstruments()
);

useLayoutEffect(() => {
Expand All @@ -59,16 +69,17 @@ export default function usePlayer(options: PlayerOptions) {
let currentTime = 0;

function advancePattern() {
const plays = Array(r(patternsRef).length).fill(false);

for (let i = 0; i < r(patternsRef).length; i += 1) {
if (r(patternsRef)[i].steps[currentBeat.value]) {
plays[i] = true;
playingInstruments.value = getPlayingInstruments(
r(patternsRef)[i].instrumentName,
true
);

playNote(r(patternsRef)[i].instrumentName, audioContext.currentTime);
}
}

playingNotes.value = plays;
currentBeat.value = (currentBeat.value + 1) % numBeats;
}

Expand All @@ -86,8 +97,9 @@ export default function usePlayer(options: PlayerOptions) {
progressSV.value = (timeDiff % totalLoopTime) / totalLoopTime;

if (currentTime - (nextNoteTime - timePerNote) > 0.05) {
playingNotes.value = Array(r(patternsRef).length).fill(false);
playingInstruments.value = getPlayingInstruments();
}

if (currentTime + averageFrameTime >= nextNoteTime) {
advancePattern();
nextNoteTime += timePerNote;
Expand All @@ -105,7 +117,7 @@ export default function usePlayer(options: PlayerOptions) {

progressSV.value = 0;
currentBeat.value = 0;
playingNotes.value = Array(patterns.length).fill(false);
playingInstruments.value = getPlayingInstruments();
startTime = audioContext.currentTime;
currentTime = audioContext.currentTime;
nextNoteTime = audioContext.currentTime;
Expand All @@ -118,7 +130,7 @@ export default function usePlayer(options: PlayerOptions) {

progressSV.value = 0;
currentBeat.value = 0;
playingNotes.value = Array(patterns.length).fill(false);
playingInstruments.value = getPlayingInstruments();
}

return () => {
Expand All @@ -137,7 +149,7 @@ export default function usePlayer(options: PlayerOptions) {
}, []);

return {
playingNotes,
playingInstruments,
progressSV,
isPlaying,
play,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Clap implements SoundEngine {
output[i] = Math.random() * 2 - 1;
}

buffer.copyToChannel(output, 0);
buffer.copyToChannel(output, 0, 0);

return buffer;
}
Expand Down
4 changes: 2 additions & 2 deletions apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2063,8 +2063,8 @@ SPEC CHECKSUMS:
RNReanimated: 77242c6d67416988a2fd9f5cf574bb3e60016362
RNScreens: e389d6a6a66a4f0d3662924ecae803073ccce8ec
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: f8ec45ce98bba1bc93dd28f2ee37215180e6d2b6
Yoga: 1d66db49f38fd9e576a1d7c3b081e46ab4c28b9e

PODFILE CHECKSUM: 75ad38075e71875257a2590065853ea6a608b897

COCOAPODS: 1.15.2
COCOAPODS: 1.16.2

1 comment on commit 3280750

@github-actions
Copy link

Choose a reason for hiding this comment

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

@check-spelling-bot Report

🔴 Please review

See the 📜action log or 📝 job summary for details.

Unrecognized words (574)
aabe
aae
aarch
abe
abecc
abefimoux
abffd
ABGJMOPRSUW
ACDD
ADACE
Additonal
aead
aec
aecba
afe
afffb
akx
allprojects
androiddebugkey
androidx
apk
appname
appveyor
ARCHS
arget
armeabi
arnings
arounds
asciinema
ASingle
askubuntu
assetcatalog
assword
ath
attributedstring
audioapi
Autolayout
autoreleasepool
AUTORELEASING
autoresizing
ayb
baccounts
badgen
baf
bamazon
bany
bapi
bapp
barchive
basciinema
bbadgen
bbcda
BBD
bbit
bbitbucket
bbootstrapcdn
bca
bcalendar
bcb
bcbb
bcdlpsw
bcdnjs
bchrome
bci
BCIPR
bcircleci
bclients
bcodepen
bcolab
bcompai
Bcu
bdatastudio
bdeveloper
bdisqus
bdocs
bdrive
bdropbox
bembed
bfa
bfb
bfbcdn
bfburl
bfcc
bfd
bfe
BFF
bffcf
bffdfd
bfigma
bfit
bfonts
bforms
bfreecodecamp
bgetopts
bgist
bgit
bgithub
bgithubassets
bgitlab
bgitter
bgnupg
bgoogle
bgoogleapis
bgravatar
bgreater
bgroups
bimage
bimg
bimgur
binance
bitbucket
BITCODE
bitrise
blogs
bmaps
bmastodon
bmedia
bmedium
bmermaid
bmicrosoft
bmount
bmusic
bmvnrepository
BNDL
bneither
bno
bnon
bnor
bootstrapcdn
bopen
Bosa
bpastebin
bpm
bqr
breddit
bregex
bregistry
breif
brequestb
bscastie
bscholar
bset
bshields
bslack
bsns
bstorage
bstruct
btinyurl
bto
btput
btwimg
btwitter
btype
buckd
Buildables
buildscript
bvisualstudio
bwork
bxn
caad
cac
cae
callinvoker
CBA
cbb
CBBA
cbbf
CBCC
CBlock
cca
ccache
ccb
ccc
ccce
cce
ccfcbd
cdc
cdcd
cddb
CDeclaration
cdn
CDPATH
cea
cefac
cefbd
cer
cfbfc
cfbundle
circleci
cloudflare
cocoapods
codacy
codepen
Colab
commitlint
compai
componentregistry
componentregistrynative
contributorsrc
cosw
coveragerc
CPLUSPLUSFLAGS
cpplint
crt
CSpace
cstddef
CStyle
cxxreact
Cyg
cygpath
dadb
DANDROID
datasources
dbbc
dbd
dbe
dbfef
DCACB
ddb
ddbc
ddf
ddfeb
debuggable
defaultsnativemodule
detials
dfc
DFOLLY
diffs
dimen
Disqus
diuoxn
domnativemodule
Dorg
dotnet
doxygen
DPL
DPWXY
drawio
DRCT
DREACT
dropbox
dsprelated
eaa
EABD
eacba
eba
ebaab
ebaf
ebfba
ecae
ecent
edb
edc
EEFC
efc
efcf
efefef
EHf
emmintrin
emplates
emptively
entrancy
eport
epreciation
equ
esolve
esult
evilmartians
faaaa
fabricexample
fadfb
faf
FBC
fbcef
FBD
fbe
FBF
fbjni
fca
fccb
fdb
fdc
fdce
FDDC
fde
featureflags
featureflagsnativemodule
febf
fef
fega
fexceptions
ffac
FFD
ffe
ffffff
FFFFFFF
figma
fprime
freebooks
freecodecamp
frtti
fstack
ftu
GETTIME
GHSA
giphy
githubassets
Gitlab
gitweb
GJd
gle
glog
googleusercontent
gpg
gradlew
graffle
gstatic
hashtag
hermesc
heroku
hmap
IAudio
IBiquad
icns
idlecallbacks
idlecallbacksnativemodule
IGain
IIR
imagemanager
imgur
ine
INFOPLIST
inputaccessory
instancetype
Instantitation
IOscillator
iostextinput
ipa
ipfs
iphoneos
iphonesimulator
ipynb
IRR
IService
IStereo
itle
IWYU
JAVACMD
javaobject
jetbrains
jhybriddata
jhybridobject
jks
JMessage
jni
JNICALL
JNIEXPORT
jpe
jre
jsbundle
jsc
jserrorhandler
jsi
jsiexecutor
jsinspector
jsitracing
jsoref
jvmargs
jzt
keystore
kotlin
kotlinx
ksh
ktlint
ktx
Lcom
LDFLAGS
leakchecker
lefthook
legacyviewmanagerinterop
LIBCPP
libfbjni
libfolly
libhermes
libjscexecutor
libjsi
libreactnativejni
libreanimated
LICEN
linelength
Llf
Llpsw
loadu
LOCALIZABILITY
LStv
mailgun
Mapbuffer
maxmgv
mcve
metacharacter
microtasksnativemodule
mozilla
MSGSEND
msys
MTL
mtrl
mvnrepository
nativeconfig
nativemodule
ndk
ndkversion
necho
nfi
nif
nistp
NONINFRINGEMENT
npmjs
nrt
NSURL
NYQUIST
objc
objcpp
ocf
OMo
Onone
openpgpkey
ormal
osub
otf
OZV
pastebin
pbxproj
pbxuser
Pdocker
perflogger
performancetimeline
perspectivev
pgp
phc
pinterest
pki
pne
podfile
podspec
posthog
postpack
powerbi
prama
proguard
pylintrc
QParam
qscriptbuffer
qtproject
qtscriptclassic
rdparty
reactjs
reactnative
reactnativejni
reactperflogger
redbox
regexplanet
regularbeat
rendererconsistency
rendererdebug
repostiory
reproducting
requestb
retvl
rncore
RNv
rootproject
rubygems
runtimeexecutor
Runtimes
runtimescheduler
rvm
RWb
safeareaview
Samsung
scastie
screenshots
scrollview
SDKROOT
serverfault
setzero
sfor
shellness
shopify
skia
SLO
soloader
sopt
sourcecode
spotify
SRCROOT
Srcs
Ssx
stackexchange
stackoverflow
statuspage
stereopanner
storeu
STREQUAL
subviews
svgz
swm
swmansion
swork
tada
taskdefs
templateprocessor
templatesz
terminfo
Testables
textlayoutmanager
tgz
thewritepractice
tinyurl
tmdb
tprteaching
troff
Ttx
turbomodule
Turborepo
uimanager
unimplementedview
unsplash
usercontent
userguide
utm
vabsq
vadd
vaddq
vdupq
vget
vld
vmax
vmaxq
vmul
vmulq
vsadd
vsmul
vsub
vsubq
webaudio
webstore
Werror
Wno
workarounds
worklets
WWRY
xcassets
xccheckout
xcconfig
xcf
xcfilelist
xcodeproj
xcodeproject
xcprivacy
XCT
xctest
xcuserdata
xcuserstate
xcworkspace
XDE
Xms
Xmx
xpm
XYWH
Ylf
youtu
youtube
youtubei
Zacdfh
Some files were automatically ignored 🙈

These sample patterns would exclude them:

[^/]\.png$
^\Q.github/actions/setup/spelling/allow.txt\E$
^\Q.github/actions/setup/spelling/expect.txt\E$
^\Q.yarn/releases/yarn-4.5.0.cjs\E$
^\Qapps/fabric-example/android/app/debug.keystore\E$
^\Qapps/fabric-example/android/gradle/wrapper/gradle-wrapper.jar\E$
^\Qyarn.lock\E$

You should consider excluding directory paths (e.g. (?:^|/)vendor/), filenames (e.g. (?:^|/)yarn\.lock$), or file extensions (e.g. \.gz$)

You should consider adding them to:

.github/actions/spelling/excludes.txt

File matching is via Perl regular expressions.

To check these files, more of their words need to be in the dictionary than not. You can use patterns.txt to exclude portions, add items to the dictionary (e.g. by adding them to allow.txt), or fix typos.

To accept these unrecognized words as correct and update file exclusions, you could run the following commands

... in a clone of the [email protected]:software-mansion-labs/react-native-audio-api.git repository
on the main branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/main/apply.pl' |
perl - 'https://github.com/software-mansion-labs/react-native-audio-api/actions/runs/11704153959/attempts/1'
Available 📚 dictionaries could cover words not in the 📘 dictionary
Dictionary Entries Covers Uniquely
cspell:cpp/src/compiler-clang-attributes.txt 46 2 1
cspell:cryptocurrencies/cryptocurrencies.txt 125 2 2
cspell:cpp/src/ecosystem.txt 51 1 1
cspell:ruby/dict/ruby.txt 157 1 1
cspell:css/dict/css.txt 263 1 1

Consider adding them (in .github/workflows/spelling.yml) in jobs:/spelling: for uses: check-spelling/check-spelling@main in its with:

      with:
        extra_dictionaries: |
          cspell:cpp/src/compiler-clang-attributes.txt
          cspell:cryptocurrencies/cryptocurrencies.txt
          cspell:cpp/src/ecosystem.txt
          cspell:ruby/dict/ruby.txt
          cspell:css/dict/css.txt

To stop checking additional dictionaries, add (in .github/workflows/spelling.yml) for uses: check-spelling/check-spelling@main in its with:

check_extra_dictionaries: ''
Errors (4)

See the 📜action log or 📝 job summary for details.

❌ Errors Count
⚠️ binary-file 15
⚠️ large-file 1
⚠️ noisy-file 1
❌ unsupported-repo-notation 2

See ❌ Event descriptions for more information.

Please sign in to comment.