staskobzar / vue-audio-visual

VueJS audio visualization components
MIT License
693 stars 111 forks source link

AVCircle as a feature for AVMedia #50

Closed alectrocute closed 3 years ago

alectrocute commented 3 years ago

Love this project. One request – I'd love to use AVCircle with WebRTC/getUserMedia.

As a workaround, I'm currently trying to get it to visualize MediaStream data with a live srcObject to no avail. Works great once the recording is finished, but not during (even though previewplayer plays the live audio with no issue). Playtime duration works however, just no bars.

I'd be happy to help with a PR if you can help elaborate what exactly would need to be done

    <audio class="mt-4" ref="previewplayer" controls></audio>
    <av-circle
      :canv-height="350"
      :canv-width="350"
      :bar-color="'#fff'"
      :bar-width="5"
      :outline-width="0"
      :bar-length="80"
      :outline-color="'#fff'"
      :progress="false"
      :playtime="true"
      ref-link="previewplayer"
    ></av-circle>

...

    async onMediaSuccess(stream) {
      this.stream = stream;
      this.active = true;

      // for audio only
      this.mediaStreamRecorder = new RecordRTC(
        this.stream,
        this.mediaStreamRecorderOptions
      );

      await this.mediaStreamRecorder.startRecording();

      this.$refs.previewplayer.srcObject = this.stream;
      this.$refs.previewplayer.play();

      this.working = true;
    },
staskobzar commented 3 years ago

Hello Alec,

Thank you. This is a nice way you do media stream setup. I was not thinking about realtime media in av-circle when working on it but you have a good point. I will try to check this when I have time to understand it better. However, what you can try to see if it changes something is to try to make sure that av-circle element is rendered after you set src and start playing. You can use v-if="working" in av-circle element.

I am not sure exactly when I have time to work on it but I will let you know here.

Have a good day!

alectrocute commented 3 years ago

@staskobzar Thanks for your words!

I've managed to get this working, and in spirit of OSS, I owe it to post here! I'm moving across the country this week otherwise I'd make a PR like a normal person. This is currently a hacky implementation, but works wonderfully with WebRTC & AVMedia. Just needs a full once-over of adding props, and integrating those parameters back into this.circle(data) as appropriate.

In AVMedia.js methods –

    draw: function() {
        const data = new Uint8Array(this.analyser.fftSize);

        if (this.canvFillColor) this.ctx.fillStyle = this.canvFillColor;
        this.ctx.clearRect(0, 0, this.canvWidth, this.canvHeight);
        this.ctx.beginPath();
        this.ctx.strokeStyle = this.lineColor;

        if (this.type === "frequ") {
            this.analyser.getByteFrequencyData(data);
            this.frequ(data);
        } else if (this.type === "circle") {
            this.analyser.getByteFrequencyData(data);
            this.circle(data);
        } else {
            this.analyser.getByteTimeDomainData(data);
            this.wform(data);
        }

        requestAnimationFrame(this.draw);
    },
    frequ: function(data) {
        ...
    },
    circle: function(data) {
        const cx = this.canvWidth / 2; // center X
        const cy = this.canvHeight / 2; // center Y
        const r = 65;
        const h = this.canvHeight;
        const lineWidth = this.lineWidth;
        const lineSpace = 10;
        const arcStep = Math.ceil(lineWidth + lineSpace);
        const frqBits = this.analyser.frequencyBinCount;

        const step = ((lineWidth + lineSpace) / data.length) * (2 * Math.PI);
        const barLen = this.canvWidth / 1.2 - r;
        let angle = Math.PI;

        let x = 0;

        this.ctx.lineWidth = this.lineWidth || 0.5;

        data.forEach((_, index) => {
            angle += step;
            if (index % arcStep) {
                return;
            }
            const bits = Math.round(
                data.slice(index, index + arcStep).reduce((v, t) => t + v, 0) /
                arcStep
            );

            const blen = r + (bits / 255.0) * barLen;
            this.ctx.beginPath();
            this.ctx.lineCap = "round";
            this.ctx.moveTo(r * Math.cos(angle) + cx, r * Math.sin(angle) + cy);
            this.ctx.lineTo(
                blen * Math.cos(angle) + cx,
                blen * Math.sin(angle) + cy
            );
            this.ctx.stroke();
        });
    },
    wform: function(data) {
        ...
    }

PS vue-audio-visual rules. That is all.

alectrocute commented 3 years ago

Disregard. #51

staskobzar commented 3 years ago

Hi Alec Published new version 2.1.0 with your contribution.

Thank you!