왜 자작 음악 플레이어의 소리가 흩날릴까

10594 단어 Gomusictech

개시하다


며칠 전에 나는 자기 집을 위한 음악 플레이어를 만들었다.음악 플레이어도 좀 특이하지만, 따로 랩베리피로 시작한 스트리밍 서버에서 보비스의 소리를 받아 스피커를 재생하는 물건이다.
Music Player Daemon은 스트리밍 서버로 사용됩니다.

도대체 왜 했을까


Music Player Daemon은 음악 파일이 저장된 디렉토리에서 음악 파일을 재생성하는 데몬입니다.음악 파일이 있는 서버에서 사운드를 낼 수 있지만 래스퍼리 피에는 스피커가 없습니다.그리고 가능하다면 다른 터미널로 소리를 틀고 싶습니다.이를 위한 용도로 뮤직플레이어 데몬은 HTTPD로서도 편지를 배달할 수 있는 기능이 있다.
개인적인 용도로 음악 파일은 랩버리 피로 한 곳에 보관하고 다른 터미널은 듣고 싶은 그런 구조를 만들려고 한다.

하지만 뮤직플레이어 대몬이 공식 제공한 컨트롤러 mpc는 HTTPD에 게시된 소리를 재생할 수 없다.브라우저 등으로 HTTPD의 URL을 열면 되지만 조작하는 곳과 재생하는 곳이 달라 조금 납득하기 어렵다.
그래서 소리를 틀고 곡을 진행할 수 있는 다음 송신 정도의 지령선 도구를 만들고 싶다.

방법


Go 언어로 만들었어요.
https://github.com/mattn/mpcc
명령-addr은 Music Player Daemon의 주소(IP:PORT)를 플래그로, HTTPD의 URL을 -stream 플래그로 지정합니다.환경 변수는 기본값을 제공합니다.
뮤직플레이어 데몬과의 통신은 github.com/fhs/gompd/mpd 패키지가 있어서 그걸 사용했어요.스피커github.com/hajimehoshi/oto를 어떻게 재생하는지에 관해서는oto의 저자인 Hajime Hoshi에게 Vorbis의 디코딩 방법github.com/jfreymuth/oggvorbis을 추천하기 때문에 이 방법을 사용했다.

문제가 생기다


만드는 과정에서 나는 왜 푸푸 소리를 내는지 알아차렸다.일지에서 조사를 했지만 잘 모르겠습니다.vim-jp의 슬랙 여러분도 디버깅에 참여했지만 모르겠습니다. 다음과 같은 추측을 했습니다.
흐르는 전송 서버에서 바이트열을 수신하고oggvolbis로 디코딩하여oto에서 이런 프로세스를 재생하는 데 버퍼링할 수 없습니다
다양한 버퍼링과 처리를 시도했지만 전혀 개선되지 않았습니다. 라이브러리 어딘가에 오류가 있다고 생각했습니다.

유감스럽지만, 오류는 당신의 코드입니다.


각종 조사에서 다음과 같은 코드에 문제가 있음을 발견하였다.
samples := make([]float32, bufferSize)
for {
	st.Read(samples)

	for i, val := range samples {
		if val < -1 {
			val = -1
		}
		if val > +1 {
			val = +1
		}
		valInt16 := int16(val * (1<<15 - 1))
		low := byte(valInt16)
		high := byte(valInt16 >> 8)
		buf[i*2+0] = low
		buf[i*2+1] = high
	}
	player.Write(buf)
}
이것은 oggvorbis 버퍼 구역에서 샘플을 채취하여low/high로 스피커로 나누는 처리입니다.이 코드 자체는oto를 사용한 음성 재생 라이브러리github.com/faiface/beep에서 참고한 것이다.스피커에 기록된 바이트열buf은 샘플링 버퍼samples([]flat32)를 low/high로 분해하여 samples 사이즈의 2배이다.
numBytes := bufferSize * 2
buf := make([]byte, numBytes)
실제로 상술한 코드의 st.Read(samples)는 되돌아오는 값이 있다.이것은 읽는 샘플의 수이지만, 매번 읽는 횟수는 다르다.이 값을 사용하지 않고 samples를 슬라이스하면 쓰레기는 고정 사이즈로 만든 buf 뒤에 남는다.변명입니다만, 이 인코딩beep에서 빌려주세요.은 했습니다.(핑계를 대는 것 같다)
buf 뒤에 만들어진 망가진 소리 데이터가 매번 스피커로 흐르기 때문에 찰칵찰칵 소리가 날아야 한다.
for i, val := range samples[:n] {
	if val < -1 {
		val = -1
	}
	if val > +1 {
		val = +1
	}
	valInt16 := int16(val * (1<<15 - 1))
	low := byte(valInt16)
	high := byte(valInt16 >> 8)
	buf[i*2+0] = low
	buf[i*2+1] = high
}
player.Write(buf[:n*2])
읽을 수량과 쓸 수량의 슬라이스를 통해 오류가 제거되었습니다.Go 언어에서는 소킷 통신 프로그램을 만들 때 아래 코드를 자주 볼 수 있지만, 결과적으로 실현되지 못한 것과 같다.
var buf [4096]byte
n, err := conn.Read(buf[:])
if err != nil {
    return err
}
fmt.Println(string(buf[:n]))

끝말


읽은 데이터는 읽은 크기로 처리하십시오. (자동 끊기)

좋은 웹페이지 즐겨찾기