Skip to content

mikekavouras/human-record-player

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Human Record Player

Leer en español

This repo demonstrates the basic controls for https://humanrecordplayer.com.

RPReplay_Final1655745366.MP4

Development

Safari requires https to request device motion/orientation events.

  1. Get a server running in the root directory (e.g., python3 -m http.server --cgi 8000)
  2. Use ngrok to explose the local server (e.g., ngrok http 8000)
  3. Point your mobile browser at the ngrok-generated URL

Audio assets

For full mobile browser support you'll want to provide both .mp3 and .ogg. To get any audio from the internet...

  1. Use youtube-dl to download a YouTube video.
  2. Use ffmpeg to isolate the audio
  3. Use ffmpeg to convert audio to .mp3 and .ogg
  4. Spin
youtube-dl <URL> -o video
ffmpeg -i video.mkv -vn -acodec copy audio.aac
ffmpeg -i audio.aac audio.mp3
ffmpeg -i audio.aac audio.ogg

Caveats

  • Safari desktop does not support DeviceMotion events
  • Chrome desktop does support DeviceMotion events. Chrome desktop can be used to debug with a constant playback rate.

Credits

Shoutout to @feross for https://github.com/feross/unmute-ios-audio, and @searls for the iOS 14.5+ patch

Code Samples

In this section, we showcase some specific code snippets from the project to give you a taste of how it works.

Initialization of the Motion Class

Here's how the Motion class is initialized in js/script.js:

const motion = new Motion(window, {
  update(val) {
    const absVal = Math.abs(val)

    let rate = 1.0
    if (absVal > groove.min && absVal < groove.max) {
      rate = 1.0
    } else if (absVal < groove.min) {
      rate = map(absVal, 0, groove.min, 0, 0.99)
    } else {
      rate = map(absVal, groove.max, 250, 1.01, 2.0)
    }

    audioPlayer.updatePlaybackRate(rate, val < 0)

    // Update debug UI
    const rpm = degToRpm(absVal).toFixed(0)
    playbackRateOutput.textContent = rate.toFixed(2)
    rotationRateOutput.textContent = `${val.toFixed(0)} deg/s (${rpm} RPM)`
  }
})

Why do programmers prefer dark mode? Because light attracts bugs!

The onMotionUpdate Method

And here's the onMotionUpdate method from js/motion.js:

onMotionUpdate(e) {
  let now = new Date()

  if ((now - this.lastReadAt) > Motion.MotionReadInterval) {
    this.lastReadAt = now

    const { beta, gamma } = e.rotationRate
    const isHorizontal = this.deviceOrientation === Motion.DeviceOrientation.Horizontal
    let value = (isHorizontal ? gamma : beta) * -1

    this.addValue(value)

    const sum = this.values.reduce((a, b) => { return a + b }, 0)
    const avg = sum / this.values.length // smoothed

    if (this.onMotionUpdateHandler) {
      this.onMotionUpdateHandler(avg)
    }
  }
}

Why was the JavaScript developer sad? Because he didn't know how to un-null his feelings.