How to create sinwave animation while Recording/Playing audio in Android app ?
Asked Answered
W

3

5

It is easy to create in iOS application. Any one tell me the easy way to create the Sinwave animation while record and play the audio. I was tried soo many ways but I couldn't. Is there any third party frameworks for that?

Can any one help me out please..

Wounded answered 16/10, 2015 at 5:8 Comment(0)
E
20

Use the below code to get the waveform like ios... get the amplitude from the recorder and pass to updateAmplitude() function to get the variation in voice.

public class WaveFormView extends View {

      private static final float defaultFrequency          = 1.5f;
      private static final float defaultAmplitude          = 1.0f;
      private static final float defaultIdleAmplitude      = 0.01f;
      private static final float defaultNumberOfWaves      = 5.0f;
      private static final float defaultPhaseShift         = -0.15f;
      private static final float defaultDensity            = 5.0f;
      private static final float defaultPrimaryLineWidth   = 3.0f;
      private static final float defaultSecondaryLineWidth = 1.0f;

      private float phase;
      private float amplitude;
      private float frequency;
      private float idleAmplitude;
      private float numberOfWaves;
      private float phaseShift;
      private float density;
      private float primaryWaveLineWidth;
      private float secondaryWaveLineWidth;
      Paint mPaintColor;
      Rect rect;
      boolean isStraightLine = false;

      public WaveFormView(Context context) {
          super(context);
          setUp();
      }

      public WaveFormView(Context context, AttributeSet attrs) {
          super(context, attrs);
          setUp();
      }

      public WaveFormView(Context context, AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
          setUp();
      }

      @TargetApi(Build.VERSION_CODES.LOLLIPOP)
      public WaveFormView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
          super(context, attrs, defStyleAttr, defStyleRes);
          setUp();
      }

      private void setUp() {
          this.frequency = defaultFrequency;

          this.amplitude = defaultAmplitude;
          this.idleAmplitude = defaultIdleAmplitude;

          this.numberOfWaves = defaultNumberOfWaves;
          this.phaseShift = defaultPhaseShift;
          this.density = defaultDensity;

          this.primaryWaveLineWidth = defaultPrimaryLineWidth;
          this.secondaryWaveLineWidth = defaultSecondaryLineWidth;
          mPaintColor = new Paint();
          mPaintColor.setColor(Color.WHITE);
      }

      public void updateAmplitude(float ampli, boolean isSpeaking) {
          this.amplitude = Math.max(ampli, idleAmplitude);
          isStraightLine = isSpeaking;
      }


      @Override
      protected void onDraw(Canvas canvas) {
          rect = new Rect(0,0,canvas.getWidth(),canvas.getWidth());
          canvas.drawColor(Color.BLUE);
          /*canvas.drawRect(rect, mPaintColor);*/
          if(isStraightLine) {
              for (int i = 0; i < numberOfWaves; i++) {
                  mPaintColor.setStrokeWidth(i == 0 ? primaryWaveLineWidth : secondaryWaveLineWidth);
                  float halfHeight = canvas.getHeight() / 2;
                  float width = canvas.getWidth();
                  float mid = canvas.getWidth() / 2;

                  float maxAmplitude = halfHeight - 4.0f;
                  float progress = 1.0f - (float) i / this.numberOfWaves;
                  float normedAmplitude = (1.5f * progress - 0.5f) * this.amplitude;
                  Path path = new Path();

                  float multiplier = Math.min(1.0f, (progress / 3.0f * 2.0f) + (1.0f / 3.0f));

                  for (float x = 0; x < width + density; x += density) {
                      // We use a parable to scale the sinus wave, that has its peak in the middle of the view.
                      float scaling = (float) (-Math.pow(1 / mid * (x - mid), 2) + 1);

                      float y = (float) (scaling * maxAmplitude * normedAmplitude * Math.sin(2 * Math.PI * (x / width) * frequency + phase) + halfHeight);

                      if (x == 0) {
                          path.moveTo(x, y);
                      } else {
                          path.lineTo(x, y);
                      }
                  }
                  mPaintColor.setStyle(Paint.Style.STROKE);
                  mPaintColor.setAntiAlias(true);
                  canvas.drawPath(path, mPaintColor);

              }
          } else {
              canvas.drawLine(5,canvas.getHeight()/2,canvas.getWidth(),canvas.getHeight()/2,mPaintColor );
              canvas.drawLine(0,canvas.getHeight()/2,canvas.getWidth(),canvas.getHeight()/2,mPaintColor);
              canvas.drawLine(-5, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight()/2,mPaintColor );
          }
          this.phase += phaseShift;
          invalidate();
      }
}
Exponible answered 19/10, 2015 at 10:6 Comment(3)
Thanks Sumathi for your suggestions. Hope it may help for us.Wounded
waves going out of the window is that any correction this view. thanks in advanceCupid
Wow thank you Vennila. Thats awesome ! Why isn't this accepted as an answer ? I would like again If I could :)Infiltration
H
4

