2017-12-19 8 views
1

음성 인식을 위해 원본 오디오 데이터를 한 형식에서 다른 형식으로 변환하려고 시도하고 있습니다. 48Khz, 16-bit stereo signed BigEndian PCM :Raw PCM 데이터를 RIFF WAV로 변환

  • 오디오 포맷은 20ms 청크에 Discord 서버로부터 수신된다.
  • I는 InputStream

RIFF (little-endian) WAVE audio, 16-bit, mono 16,000Hz 오디오 데이터 길이 3840byte[]에서 수신되는 오디오 얻어 음성 인식을위한 CMU's Sphinx를 사용하고 있습니다. 이 byte[] 배열은 위에서 설명한 형식 1의 오디오를 20ms 개 포함합니다. 즉,이 오디오의 1 초가 3840 * 50이고 이는 192,000입니다. 그래서 초당 192,000 샘플입니다. 이는 바이트가 8 비트이고 우리의 오디오가 16 비트이고 스테레오에 대해 두 번 더하기 때문에 48KHz 샘플 속도, 시간 2 (96K 샘플)입니다. 그래서 48,000 * 2 * 2 = 192,000. (576,000을

private void addToPacket(byte[] toAdd) { 
    if(packet.length >= 576000 && !done) { 
     System.out.println("Processing needs to occur..."); 
     getResult(convertAudio()); 
     packet = null; // reset the packet 
     return; 
    } 

    byte[] newPacket = new byte[packet.length + 3840]; 
    // copy old packet onto new temp array 
    System.arraycopy(packet, 0, newPacket, 0, packet.length); 
    // copy toAdd packet onto new temp array 
    System.arraycopy(toAdd, 0, newPacket, 3840, toAdd.length); 
    // overwrite the old packet with the newly resized packet 
    packet = newPacket; 
} 

이것은 단지 바이트 [] 오디오 데이터 3초 포함될 때까지 하나의 큰 바이트 []에 새로운 패킷을 추가한다 :

그래서 먼저이 방법을 오디오 패킷이 수신 될 때마다 호출 샘플 또는 192000 * 3). 오디오 데이터의 3 초는 사용자가 봇의 활성화 핫 워드 (예 : "컴퓨터")를 말했는지 감지 할 수있는 충분한 시간입니다 (추측).

private byte[] convertAudio() { 
     // STEP 1 - DROP EVERY OTHER PACKET TO REMOVE STEREO FROM THE AUDIO 
     byte[] mono = new byte[96000]; 
     for(int i = 0, j = 0; i % 2 == 0 && i < packet.length; i++, j++) { 
      mono[j] = packet[i]; 
     } 

     // STEP 2 - DROP EVERY 3RD PACKET TO CONVERT TO 16K HZ Audio 
     byte[] resampled = new byte[32000]; 
     for(int i = 0, j = 0; i % 3 == 0 && i < mono.length; i++, j++) { 
      resampled[j] = mono[i]; 
     } 

     // STEP 3 - CONVERT TO LITTLE ENDIAN 
     ByteBuffer buffer = ByteBuffer.allocate(resampled.length); 
     buffer.order(ByteOrder.BIG_ENDIAN); 
     for(byte b : resampled) { 
      buffer.put(b); 
     } 
     buffer.order(ByteOrder.LITTLE_ENDIAN); 
     buffer.rewind(); 
     for(int i = 0; i < resampled.length; i++) { 
      resampled[i] = buffer.get(i); 
     } 

     return resampled; 
    } 

그리고 마지막으로는 시도가 음성을 인식 : 여기에 내가 사운드 데이터를 변환하는 방법은

private void getResult(byte[] toProcess) { 
    InputStream stream = new ByteArrayInputStream(toProcess); 
    recognizer.startRecognition(stream); 
    SpeechResult result; 
    while ((result = recognizer.getResult()) != null) { 
     System.out.format("Hypothesis: %s\n", result.getHypothesis()); 
    } 
    recognizer.stopRecognition(); 
} 

나는 데 문제가 CMUSphinx 충돌 또는 오류 메시지를 제공하지 않는다는 것입니다, 3 초마다 빈 가설이 생깁니다. 나는 정확히 왜 그런지는 모르지만, 내 생각으로는 사운드를 올바르게 변환하지 않았다는 것입니다. 어떤 아이디어? 어떤 도움이라도 대단히 감사하겠습니다.

+0

봐. 디버거에서 실행하십시오. 무슨 일이 일어나는지보십시오. 당신이 다른 모든 가치를 원한다면 왜 나는 하나씩 늘리십니까? 또한 항상 새로운 버퍼를 생성하지 말고 데이터를 복사하면 GC에 엄청난 스트레스가되며 완전히 쓸모가 없습니다. 당신이 원하는 크기의 버퍼 하나, 데이터를 복사해라. –

답변

0

따라서 실제로는 오디오를 byte[]에서 변환하는 훨씬 나은 자체 솔루션이 있습니다.

는 여기에 내가 꽤 잘 작동을 발견 내용은 다음과 같습니다 스테레오 떨어지는 루프 조건에서

 // Specify the output format you want 
     AudioFormat target = new AudioFormat(16000f, 16, 1, true, false); 
     // Get the audio stream ready, and pass in the raw byte[] 
     AudioInputStream is = AudioSystem.getAudioInputStream(target, new AudioInputStream(new ByteArrayInputStream(raw), AudioReceiveHandler.OUTPUT_FORMAT, raw.length)); 
     // Write a temporary file to the computer somewhere, this method will return a InputStream that can be used for recognition 
     try { 
      AudioSystem.write(is, AudioFileFormat.Type.WAVE, new File("C:\\filename.wav")); 
     } catch(Exception e) {} 
관련 문제