Problem implementation MediaRecorder and Shazam API through RapidApi

157 Views Asked by At

I'm currently working on a school project, where I'm creating a sort of clone of Shazam. I'm at the point where I can successfully record audio, and decode it into a base64string, just like the api asks, but it doesn't recognize any songs, since it's probably a wrong decoding process or wrong recording process. At the moment, I'm manually inserting my data into the api, so connection issues are out of the question.

This is what the api asks: Encoded base64 string of byte[] that generated from raw data less than 500KB (3-5 seconds sample are good enough for detection). The raw sound data must be 44100Hz, 1 channel (Mono), signed 16 bit PCM little endian. Other types of media are NOT supported, such as : mp3, wav, e...

I have a AudioRecorder class that handles the recording of the audio:

override fun start(outputFile: File) {
        createRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
            setAudioChannels(1)
            setAudioSamplingRate(44100)
            setAudioEncodingBitRate(16 * 44100)
            setOutputFile(outputFile.absolutePath)

            prepare()
            start()

            recorder = this
        }
    }

    override fun stop() {
        recorder?.stop()
        recorder?.reset()
        recorder = null
    }

    fun readAudioFile(file: File): ByteArray {
        return file.readBytes()
    }

Then i have a function that handles my recording, this gets executed at the right time as well

private suspend fun launchAudioRecording(audioRecorder: AudioRecorder, context: Context): String {
    Log.d("be.howest.rythmradar.ui.SearchingSongScreen", "be.howest.rythmradar.ui.launchAudioRecording - Start")
    // Start recording
    File(context.cacheDir, "audio.3gp").also {
        audioRecorder.start(it)
        audioFile = it
    }

    // Delay for 5 seconds to record audio
    delay(5000)

    // Stop recording
    audioRecorder.stop()

    // Do something with the recorded audio, such as converting it to Base64 string
    val audioData = audioRecorder.readAudioFile(audioFile!!)
    val base64String = convertToBase64String(audioData)
    
    return base64String
}

And finally my base64string converter:

private fun convertToBase64String(audioData: ByteArray): String {
    return Base64.encodeToString(audioData, Base64.DEFAULT)
}
1

There are 1 best solutions below

0
Doc On

I recently worked on a similar project. RapidApi doesn't work for me either but AudDApi! does. I managed to get hold of my base64String into a hidden form input element by setting its value to base64String and passed this to AudDApi which works perfectly. However, audio recording must not exceed 5 seconds else results in payload error.

You may try the code below to tweak your audio recording and base65String.

    <div>
  <form action="/" method="post">
    <input id="audioString" hidden />
    <button type="submit">Fetch</button>
  </form>
</div>

    <script type="text/javascript">
  class VoiceRecorder {
    constructor() {
      if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        console.log("getUserMedia supported");
      } else {
        console.log("getUserMedia is not supported on your browser!");
      }

      this.mediaRecorder;
      this.stream;
      this.chunks = [];
      this.isRecording = false;

      this.recorderRef = document.querySelector("#recorder");
      this.playerRef = document.querySelector("#player");
      this.startRef = document.querySelector("#start");
      this.stopRef = document.querySelector("#stop");

      this.startRef.onclick = this.startRecording.bind(this);
      this.stopRef.onclick = this.stopRecording.bind(this);

      this.constraints = {
        audio: true,
        video: false,
      };
    }

    handleSuccess(stream) {
      this.stream = stream;
      this.stream.oninactive = () => {
        console.log("Stream ended!");
      };
      this.recorderRef.srcObject = this.stream;
      this.mediaRecorder = new MediaRecorder(this.stream);
      console.log(this.mediaRecorder);
      this.mediaRecorder.ondataavailable =
        this.onMediaRecorderDataAvailable.bind(this);
      this.mediaRecorder.onstop = this.onMediaRecorderStop.bind(this);
      this.recorderRef.play();
      this.mediaRecorder.start();
    }

    handleError(error) {
      console.log("navigator.getUserMedia error: ", error);
    }

    onMediaRecorderDataAvailable(e) {
      this.chunks.push(e.data);
    }

    onMediaRecorderStop(e) {
      const blob = new Blob(this.chunks, { type: "audio/ogg; codecs=opus" });
      const audioURL = window.URL.createObjectURL(blob);
      this.playerRef.src = audioURL;
      this.chunks = [];
      this.stream.getAudioTracks().forEach((track) => track.stop());
      this.stream = null;
      var reader = new FileReader();
      reader.readAsDataURL(blob);
      reader.onloadend = function () {
        var base64String = reader.result;
        base64String = base64String.substring(base64String.indexOf(',') + 1);
        document.querySelector("#audioString").value = base64String;
      };
    }

    startRecording() {
      if (this.isRecording) return;
      this.isRecording = true;
      this.startRef.innerHTML =
        '<em>Recording <span class="material-symbols-outlined listen">noise_aware</span></em>';
      this.playerRef.src = "";
      navigator.mediaDevices
        .getUserMedia(this.constraints)
        .then(this.handleSuccess.bind(this))
        .catch(this.handleError.bind(this));
    }

    stopRecording() {
      if (!this.isRecording) return;
      this.isRecording = false;
      this.startRef.innerHTML =
        "<span class='material-symbols-outlined'>mic</span>";
      this.recorderRef.pause();
      this.mediaRecorder.stop();
    }
  }
  window.voiceRecorder = new VoiceRecorder();
</script>