mirror of
				https://github.com/simon987/toolbox.git
				synced 2025-10-31 08:26:53 +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 | ||||
| from subprocess import Popen, PIPE | ||||
| 
 | ||||
| from fastapi.params import Form | ||||
| from starlette.responses import Response | ||||
| 
 | ||||
| app = FastAPI() | ||||
| @ -21,15 +22,79 @@ def read_root(): | ||||
|     return {"Hello": "World"} | ||||
| 
 | ||||
| 
 | ||||
| HOUR = 3600 | ||||
| DAY = HOUR * 24 | ||||
| FLAMEGRAPH_TTL = DAY * 7 | ||||
| TTL = 60 * 60 | ||||
| 
 | ||||
| @app.get("/flame_graph/{key}") | ||||
| def flame_graph_get(key: str): | ||||
| 
 | ||||
|     data = rdb.get("toolbox:FlameGraph:" + key) | ||||
|     return Response(content=data, media_type="image/svg+xml") | ||||
| @app.get("/data/{key}") | ||||
| 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") | ||||
| 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() | ||||
| 
 | ||||
|         out = p3.stdout.read() | ||||
|         rdb.set("toolbox:FlameGraph:" + key, out, ex=FLAMEGRAPH_TTL) | ||||
|         rdb.set("toolbox:data:" + key, out, ex=TTL) | ||||
| 
 | ||||
|         return { | ||||
|             "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 | ||||
| 
 | ||||
|   getGraphUrl() { | ||||
|     return `${API_URL}/flame_graph/${this.key}` | ||||
|     return `${API_URL}/data/${this.key}?type=image/svg%2Bxml` | ||||
|   } | ||||
| 
 | ||||
|   onUpload() { | ||||
|  | ||||
| @ -1,13 +1,17 @@ | ||||
| <template> | ||||
|   <div> | ||||
|   <div class="px-2"> | ||||
|     <v-row> | ||||
|       <v-col> | ||||
|         <v-card max-width="344" to="/tool/FlameGraph"> | ||||
|         <v-card class="mx-2" max-width="344" to="/tool/FlameGraph"> | ||||
|           <v-img :src="require('@/assets/FlameGraph.png')" height="200px"></v-img> | ||||
|           <v-card-title>FlameGraph</v-card-title> | ||||
|           <v-card-subtitle><i>perf</i> profiling data visualization</v-card-subtitle> | ||||
|         </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> | ||||
|   </div> | ||||
| </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 Home from '../components/Home.vue' | ||||
| import FlameGraph from '@/components/FlameGraph.vue' | ||||
| import Spectrograph from '@/components/Spectrograph.vue'; | ||||
| 
 | ||||
| Vue.use(VueRouter) | ||||
| 
 | ||||
| const routes: Array<RouteConfig> = [ | ||||
|   {path: '/', component: Home}, | ||||
|   {path: '/tool/FlameGraph', component: FlameGraph} | ||||
|   {path: '/tool/FlameGraph', component: FlameGraph}, | ||||
|   {path: '/tool/Spectrograph', component: Spectrograph} | ||||
| ] | ||||
| 
 | ||||
| const router = new VueRouter({ | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| import {Tool} from "@/models"; | ||||
| 
 | ||||
| export const tools: Tool[] = [ | ||||
|   {name: "FlameGraph", icon: "mdi-bug"} | ||||
|   {name: "FlameGraph", icon: "mdi-bug"}, | ||||
|   {name: "Spectrograph", icon: "mdi-music"} | ||||
| ] | ||||
| 
 | ||||
| export const toolByName = (name: string) => { | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
|     "experimentalDecorators": true, | ||||
|     "esModuleInterop": true, | ||||
|     "allowSyntheticDefaultImports": true, | ||||
|     "strictNullChecks": false, | ||||
|     "sourceMap": true, | ||||
|     "baseUrl": ".", | ||||
|     "types": [ | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user