Skip to content

Commit

Permalink
feat: Establish latency compensated clip playback! YAY!
Browse files Browse the repository at this point in the history
  • Loading branch information
TimSusa committed Jan 9, 2021
1 parent ce157f9 commit f81788a
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 57 deletions.
56 changes: 44 additions & 12 deletions src/clip/Clip.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,45 @@ const useStyles = makeStyles(() => ({
}
}))
export function Clip({ url, tracksId, clipId }) {
console.log('component rendered ', clipId)
const classes = useStyles()
const { audioContext } = useContext(context)
const dispatch = useDispatch()
const { registerClip } = actionsViewSettings
const { changeClipSrc, changeClipVolume, toggleIsLooping } = actionsContent
const audioDriverOuts = useSelector(
(state) => state.viewSettings.audioDriverOuts
)
const { audioDriverOuts } = useSelector((state) => state.viewSettings)

const tracks = useSelector((state) => state.content.tracks)
const tracksIdx = tracks.findIndex((item) => item.id === tracksId)
const clipIdx = tracks[tracksIdx].data.findIndex((item) => item.id === clipId)
const track = tracks[tracksIdx]
const {
isPlaying,
isLooping,
isWaveformShown,
volume,
audioDriverOutName
} = tracks[tracksIdx].data[clipIdx]
} = track.data[clipIdx]
const waveformRef = useRef(null)
const wavesurfer = useRef(null)
const nrOfCycles = useRef(0)
const [playing, setPlay] = useState(isPlaying)