Based on @Vennila answer.

Here is Kotlin version of WaveFormView.

class SiriVisualView(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private var phase = 0f
    private var amplitude = 1.5f // wave height
    private var frequency = 1.2f // number of waves
    private var idleAmplitude = 0.05f // default height
    private var numberOfWaves = 8.0f // number of wave lines
    private var phaseShift = -0.1f // wave speed
    private var primaryWaveLineWidth = 2.0f // outer line stroke
    private var secondaryWaveLineWidth = 0.5f // inner line stroke
    private var density = 5f
    var mPaintColor: Paint = Paint()
    var isStraightLine = false

    fun updateViewColor(@ColorInt color: Int) {
        mPaintColor.color = color
    }

    fun updateSpeaking(isSpeaking: Boolean) {
        isStraightLine = isSpeaking
    }

    fun updateAmplitude(ampli: Float) {
        amplitude = Math.max(ampli, idleAmplitude)
    }

    fun updateSpeed(phase: Float) {
        phaseShift = phase
    }

    /** Here you can override default wave values and customize SiriVisualView

        updateNumberOfWaves()
        updatePrimaryLineStroke()
            . . .
    */

    override fun onDraw(canvas: Canvas) {
        if (isStraightLine) {
            var i = 0
            while (i < numberOfWaves) {
                mPaintColor.strokeWidth = if (i == 0) primaryWaveLineWidth else secondaryWaveLineWidth
                val halfHeight = height / 2.toFloat()
                val width = width.toFloat()
                val mid = width / 2.toFloat()
                val maxAmplitude = halfHeight - 4.0f
                val progress = 1.0f - i.toFloat() / numberOfWaves
                val normedAmplitude = (1.5f * progress - 0.5f) * amplitude
                val path = Path()
                val multiplier = Math.min(1.0f, progress / 3.0f * 2.0f + 1.0f / 3.0f)
                var x = 0f
                while (x < width + density) {
                    // We use a parable to scale the sinus wave, that has its peak in the middle of the view.
                    val scaling = (-Math.pow(
                        1 / mid * (x - mid).toDouble(),
                        2.0
                    ) + 1).toFloat()
                    val y = (scaling * maxAmplitude * normedAmplitude * Math.sin(2 * Math.PI * (x / width) * frequency + phase) + halfHeight).toFloat()
                    if (x == 0f) {
                        path.moveTo(x, y)
                    } else {
                        path.lineTo(x, y)
                    }
                    x += density
                }
                mPaintColor.style = Paint.Style.STROKE
                mPaintColor.isAntiAlias = true
                canvas.drawPath(path, mPaintColor)
                i++
            }
        } else {
            for(i in 5 downTo -5 step 5) {
                canvas.drawLine(
                    i.toFloat(),
                    height / 2.toFloat(),
                    width.toFloat(),
                    height / 2.toFloat(),
                    mPaintColor
                )
            }
        }
        phase += phaseShift
        invalidate()
    }
}

Add view to your XML file

<linc.com.visualtest.SiriVisualView
    android:id="@+id/siriView"
    android:layout_width="0dp"
    android:layout_height="200dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"/>

Update and customize view inside your Activity or Fragment

siriView.apply {
        updateSpeaking(true)
        updateViewColor(Color.WHITE)
        updateAmplitude(0.5f)
        updateSpeed(-0.1f)
    }

Here some screenshots of the SiriVisualView

enter image description here

enter image description here

enter image description here

Hobie answered 1/11, 2020 at 11:2 Comment(0)
S
0

I needed something similar but instead of sine wave, I needed vertical lines(audio waveform). I modified an answer given above to achieve it(Thanks @Vennila). Hope it is helpful for someone else :)

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;

public class WaveFormView extends View {

    private static final float defaultFrequency          = 1.5f;
    private static final float defaultAmplitude          = 1.0f;
    private static final float defaultIdleAmplitude      = 0.01f;
    private static final float defaultNumberOfWaves      = 25.0f;
    private static final float defaultPhaseShift         = -0.15f;
    private static final float defaultDensity            = 5.0f;
    private static final float defaultPrimaryLineWidth   = 6.0f;
    private static final float defaultSecondaryLineWidth = 6.0f;

    private float phase;
    private float amplitude;
    private float frequency;
    private float idleAmplitude;
    private float numberOfWaves;
    private float phaseShift;
    private float density;
    private float primaryWaveLineWidth;
    private float secondaryWaveLineWidth;
    Paint mPaintColor;
    Rect rect;
    boolean isStraightLine = false;

    public WaveFormView(Context context) {
        super(context);
        setUp();
    }

    public WaveFormView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setUp();
    }

    public WaveFormView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setUp();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public WaveFormView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setUp();
    }

    private void setUp() {
        this.frequency = defaultFrequency;

        this.amplitude = defaultAmplitude;
        this.idleAmplitude = defaultIdleAmplitude;

        this.numberOfWaves = defaultNumberOfWaves;
        this.phaseShift = defaultPhaseShift;
        this.density = defaultDensity;

        this.primaryWaveLineWidth = defaultPrimaryLineWidth;
        this.secondaryWaveLineWidth = defaultSecondaryLineWidth;
        mPaintColor = new Paint();
        mPaintColor.setColor(Color.WHITE);
    }

    public void updateAmplitude(float ampli, boolean isSpeaking) {
        this.amplitude = Math.max(ampli, idleAmplitude);
        isStraightLine = isSpeaking;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        rect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
        canvas.drawColor(getResources().getColor(R.color.dark_grey));

        if (isStraightLine) {
            int numLines = (int) numberOfWaves;
            float lineSpacing = canvas.getWidth() / (numLines + 1);
            float halfHeight = canvas.getHeight() / 2;
            float maxAmplitude = halfHeight - 4.0f;

            for (int i = 0; i < numLines; i++) {
                float lineX = (i + 1) * lineSpacing;
                float normedAmplitude = (1.5f * (1.0f - (float) i / this.numberOfWaves) - 0.5f) * this.amplitude;
                float lineHeight = maxAmplitude * normedAmplitude;

                mPaintColor.setStrokeWidth(i == 0 ? primaryWaveLineWidth : secondaryWaveLineWidth);
                canvas.drawLine(lineX, halfHeight - lineHeight / 2, lineX, halfHeight + lineHeight / 2, mPaintColor);
            }
        } else {
            canvas.drawLine(5, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, mPaintColor);
            canvas.drawLine(0, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, mPaintColor);
            canvas.drawLine(-5, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, mPaintColor);
        }

        this.phase += phaseShift;
        invalidate();
    }
}
Surface answered 10/4 at 8:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.