summaryrefslogtreecommitdiff
path: root/app/client/audio/wav2pix.js
blob: 48ca140034dce744a5cec8e489b16b46c3640738 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import types from '../types'

import Tone from 'tone'
import JSZip from 'jszip'
import FileSaver from 'file-saver'
import { sprintf } from 'sprintf-js'

import * as draw from './lib/draw'
import output from './lib/output'
import spectrum from './lib/spectrum'

import { requestAudioContext } from './lib'

export const FRAME_LENGTH = 126 * 255
export const FRAME_STEP = Math.round(FRAME_LENGTH / 4)

const _r = 8
const _i = 8

// requestAudioContext(() => {})

export const loadBuffer = file => {
  return new Promise((resolve, reject) => {
    const url = URL.createObjectURL(file)
    let buffer = new Tone.Buffer(
      url,
      loadBuffer,
      err => {
        console.error('err', err)
        reject(err)
      }
    )
    function loadBuffer() {
      URL.revokeObjectURL(url)
      resolve(buffer)
    }
  })
}

export const loadPCM = (file) => {
  return new Promise((resolve, reject) => {
    loadBuffer(file).then(buffer => {
      const pcm = buffer._buffer.getChannelData(0)
      const sr = buffer._buffer.sampleRate
      if (! pcm) return reject()
      console.log(buffer, pcm, sr)
      resolve({ buffer, pcm, sr })
    })
  })
}

export const renderFrames = (file, { frame_step=FRAME_STEP, max=12 }) => dispatch => {
  return new Promise((resolve, reject) => {
    loadPCM(file).then(({ buffer, pcm, sr }) => {
      dispatch({ type: types.wav2pix.load })
      let canvases = []
      let offset = 0, count = 0, _len = pcm.length - FRAME_LENGTH
      for (;
            offset < _len && count < max;
            offset += frame_step, count += 1
          ) {
        frames.push(render(pcm.slice(offset, offset+FRAME_LENGTH), sr, count))
      }
      dispatch({ type: types.wav2pix.finish, message: 'Rendered ' + count + ' images' })
      resolve(frames)
    })
  })
}

export const buildZip = (name, file, { frame_step=FRAME_STEP, max=10000 }) => dispatch => {
  return new Promise((resolve, reject) => {
    loadPCM(file).then(({ buffer, pcm, sr }) => {
      dispatch({ type: types.wav2pix.load })

      const zip = new JSZip()
      const zip_folder = zip.folder("wav2pix_" + name);

      let steps = (pcm.length - FRAME_LENGTH) / frame_step
      console.log(steps)

      let offset = 0, count = 0, _len = pcm.length - FRAME_LENGTH
      for (;
            offset < _len && count < max;
            offset += frame_step, count += 1
          ) {
        if ((count % 10) === 0) {
          dispatch({ type: types.wav2pix.progress, progress: { i: count / steps * 6, n: 6 } })
        }
        render(pcm.slice(offset, offset+FRAME_LENGTH), sr, count, zip_folder)
      }

      // dispatch event
      dispatch({ type: types.wav2pix.finish, message: 'Rendered ' + count + ' images' })
      zip.generateAsync({ type: "blob" }).then(content => {
        dispatch({ type: types.wav2pix.zip })
        // FileSaver.saveAs(content, "wav2pix_" + name + ".zip")
        resolve({
          zip: content,
          filename: "wav2pix_" + name + ".zip",
          count
        })
      })
    })
  })
}

function render(pcm, sr, count, zip){
  const fft = spectrum.toSpectrum(pcm, sr)
  // console.log('render', fft)
  // const pcm_rev = pcm.slice().reverse()
  // const spec_rev = spectrum.toSpectrum(pcm_rev, spec.sr)
  const { canvas, imageData } = draw.raw_spectrum(fft, 0, 256, 0, 256, _r, _i)
  if (zip) {
    const name = sprintf('frame_%05d.png', count)
    const dataURL = canvas.toDataURL("image/png")
    zip.file(name, dataURL.split(',')[1], {base64: true})
  }
  return { fft, canvas, imageData }
}