audio renderer v1
This commit is contained in:
parent
8be959a917
commit
64ac22f8d9
4 changed files with 103 additions and 43 deletions
|
|
@ -3,6 +3,19 @@
|
|||
<g id="Group_126" data-name="Group 126" transform="translate(0 0)">
|
||||
<rect id="Rectangle_478" data-name="Rectangle 478" :width="bubbleWidth" height="34" rx="10" transform="translate(0 0)" fill="#b9b9b9"/>
|
||||
<g id="Group_125" data-name="Group 125">
|
||||
|
||||
<path v-for="(path, index) in paths"
|
||||
:key="index"
|
||||
:id="path.id"
|
||||
:data-name="path.name"
|
||||
:d="path.d"
|
||||
:transform="path.transform"
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-linecap="round"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
|
||||
<!-- <path id="Path_328" data-name="Path 328" d="M0,25v-10" transform="translate(10 0)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.5"/>
|
||||
<path id="Path_328" data-name="Path 328" d="M0,25v-15" transform="translate(15 0)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.5"/>
|
||||
<path id="Path_328" data-name="Path 328" d="M0,25v-20" transform="translate(20 0)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.5"/>
|
||||
|
|
@ -10,21 +23,25 @@
|
|||
<path id="Path_328" data-name="Path 328" d="M0,25v-15" transform="translate(25 0)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.5"/>
|
||||
<path id="Path_328" data-name="Path 328" d="M0,25v-10" transform="translate(30 0)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.5"/>
|
||||
|
||||
<path id="Path_328" data-name="Path 328" d="M0,25v-5" transform="translate(120 0)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.5"/> -->
|
||||
</g>
|
||||
<path id="Path_328" data-name="Path 328" d="M0,25v-5" transform="translate(120 0)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.5"/>
|
||||
--> </g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { defineComponent, computed, ref } from 'vue'
|
||||
export default defineComponent ({
|
||||
name: 'AudioInput',
|
||||
props: {
|
||||
bubbleWidth: Number
|
||||
bubbleWidth: Number,
|
||||
paths: Array
|
||||
},
|
||||
setup(props){
|
||||
|
||||
// need audioPath type/interface
|
||||
|
||||
const center = computed(() => {
|
||||
if (props.bubbleWidth) {
|
||||
return 175 - props.bubbleWidth / 2;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<audio-input
|
||||
v-if="renderAudio"
|
||||
:bubbleWidth="reactiveWidth"
|
||||
:paths="paths"
|
||||
/>
|
||||
<text-input
|
||||
v-else
|
||||
|
|
@ -20,13 +21,19 @@ export default defineComponent({
|
|||
components: {TextInput, AudioInput},
|
||||
setup() {
|
||||
|
||||
const { input, renderAudio, reactiveWidth, textInputXoffset } = useInputRenderer();
|
||||
const {
|
||||
input,
|
||||
renderAudio,
|
||||
reactiveWidth,
|
||||
textInputXoffset,
|
||||
paths } = useInputRenderer();
|
||||
|
||||
return {
|
||||
input,
|
||||
textInputXoffset,
|
||||
renderAudio,
|
||||
reactiveWidth
|
||||
reactiveWidth,
|
||||
paths
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,17 +10,20 @@ export default function useInputRenderer() {
|
|||
// program ref varaiables
|
||||
const renderAudio = ref(false);
|
||||
const reactiveWidth = ref(10);
|
||||
const pathOffset = ref(10);
|
||||
const paths = ref([]);
|
||||
|
||||
// get input feedback & handler function
|
||||
const { keyDownHandler, input } = useKeyDownHandler();
|
||||
// get text Offset and & textRender function
|
||||
const { textInputXoffset, renderTextInput } = useTextInput(input, renderAudio);
|
||||
|
||||
// why do we have audio stuff here
|
||||
let stream: any;
|
||||
const {handleStream} = useStreamRecorder(renderAudio)
|
||||
// why do we have audio stuff here ?
|
||||
let stream: MediaStream;
|
||||
const {renderStream} = useStreamRecorder(renderAudio)
|
||||
onMounted(async () => {
|
||||
stream = await useMediaStream()
|
||||
const streamRes = await useMediaStream()
|
||||
stream = streamRes.stream;
|
||||
})
|
||||
|
||||
// stop stream recording on space keyup event
|
||||
|
|
@ -30,6 +33,7 @@ export default function useInputRenderer() {
|
|||
renderAudio.value = false;
|
||||
input.value = "";
|
||||
reactiveWidth.value = 10;
|
||||
pathOffset.value = 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +58,7 @@ export default function useInputRenderer() {
|
|||
if (input === " " && renderAudio.value === false) {
|
||||
console.log("record");
|
||||
renderAudio.value = true;
|
||||
handleStream(stream.stream, reactiveWidth);
|
||||
renderStream(stream, reactiveWidth, pathOffset, paths);
|
||||
return;
|
||||
}
|
||||
renderTextInput();
|
||||
|
|
@ -64,7 +68,8 @@ export default function useInputRenderer() {
|
|||
input,
|
||||
renderAudio,
|
||||
reactiveWidth,
|
||||
textInputXoffset
|
||||
textInputXoffset,
|
||||
paths
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,23 +1,14 @@
|
|||
import { watch, ref, inject } from "vue";
|
||||
import useAudioRenderer from "@/composables/audioRenderer/useAudioRenderer";
|
||||
import AnimeFunc from "@/types/animejs/index";
|
||||
export default function useStreamRecorder(render: any) {
|
||||
import {Ref} from '@/types/vueRef/index';
|
||||
import { ref } from 'vue';
|
||||
export default function useStreamRecorder(render: Ref<boolean>) {
|
||||
let streamRecorder: MediaRecorder;
|
||||
const chunks: Blob[] = [];
|
||||
|
||||
// imports animejs safely
|
||||
let anime: AnimeFunc;
|
||||
const animeInject: AnimeFunc | undefined = inject("animejs");
|
||||
if (animeInject) anime = animeInject;
|
||||
|
||||
window.AudioContext = window.AudioContext;
|
||||
let audio_context = new AudioContext();
|
||||
const analyzer = audio_context.createAnalyser();
|
||||
// window.AudioContext = window.AudioContext;
|
||||
const audioContext = new AudioContext();
|
||||
const analyzer = audioContext.createAnalyser();
|
||||
analyzer.fftSize = 2048;
|
||||
var bufferLengthAlt = analyzer.frequencyBinCount;
|
||||
var dataArrayAlt = new Uint8Array(bufferLengthAlt);
|
||||
|
||||
let source: any;
|
||||
const dataArray = new Uint8Array(analyzer.frequencyBinCount);
|
||||
|
||||
const initRecorder = async () => {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
|
|
@ -32,36 +23,76 @@ export default function useStreamRecorder(render: any) {
|
|||
}
|
||||
}
|
||||
|
||||
function visualize(w: any) {
|
||||
interface AmplitudePath {
|
||||
id: string;
|
||||
name: string;
|
||||
d: string;
|
||||
transform: string;
|
||||
}
|
||||
|
||||
function drawAlt() {
|
||||
const createAudioPath = (a: number, offset: number) => {
|
||||
const identifier = `path_${offset}`
|
||||
const path: AmplitudePath = {
|
||||
id: identifier,
|
||||
name: identifier,
|
||||
d: `M0,25v${-a}`,
|
||||
transform: `translate(${offset} 0)`
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
const xInitial = 24;
|
||||
const xFinal = 640;
|
||||
|
||||
function visualize(w: Ref<number>, p: Ref<number>, paths: Ref<any>) {
|
||||
|
||||
const amplitude = () => {
|
||||
const dataView = dataArray.slice(xInitial, xFinal);
|
||||
const sum = dataView.reduce((a, b) => a + b);
|
||||
const average = sum / dataView.length;
|
||||
const scaled = average * 0.45;
|
||||
return scaled;
|
||||
}
|
||||
|
||||
function draw() {
|
||||
if (!render.value) {
|
||||
return;
|
||||
}
|
||||
const drawVisual = requestAnimationFrame(drawAlt);
|
||||
analyzer.getByteTimeDomainData(dataArrayAlt);
|
||||
requestAnimationFrame(draw);
|
||||
analyzer.getByteFrequencyData(dataArray);
|
||||
|
||||
// max-width
|
||||
if (w.value === 190) {
|
||||
if (w.value === 300) {
|
||||
return;
|
||||
}
|
||||
|
||||
// increase width and center as time passes
|
||||
// increase width as time passes
|
||||
|
||||
// get amplitude
|
||||
const a = amplitude();
|
||||
|
||||
if (a > 1) {
|
||||
|
||||
w.value++;
|
||||
const center = 175 - w.value / 2;
|
||||
// w.value= center;
|
||||
}
|
||||
drawAlt();
|
||||
p.value += 5;
|
||||
console.log(a)
|
||||
const path = createAudioPath(a, p.value);
|
||||
paths.value.push(path)
|
||||
}
|
||||
|
||||
function handleStream(s: MediaStream, w: any) {
|
||||
source = audio_context.createMediaStreamSource(s);
|
||||
// create path item
|
||||
}
|
||||
draw();
|
||||
}
|
||||
|
||||
function renderStream(s: MediaStream, w: Ref<number>, pathOffset: Ref<number>, paths: Ref<any>) {
|
||||
const source = audioContext.createMediaStreamSource(s);
|
||||
source.connect(analyzer);
|
||||
visualize(w);
|
||||
visualize(w, pathOffset, paths);
|
||||
}
|
||||
|
||||
return {
|
||||
initRecorder,
|
||||
handleStream
|
||||
renderStream
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue