mirror of
https://github.com/simon987/toolbox.git
synced 2025-04-10 05:56:44 +00:00
spectrograph
This commit is contained in:
parent
0036974798
commit
f8883e9d97
81
api/app.py
81
api/app.py
@ -10,6 +10,7 @@ import uvicorn
|
|||||||
import redis
|
import redis
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
|
|
||||||
|
from fastapi.params import Form
|
||||||
from starlette.responses import Response
|
from starlette.responses import Response
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@ -21,15 +22,79 @@ def read_root():
|
|||||||
return {"Hello": "World"}
|
return {"Hello": "World"}
|
||||||
|
|
||||||
|
|
||||||
HOUR = 3600
|
TTL = 60 * 60
|
||||||
DAY = HOUR * 24
|
|
||||||
FLAMEGRAPH_TTL = DAY * 7
|
|
||||||
|
|
||||||
@app.get("/flame_graph/{key}")
|
|
||||||
def flame_graph_get(key: str):
|
|
||||||
|
|
||||||
data = rdb.get("toolbox:FlameGraph:" + key)
|
@app.get("/data/{key}")
|
||||||
return Response(content=data, media_type="image/svg+xml")
|
def flame_graph_get(key: str, type: str):
|
||||||
|
data = rdb.get("toolbox:data:" + key)
|
||||||
|
return Response(content=data, media_type=type)
|
||||||
|
|
||||||
|
|
||||||
|
class SpectrographRequest:
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
z: int
|
||||||
|
label: str
|
||||||
|
window: str
|
||||||
|
|
||||||
|
def __init__(self, x, y, z, label, window, type):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.z = z
|
||||||
|
self.label = label
|
||||||
|
self.window = window
|
||||||
|
self.type = type
|
||||||
|
|
||||||
|
def valid(self):
|
||||||
|
if self.x < 100 or self.x > 200000:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.y < 100 or self.y > 10000:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.z < 20 or self.z > 180:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.window not in ("Hann", "Hamming", "Bartlett", "Rectangular", "Kaiser", "Dolph"):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/spectrograph")
|
||||||
|
def spectrograph(file: bytes = File(...), x: int = Form(...), y: int = Form(...), z: int = Form(...), label=Form(...),
|
||||||
|
window=Form(...), type=Form(...)):
|
||||||
|
key = str(uuid.uuid4())
|
||||||
|
|
||||||
|
req = SpectrographRequest(x, y, z, label, window, type)
|
||||||
|
if not req.valid():
|
||||||
|
return {
|
||||||
|
"err": "Invalid request"
|
||||||
|
}
|
||||||
|
|
||||||
|
p = Popen(
|
||||||
|
["sox",
|
||||||
|
"-t", type,
|
||||||
|
"-",
|
||||||
|
"-n", "remix", "1", "spectrogram",
|
||||||
|
"-t", label,
|
||||||
|
"-x", str(x), "-y", str(y), "-z", str(z),
|
||||||
|
"-w", window,
|
||||||
|
"-o", "-"],
|
||||||
|
cwd="./FlameGraph",
|
||||||
|
stdin=PIPE, stdout=PIPE, stderr=PIPE,
|
||||||
|
)
|
||||||
|
|
||||||
|
p.stdin.write(file)
|
||||||
|
p.stdin.close()
|
||||||
|
|
||||||
|
out = p.stdout.read()
|
||||||
|
rdb.set("toolbox:data:" + key, out, ex=TTL)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"key": key
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/flame_graph")
|
@app.post("/flame_graph")
|
||||||
def flame_graph(file: bytes = File(...), width: int = 1200):
|
def flame_graph(file: bytes = File(...), width: int = 1200):
|
||||||
@ -64,7 +129,7 @@ def flame_graph(file: bytes = File(...), width: int = 1200):
|
|||||||
p3.stdin.close()
|
p3.stdin.close()
|
||||||
|
|
||||||
out = p3.stdout.read()
|
out = p3.stdout.read()
|
||||||
rdb.set("toolbox:FlameGraph:" + key, out, ex=FLAMEGRAPH_TTL)
|
rdb.set("toolbox:data:" + key, out, ex=TTL)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"key": key,
|
"key": key,
|
||||||
|
BIN
toolbox-web/src/assets/Spectrograph.png
Normal file
BIN
toolbox-web/src/assets/Spectrograph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 589 KiB |
@ -30,7 +30,7 @@ export default class FlameGraph extends Vue {
|
|||||||
public apiUrl = API_URL
|
public apiUrl = API_URL
|
||||||
|
|
||||||
getGraphUrl() {
|
getGraphUrl() {
|
||||||
return `${API_URL}/flame_graph/${this.key}`
|
return `${API_URL}/data/${this.key}?type=image/svg%2Bxml`
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpload() {
|
onUpload() {
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="px-2">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-card class="mx-2" max-width="344" to="/tool/FlameGraph">
|
||||||
<v-card max-width="344" to="/tool/FlameGraph">
|
|
||||||
<v-img :src="require('@/assets/FlameGraph.png')" height="200px"></v-img>
|
<v-img :src="require('@/assets/FlameGraph.png')" height="200px"></v-img>
|
||||||
<v-card-title>FlameGraph</v-card-title>
|
<v-card-title>FlameGraph</v-card-title>
|
||||||
<v-card-subtitle><i>perf</i> profiling data visualization</v-card-subtitle>
|
<v-card-subtitle><i>perf</i> profiling data visualization</v-card-subtitle>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
|
||||||
|
<v-card class="mx-2" max-width="344" to="/tool/Spectrograph">
|
||||||
|
<v-img :src="require('@/assets/Spectrograph.png')" height="200px"></v-img>
|
||||||
|
<v-card-title>Spectrograph</v-card-title>
|
||||||
|
<v-card-subtitle>Generate spectrograph of audio files with <i>SoX</i></v-card-subtitle>
|
||||||
|
</v-card>
|
||||||
</v-row>
|
</v-row>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
90
toolbox-web/src/components/Spectrograph.vue
Normal file
90
toolbox-web/src/components/Spectrograph.vue
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<v-container>
|
||||||
|
|
||||||
|
<v-form v-model="valid" id="spectrograph-form">
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field :rules="[x => x < 20000 && x > 100]" label="Width" type="number" value="5000"
|
||||||
|
name="x"></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field :rules="[y => y < 20000 && y > 100]" label="Height" type="number" value="1100"
|
||||||
|
name="y"></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field :rules="[z => z < 120 && z > 20]" label="Z" type="number" value="100"
|
||||||
|
name="z"></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-text-field label="Label" name="label"></v-text-field>
|
||||||
|
<v-select label="Window" :items="windows" value="Kaiser" name="window"></v-select>
|
||||||
|
|
||||||
|
<v-file-input name="file" :disabled="!valid || loading" :loading="loading" label="Audio file" id="spectrograph"
|
||||||
|
@change="onUpload()"></v-file-input>
|
||||||
|
</v-form>
|
||||||
|
|
||||||
|
<a v-if="key" :href="getImageUrl()" target="_blank"><img width="100%" type="image/png" :src="getImageUrl()" id="img"></a>
|
||||||
|
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue'
|
||||||
|
import {Component} from 'vue-property-decorator'
|
||||||
|
import {toolByName} from '@/tools'
|
||||||
|
import axios from 'axios'
|
||||||
|
import {API_URL} from '@/config';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class Spectrograph extends Vue {
|
||||||
|
public tool = toolByName('Spectrograph')
|
||||||
|
public key: string | null = null
|
||||||
|
public loading = false
|
||||||
|
public apiUrl = API_URL
|
||||||
|
|
||||||
|
public valid = true
|
||||||
|
|
||||||
|
public windows = [
|
||||||
|
"Hann",
|
||||||
|
"Hamming",
|
||||||
|
"Bartlett",
|
||||||
|
"Rectangular",
|
||||||
|
"Kaiser",
|
||||||
|
"Dolph"
|
||||||
|
]
|
||||||
|
|
||||||
|
getImageUrl() {
|
||||||
|
return `${API_URL}/data/${this.key}?type=image/png`
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpload() {
|
||||||
|
this.loading = true;
|
||||||
|
const formData = new FormData(document.getElementById("spectrograph-form") as any);
|
||||||
|
const input = document.getElementById("spectrograph") as any;
|
||||||
|
|
||||||
|
if (!formData.get("label")) {
|
||||||
|
formData.set("label", " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
formData.set("type", input.value.split(".").slice(-1)[0])
|
||||||
|
|
||||||
|
axios.post(API_URL + '/spectrograph', formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
}).then(resp => {
|
||||||
|
const result = resp.data
|
||||||
|
|
||||||
|
this.key = result.key
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.img-zoom-result {
|
||||||
|
width: 325px;
|
||||||
|
height: 325px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -2,12 +2,14 @@ import Vue from 'vue'
|
|||||||
import VueRouter, {RouteConfig} from 'vue-router'
|
import VueRouter, {RouteConfig} from 'vue-router'
|
||||||
import Home from '../components/Home.vue'
|
import Home from '../components/Home.vue'
|
||||||
import FlameGraph from '@/components/FlameGraph.vue'
|
import FlameGraph from '@/components/FlameGraph.vue'
|
||||||
|
import Spectrograph from '@/components/Spectrograph.vue';
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
const routes: Array<RouteConfig> = [
|
const routes: Array<RouteConfig> = [
|
||||||
{path: '/', component: Home},
|
{path: '/', component: Home},
|
||||||
{path: '/tool/FlameGraph', component: FlameGraph}
|
{path: '/tool/FlameGraph', component: FlameGraph},
|
||||||
|
{path: '/tool/Spectrograph', component: Spectrograph}
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {Tool} from "@/models";
|
import {Tool} from "@/models";
|
||||||
|
|
||||||
export const tools: Tool[] = [
|
export const tools: Tool[] = [
|
||||||
{name: "FlameGraph", icon: "mdi-bug"}
|
{name: "FlameGraph", icon: "mdi-bug"},
|
||||||
|
{name: "Spectrograph", icon: "mdi-music"}
|
||||||
]
|
]
|
||||||
|
|
||||||
export const toolByName = (name: string) => {
|
export const toolByName = (name: string) => {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strictNullChecks": false,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"types": [
|
"types": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user