Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#190] Fix usePlayer and related hooks to not rely on instrument order and their indexes #200

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 { Pressable, StyleSheet } from 'react-native';

import { colors } from '../../styles';
import type { XYWHRect } from './types';
import type { PlayingInstruments, XYWHRect } from './types';
Dismissed Show dismissed Hide dismissed
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 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 @@
<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 @@
RNReanimated: 77242c6d67416988a2fd9f5cf574bb3e60016362
RNScreens: e389d6a6a66a4f0d3662924ecae803073ccce8ec
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: f8ec45ce98bba1bc93dd28f2ee37215180e6d2b6
Yoga: 1d66db49f38fd9e576a1d7c3b081e46ab4c28b9e

PODFILE CHECKSUM: 75ad38075e71875257a2590065853ea6a608b897

COCOAPODS: 1.15.2
COCOAPODS: 1.16.2
Dismissed Show dismissed Hide dismissed
Loading