summaryrefslogtreecommitdiff
path: root/app/client
diff options
context:
space:
mode:
Diffstat (limited to 'app/client')
-rw-r--r--app/client/api/crud.types.js8
-rw-r--r--app/client/audio/lib/draw.js9
-rw-r--r--app/client/audio/lib/index.js2
-rw-r--r--app/client/audio/lib/spectrum.js2
-rw-r--r--app/client/audio/pix2wav.js14
-rw-r--r--app/client/audio/wav2pix.js145
-rw-r--r--app/client/common/index.js22
-rw-r--r--app/client/common/progress.component.js1
-rw-r--r--app/client/dataset/dataset.new.js2
-rw-r--r--app/client/modules/pix2pix/views/pix2pix.show.js2
-rw-r--r--app/client/modules/pix2wav/pix2wav.reducer.js22
-rw-r--r--app/client/modules/pix2wav/views/pix2wav.show.js12
-rw-r--r--app/client/modules/pix2wav/views/spectrogram.upload.js132
-rw-r--r--app/client/modules/samplernn/views/samplernn.show.js2
-rw-r--r--app/client/types.js6
15 files changed, 301 insertions, 80 deletions
diff --git a/app/client/api/crud.types.js b/app/client/api/crud.types.js
index 630bf83..83be34d 100644
--- a/app/client/api/crud.types.js
+++ b/app/client/api/crud.types.js
@@ -1,8 +1,11 @@
export const as_type = (a, b) => [a, b].join('_').toUpperCase()
+export const with_type = (type, actions) =>
+ actions.reduce((a, b) => (a[b] = as_type(type, b)) && a, {})
+
export const crud_type = (type, actions=[]) =>
- actions.concat([
+ with_type(type, actions.concat([
'index_loading',
'index',
'index_error',
@@ -24,5 +27,4 @@ export const crud_type = (type, actions=[]) =>
'upload_complete',
'upload_error',
'sort',
- ])
- .reduce((a, b) => (a[b] = as_type(type, b)) && a, {})
+ ]))
diff --git a/app/client/audio/lib/draw.js b/app/client/audio/lib/draw.js
index e523b6a..d4bb817 100644
--- a/app/client/audio/lib/draw.js
+++ b/app/client/audio/lib/draw.js
@@ -1,6 +1,11 @@
+import util from '../../util'
+
const scratch = document.createElement('canvas')
const scratchCtx = scratch.getContext('2d-lodpi')
+const w = 256
+const h = 256
+
export function spectrum(spec, x0, y0, ww, hh){
const data = spec.data
const fft_size = spec.fft_size
@@ -103,8 +108,8 @@ export function raw_spectrum(spec, x0, y0, ww, hh, def_min_r, def_min_i){
imag = col[v+1]
mean_r += real
mean_i += imag
- _r = clamp((real - min_r) / delta_r * 255, 0, 255)
- _i = clamp((imag - min_i) / delta_i * 255, 0, 255)
+ _r = util.clamp((real - min_r) / delta_r * 255, 0, 255)
+ _i = util.clamp((imag - min_i) / delta_i * 255, 0, 255)
// hsl = color.hsl2rgb((_i + 1) / 2, 1.0, 1 - Math.abs(_r / 10))
pixels[u+0] = _r
diff --git a/app/client/audio/lib/index.js b/app/client/audio/lib/index.js
index ba96112..2d89ff6 100644
--- a/app/client/audio/lib/index.js
+++ b/app/client/audio/lib/index.js
@@ -1,7 +1,7 @@
import Tone from 'tone'
import StartAudioContext from './startAudioContext'
-import { is_mobile } from '../util'
+import { is_mobile } from '../../util'
export function requestAudioContext (fn) {
if (is_mobile) {
diff --git a/app/client/audio/lib/spectrum.js b/app/client/audio/lib/spectrum.js
index f4a5444..c7252f1 100644
--- a/app/client/audio/lib/spectrum.js
+++ b/app/client/audio/lib/spectrum.js
@@ -1,6 +1,6 @@
import Tone from 'tone'
-import { shuffle, quantize, mod } from '../util'
+import { shuffle, quantize, mod } from '../../util'
import { windows as signalWindows } from 'signal-windows'
import FFTJS from 'fft.js'
diff --git a/app/client/audio/pix2wav.js b/app/client/audio/pix2wav.js
index ccd36be..1b103b4 100644
--- a/app/client/audio/pix2wav.js
+++ b/app/client/audio/pix2wav.js
@@ -1,17 +1,3 @@
-function render(pcm, 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)
- draw.clear()
- const { canvas, imageData } = draw.raw_spectrum(fft, 0, 256, 0, 256, _r, _i)
- const dataURL = canvas.toDataURL("image/png")
- if (zip) {
- const fn = sprintf('frame_%05d.png', count)
- zip.file(fn, dataURL.split(',')[1], {base64: true})
- }
- return { fft, canvas, imageData }
-}
function play(i) {
// console.log('play', i)
last_i = i
diff --git a/app/client/audio/wav2pix.js b/app/client/audio/wav2pix.js
index e0a10fd..e9233e5 100644
--- a/app/client/audio/wav2pix.js
+++ b/app/client/audio/wav2pix.js
@@ -1,3 +1,5 @@
+import types from '../types'
+
import Tone from 'tone'
import JSZip from 'jszip'
import FileSaver from 'file-saver'
@@ -6,73 +8,104 @@ import * as draw from './lib/draw'
import output from './lib/output'
import spectrum from './lib/spectrum'
-import {
- lerp, clamp, mod,
-} from '../util'
-
import { requestAudioContext } from './lib'
-let buffer
-let sr = 44100
-let last_i = 0
-let _r = 8, _i = 8
-
-let files, file_index = 0;
-
const FRAME_LENGTH = 126 * 255
const FRAME_OFFSET = FRAME_LENGTH / 4
-function init() {
- requestAudioContext(ready)
- draw.resize(256, 256)
-}
-function handleFileSelect(e) {
- files = e.dataTransfer ? e.dataTransfer.files : e.target.files
- loadNext()
+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)
+ }
+ })
}
-function loadNext() {
- var file = files[file_index++]
- if (! file) return
- load(file)
+
+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 })
+ })
+ })
}
-function load(file) {
- buffer = new Tone.Buffer(
- URL.createObjectURL(file),
- loadBuffer,
- (err) => console.error('err', err)
- )
+
+export const renderFrames = (file, { frame_offset=FRAME_OFFSET, max=10 }) => dispatch => {
+ return new Promise((resolve, reject) => {
+ loadPCM(file).then(({ buffer, pcm, sr }) => {
+ dispatch({ type: types.wav2pix.loaded_buffer })
+ let canvases = []
+ for (let offset = 0, count = 0, _len = pcm.length - FRAME_LENGTH;
+ offset < _len && count < max;
+ offset += frame_offset, count += 1
+ ) {
+ canvases.push(render(pcm.slice(offset, offset+FRAME_LENGTH), sr, count))
+ }
+ dispatch({ type: types.wav2pix.draw_finish })
+ resolve(canvases)
+ })
+ })
}
-function loadBuffer() {
- // dispatch
- console.log('loaded buffer', buffer)
- const pcm = buffer._buffer.getChannelData(0)
- const sr = buffer._buffer.sampleRate
- if (! pcm) return
- const zip = new JSZip()
- const zip_folder = zip.folder("wav2pix_" + name);
+export const buildZip = (name, file, opt) => dispatch => {
+ return new Promise((resolve, reject) => {
+ const frame_offset = opt.frame_offset || FRAME_OFFSET
+ loadPCM(file).then(({ buffer, pcm, sr }) => {
+ dispatch({ type: types.wav2pix.loaded_buffer })
- const offset = 0
- for (offset = 0, count = 0, _len = pcm.length - FRAME_LENGTH;
- offset < _len;
- offset += FRAME_OFFSET, count += 1
- ) {
- if ((count % 100) === 0) {
- // dispatch event instead..
- console.log(count)
- }
- render(pcm.slice(offset, offset+FRAME_LENGTH), count, zip_folder)
- }
+ const zip = new JSZip()
+ const zip_folder = zip.folder("wav2pix_" + name);
- // dispatch event
- console.log('done exporting')
- zip.generateAsync({ type: "blob" }).then(content => {
- // dispatch
- console.log('saving zip')
- // FileSaver.saveAs(content, "wav2pix_" + name + ".zip")
- setTimeout(loadNext, 1000)
+ for (let offset = 0, count = 0, _len = pcm.length - FRAME_LENGTH;
+ offset < _len;
+ offset += frame_offset, count += 1
+ ) {
+ if ((count % 100) === 0) {
+ dispatch({ type: types.wav2pix.draw_progress, count })
+ }
+ render(pcm.slice(offset, offset+FRAME_LENGTH), sr, count, zip_folder)
+ }
+
+ // dispatch event
+ dispatch({ type: types.wav2pix.draw_finish })
+ zip.generateAsync({ type: "blob" }).then(content => {
+ dispatch({ type: types.wav2pix.load_zip })
+ // FileSaver.saveAs(content, "wav2pix_" + name + ".zip")
+ resolve(content)
+ })
+ })
})
- // play(0)
}
-init()
+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 }
+}
+
diff --git a/app/client/common/index.js b/app/client/common/index.js
new file mode 100644
index 0000000..de3e5b4
--- /dev/null
+++ b/app/client/common/index.js
@@ -0,0 +1,22 @@
+import Button from './button.component'
+import { FileList, FileRow } from './fileList.component'
+import FileUpload from './fileUpload.component'
+import Gallery from './gallery.component'
+import Group from './group.component'
+import Header from './header.component'
+import Loading from './loading.component'
+import Param from './param.component'
+import ParamGroup from './paramGroup.component'
+import Player from './player.component'
+import Progress from './progress.component'
+import Select from './select.component'
+import Slider from './slider.component'
+import TextInput from './textInput.component'
+
+export {
+ Loading, Progress, Header,
+ FileList, FileRow, FileUpload,
+ Gallery, Player,
+ Group, ParamGroup, Param,
+ TextInput, Slider, Select, Button,
+} \ No newline at end of file
diff --git a/app/client/common/progress.component.js b/app/client/common/progress.component.js
index 364f232..5882239 100644
--- a/app/client/common/progress.component.js
+++ b/app/client/common/progress.component.js
@@ -1,6 +1,7 @@
import { h, Component } from 'preact'
export default function Progress({progress}) {
+ if (! progress) return
const { i, n } = progress
const a = []
for (let j = 0; j < n; j++) {
diff --git a/app/client/dataset/dataset.new.js b/app/client/dataset/dataset.new.js
index bc169c7..a74e0bf 100644
--- a/app/client/dataset/dataset.new.js
+++ b/app/client/dataset/dataset.new.js
@@ -32,7 +32,7 @@ function NewDatasetForm (props) {
)
}
-const mapStateToProps = state => state
+const mapStateToProps = state => ({})
const mapDispatchToProps = (dispatch, ownProps) => ({
actions: bindActionCreators(datasetActions, dispatch),
diff --git a/app/client/modules/pix2pix/views/pix2pix.show.js b/app/client/modules/pix2pix/views/pix2pix.show.js
index 2139c6c..9c55b0b 100644
--- a/app/client/modules/pix2pix/views/pix2pix.show.js
+++ b/app/client/modules/pix2pix/views/pix2pix.show.js
@@ -31,6 +31,8 @@ class Pix2pixShow extends Component {
if (! pix2pix.folder || pix2pix.folder.id !== id) {
actions.load_directories(id)
}
+ } else {
+ this.props.history.push('/pix2pix/new/')
}
}
render(){
diff --git a/app/client/modules/pix2wav/pix2wav.reducer.js b/app/client/modules/pix2wav/pix2wav.reducer.js
index 0b55b07..8db1046 100644
--- a/app/client/modules/pix2wav/pix2wav.reducer.js
+++ b/app/client/modules/pix2wav/pix2wav.reducer.js
@@ -4,6 +4,7 @@ import datasetReducer from '../../dataset/dataset.reducer'
const pix2wavInitialState = {
loading: true,
progress: { i: 0, n: 0 },
+ status: '',
error: null,
folder_id: 0,
data: null,
@@ -15,6 +16,27 @@ const pix2wavReducer = (state = pix2wavInitialState, action) => {
}
switch (action.type) {
+ case types.wav2pix.loaded_buffer:
+ return {
+ ...state,
+ status: 'Loaded buffer',
+ }
+ case types.wav2pix.draw_progress:
+ console.log(action)
+ return {
+ ...state,
+ status: 'Rendering frame /',
+ }
+ case types.wav2pix.draw_finish:
+ return {
+ ...state,
+ status: 'Render complete',
+ }
+ case types.wav2pix.load_zip:
+ return {
+ ...state,
+ status: 'Built zip file',
+ }
default:
return state
}
diff --git a/app/client/modules/pix2wav/views/pix2wav.show.js b/app/client/modules/pix2wav/views/pix2wav.show.js
index acb99b1..19f303d 100644
--- a/app/client/modules/pix2wav/views/pix2wav.show.js
+++ b/app/client/modules/pix2wav/views/pix2wav.show.js
@@ -12,6 +12,8 @@ import NewDatasetForm from '../../../dataset/dataset.new'
import UploadStatus from '../../../dataset/upload.status'
import { FileList, FileRow } from '../../../common/fileList.component'
+import SpectrogramUpload from './spectrogram.upload'
+
import DatasetComponent from '../../../dataset/dataset.component'
import pix2wavModule from '../pix2wav.module'
@@ -31,6 +33,8 @@ class Pix2wavShow extends Component {
if (! pix2wav.folder || pix2wav.folder.id !== id) {
actions.load_directories(id)
}
+ } else {
+ this.props.history.push('/pix2wav/new/')
}
}
render(){
@@ -46,6 +50,14 @@ class Pix2wavShow extends Component {
<UploadStatus />
</div>
</div>
+ <SpectrogramUpload
+ loading={pix2wav.loading}
+ progress={pix2wav.progress}
+ id={pix2wav.folder_id}
+ module={pix2wavModule}
+ data={pix2wav.data}
+ folder={folder}
+ />
<DatasetComponent
loading={pix2wav.loading}
progress={pix2wav.progress}
diff --git a/app/client/modules/pix2wav/views/spectrogram.upload.js b/app/client/modules/pix2wav/views/spectrogram.upload.js
new file mode 100644
index 0000000..a0a2708
--- /dev/null
+++ b/app/client/modules/pix2wav/views/spectrogram.upload.js
@@ -0,0 +1,132 @@
+import { h, Component } from 'preact'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+import moment from 'moment'
+import util from '../../../util'
+
+import * as pix2wavActions from '../pix2wav.actions'
+import * as pix2wavTasks from '../pix2wav.tasks'
+
+import {
+ Loading, Progress,
+ Group, Param, FileUpload, TextInput, Button
+} from '../../../common'
+
+import * as wav2pixActions from '../../../audio/wav2pix'
+
+import pix2wavModule from '../pix2wav.module'
+
+class SpectrogramUpload extends Component {
+ constructor(props){
+ super(props)
+ this.state = {
+ file: null,
+ name: "",
+ frames: [],
+ }
+ const audioElement = document.createElement('audio')
+ audioElement.addEventListener('loadedmetadata', () => {
+ this.setState({ duration: audioElement.duration })
+ })
+ this.audioElement = audioElement
+ }
+ pickFile(file){
+ let name = file.name.split('.')[0]
+ .replace(/\s+/g, '_')
+ .replace(/-/g, '_')
+ .replace(/_+/g, '_')
+ this.setState({ file, name })
+ this.audioElement.src = URL.createObjectURL(file)
+ console.log(file.size)
+ if (file.size < 2 << 20) {
+ console.log('booooooooo')
+ this.props.wav2pix.renderFrames(file, {})
+ .then(frames => {
+ console.log(frames)
+ this.setState({
+ ...this.state, frames
+ })
+ })
+ }
+ console.log(file)
+ // get info on the file... size, etc
+ }
+ buildZip(){
+ const { file } = this.state
+
+ }
+ render(){
+ // loading={pix2wav.loading}
+ // progress={pix2wav.progress}
+ // id={pix2wav.folder_id}
+ // module={pix2wavModule}
+ // data={pix2wav.data}
+ // folder={folder}
+ const { file, frames } = this.state
+ return (
+ <div className='row'>
+ <div className='col spectrogramBuilder'>
+ <Group title='Spectrogram Builder'>
+ <p>
+ {"Convert your sounds into spectrograms. "}
+ {"Sound files can be WAV, MP3, AIFF, or FLAC. "}
+ <b>2 minutes max.</b>
+ </p>
+ <FileUpload
+ title='Choose a sound file'
+ mime='image.*'
+ onUpload={file => this.pickFile(file)}
+ />
+ <TextInput
+ title='Dataset name'
+ onChange={e => this.setState({ name: e.target.value })}
+ value={this.state.name}
+ />
+ <Button
+ onClick={() => this.buildZip()}
+ >Build Zip</Button>
+ <Progress />
+ {file && this.renderMetadata(file)}
+ </Group>
+ </div>
+ <div ref={(c) => { this.canvases = c }} id='pix2wav_canvases' />
+ </div>
+ )
+ }
+ renderMetadata(file){
+ const { duration } = this.state
+ const size = util.hush_size(file.size)
+ return (
+ <div className='fileMetadata'>
+ {file.size > 2 << 20 &&
+ <p>
+ <i>Careful, your file is larger than 2 MB.</i>
+ </p>}
+ <Param title='Name'>{file.name}</Param>
+ <Param title='Type'>{file.type}</Param>
+ <Param title='Size'><span className={size[0]}>{size[1]}</span></Param>
+ <Param title='Date'>{moment(file.lastModifiedDate).format("YYYY-MM-DD h:mm a")}</Param>
+ <Param title='Duration'>{Math.floor(duration) + ' s.'}</Param>
+ <br />
+ <Param title='Status'>{this.props.pix2wav.status}</Param>
+ </div>
+ )
+ }
+ componentDidUpdate(){
+ const canvases = (this.state.frames || []).map(c => {
+ this.canvases.append(c.canvas)
+ })
+ }
+}
+
+const mapStateToProps = state => ({
+ pix2wav: state.module.pix2wav,
+})
+
+const mapDispatchToProps = (dispatch, ownProps) => ({
+ actions: bindActionCreators(pix2wavActions, dispatch),
+ remote: bindActionCreators(pix2wavTasks, dispatch),
+ wav2pix: bindActionCreators(wav2pixActions, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(SpectrogramUpload)
diff --git a/app/client/modules/samplernn/views/samplernn.show.js b/app/client/modules/samplernn/views/samplernn.show.js
index b7e0740..a905b7c 100644
--- a/app/client/modules/samplernn/views/samplernn.show.js
+++ b/app/client/modules/samplernn/views/samplernn.show.js
@@ -33,6 +33,8 @@ class SampleRNNShow extends Component {
console.log('looooooooooad', id)
actions.load_directories(id)
}
+ } else {
+ this.props.history.push('/samplernn/new/')
}
}
render(){
diff --git a/app/client/types.js b/app/client/types.js
index 7193c9a..3ef6366 100644
--- a/app/client/types.js
+++ b/app/client/types.js
@@ -1,4 +1,4 @@
-import { crud_type } from './api/crud.types'
+import { crud_type, with_type } from './api/crud.types'
export default {
system: {
@@ -96,7 +96,6 @@ export default {
// update checkpoint settings
// reset checkpoint settings
// queue new checkpoint
- //
},
pix2pix: {
init: 'PIX2PIX_INIT',
@@ -106,4 +105,7 @@ export default {
init: 'PIX2WAV_INIT',
set_folder: 'PIX2WAV_SET_FOLDER',
},
+ wav2pix: with_type('wav2pix', [
+ 'loaded_buffer', 'draw_progress', 'draw_finish', 'load_zip', 'rendered_frames',
+ ])
}