useEffect(() => {
const options = formWaveSurferOptions(waveformRef.current)
wavesurfer.current = WaveSurfer.create(options)
wavesurfer.current.load(url)
wavesurfer.current.on('finish', () => {
const duration = wavesurfer.current.getDuration()
nrOfCycles.current++

const tmp =
audioContext.currentTime +
audioContext.baseLatency -
nrOfCycles.current * duration
if (isLooping) {
wavesurfer.current.play()
wavesurfer.current.play(tmp >= 0 ? tmp : 0)
} else {
wavesurfer.current.stop(tmp >= 0 ? tmp : 0)
}
})
// Removes events, elements and disconnects Web Audio nodes.
Expand All @@ -75,12 +84,19 @@ export function Clip({ url, tracksId, clipId }) {
}, [audioDriverOutName])

useEffect(() => {
const duration = wavesurfer.current.getDuration()
if (isPlaying) {
dispatch(toggleIsLooping({ tracksId, clipId, isLooping: false }))
wavesurfer.current.play(audioContext.currentTime)
dispatch(toggleIsLooping({ tracksId, clipId, isLooping: true }))
wavesurfer.current.play(
audioContext.currentTime +
audioContext.baseLatency -
nrOfCycles.current * duration
)
} else {
wavesurfer.current.stop(audioContext.currentTime)
wavesurfer.current.stop(
audioContext.currentTime +
audioContext.baseLatency -
nrOfCycles.current * duration
)
}
}, [isPlaying])

Expand Down Expand Up @@ -226,9 +242,25 @@ export function Clip({ url, tracksId, clipId }) {
function handlePlayPause() {
setPlay(!playing)
if (isPlaying) {
dispatch(registerClip({ clip: { tracksId, clipId, isPlaying: false } }))
dispatch(
registerClip({
clip: {
tracksId,
clipId,
isPlaying: false
}
})
)
} else {
dispatch(registerClip({ clip: { tracksId, clipId, isPlaying: true } }))
dispatch(
registerClip({
clip: {
tracksId,
clipId,
isPlaying: true
}
})
)
}
}
function formWaveSurferOptions(ref) {
Expand Down
3 changes: 1 addition & 2 deletions src/global-state/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import { content } from './reducers/content'
const { reducer: reducerContent, actions: actionsContents } = createSlice({
name: 'content',
initialState: {
tracks: [],
sceneIdx: null
tracks: []
},
reducers: content
})
Expand Down
19 changes: 0 additions & 19 deletions src/global-state/reducers/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,30 +109,11 @@ export const content = {
)
state.tracks[tracksIdx].data[clipIdx].isWaveformShown = isWaveformShown
},
// deprecated
stopIsScenePlaying(state, { payload: { sceneIdx } }) {
state.tracks.forEach((track, idx) => {
if (track.data[sceneIdx]) {
state.tracks[idx].data[sceneIdx].isPlaying = false
}
})
},
stopAll(state) {
state.tracks.forEach((track, idx) => {
track.data.forEach((clip, clipIdx) => {
state.tracks[idx].data[clipIdx].isPlaying = false
})
})
},
// deprecated
toggleIsScenePlaying(state, { payload: { sceneIdx } }) {
state.tracks.forEach((track, idx) => {
state.tracks[idx].data.forEach((clip, idxx) => {
state.tracks[idx].data[idxx].isPlaying = false
})
if (track.data[sceneIdx]) {
state.tracks[idx].data[sceneIdx].isPlaying = true
}
})
}
}
6 changes: 1 addition & 5 deletions src/global-state/reducers/view-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ export const playbackStates = {
}
export const viewSettingsInitState = {
playbackState: null,
currentTimeStamp: 0.0,
bpm: 120,
windowFrameInSteps: 1,
currentSceneIdx: null,
registeredClips: [],
audioDriverOuts: [],

Expand All @@ -28,15 +26,13 @@ export const viewSettings = {
state.playbackState = playbackState
state.currentTimeStamp = currentTimeStamp
},

setBpm(state, { payload: { bpm } }) {
state.bpm = bpm
},
setWindowFrameInSteps(state, { payload: { windowFrameInSteps } }) {
state.windowFrameInSteps = windowFrameInSteps
},
changeCurrentScene(state, { payload: { currentSceneIdx } }) {
state.currentSceneIdx = currentSceneIdx
},
registerClip(state, { payload: { clip } }) {
if (state.registeredClips.every((item) => item.clipId !== clip.clipId)) {
state.registeredClips = [...state.registeredClips, clip]
Expand Down
5 changes: 0 additions & 5 deletions src/global-state/thunks/clock.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,7 @@ export function clock() {
const {
viewSettings: { registeredClips }
} = getState()
//console.log('tick: ', evt.timeStamp)

// if (currentSceneIdx !== null) {
// dispatch(toggleIsScenePlaying({ sceneIdx: currentSceneIdx }))
// dispatch(changeCurrentScene({ currentSceneIdx: null }))
// }
if (registeredClips.length > 0) {
dispatch(toggleIsPlayingList({ clips: registeredClips }))
dispatch(clearRegisteredClips())
Expand Down
17 changes: 9 additions & 8 deletions src/matrix/Matrix.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { actionsContent, actionsViewSettings } from '../global-state'

export function Matrix() {
const { setContent, addTrack, addClipToTrack } = actionsContent
const { registerClips, clearRegisteredClips } = actionsViewSettings
const { registerClips } = actionsViewSettings
const theme = useTheme()
const dispatch = useDispatch()
const classes = makeStyles(styles.bind(this, theme))()
Expand Down Expand Up @@ -45,8 +45,14 @@ export function Matrix() {
<IconButton
aria-label='stop-scene'
onClick={() => {
dispatch(clearRegisteredClips())
// dispatch(stopIsScenePlaying({ sceneIdx: clipIdx }))
const clips = tracks.map((track) => {
return {
tracksId: track.id,
clipId: track.data[clipIdx].id,
isPlaying: false
}
})
dispatch(registerClips({ clips }))
}}
>
<StopSceneIcon></StopSceneIcon>
Expand All @@ -55,19 +61,14 @@ export function Matrix() {
<IconButton
aria-label='play-scene'
onClick={() => {
console.log('play scene: ', clipIdx)
const clips = tracks.map((track) => {
return {
tracksId: track.id,
clipId: track.data[clipIdx].id,
isPlaying: true
}
})
console.log('register clips from ', clips)
dispatch(registerClips({ clips }))
// TODO: convert to register clips
// dispatch(changeCurrentScene({ currentSceneIdx: clipIdx }))
//registerClips
}}
>
<PlaySceneIcon></PlaySceneIcon>
Expand Down
4 changes: 1 addition & 3 deletions src/utils/example_120bpm.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,11 @@ export const content = {
}
]
}
],
sceneIdx: null
]
},
viewSettings: {
bpm: 95,
windowFrameInSteps: 4,
currentSceneIdx: null,
columns: 18,
availableDrivers: {
inputs: {
Expand Down
4 changes: 1 addition & 3 deletions src/utils/example_95bpm.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,11 @@ export const content = {
}
]
}
],
sceneIdx: null
]
},
viewSettings: {
bpm: 95,
windowFrameInSteps: 4,
currentSceneIdx: null,
columns: 18,
availableDrivers: {
inputs: {
Expand Down

1 comment on commit f81788a

@vercel
Copy link

@vercel vercel bot commented on f81788a Jan 9, 2021

Choose a reason for hiding this comment

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

Please sign in to comment.