간단한 Unix 오디오 인터페이스를 찾고 있습니다.
재미삼아 옛날 게임을 포팅해서 x86 어셈블리 언어를 배우고 있습니다. 가장 까다로운 부분은 간단한 사운드(주로 구형파)를 재생하는 방법을 찾는 것입니다. 현재 게임은 스피커에 직접 쓸 수 있을 것으로 예상됩니다. 현재 Unix 시스템에서 오디오를 재생하는 가장 쉬운 방법은 무엇입니까?
기준
보다 정확하게는 이것이 내가 찾고 있는 것입니다(가장 중요한 기준).
- 파형을 재생할 수 있습니다. (8비트, 8000Hz이면 충분합니다.)
- 어셈블리 언어에서 호출할 수 있을 만큼 간단합니다.
- 최신 Debian GNU/Linux 시스템에 적합하며 루트 없이 간단한 패키지 설치가 가능합니다.
- 사용자는 라이브러리나 기타 종속성을 설치할 필요가 없습니다.
- 가능한 한 많은 UNIX 시스템에 걸쳐 이식 가능합니다. (특히 {Net,Free,Open}BSD. Solaris, MacOS 및 WSL에는 이 작업을 수행하는 고유한 방법이 있을 것으로 예상됩니다.)
우선 사항
제가 포팅하는 코드는 32비트 x86 어셈블리 언어이므로 지금은 64비트로 변환하고 싶지 않습니다. 또한 코드는 libc와 링크될 필요가 없으며 가능하면 그렇게 유지하는 것이 가장 좋습니다.
연구
내가 조사한 몇 가지 방법은 다음과 같습니다.
/dev/audio
내가 바라는 것은 이것이다: 난 그냥 열어
/dev/audio
파일로 저장하고 바이트를 씁니다. 불행하게도 Linux 커널은 더 이상 기본적으로 이 장치와 함께 제공되지 않는 것 같으므로 커널 모듈(snd-pcm-oss)을 루트로 설치해야 합니다. (또한 이는 사소한 문제이지만 Linux는 이전 SunOS 의미 체계를 사용하며 멀티플렉싱을 허용하지 않습니다.)직접 발언자 액세스(
/dev/tty0
,/dev/input/by-path/platform-pcspkr-event-spkr
)이 게임은 원래 이런 목적으로 작성되었습니다. 그러나 PC 내장 스피커에 직접 쓰려면 Linux에서 루트 구성과 커널 모듈(pcspkr)이 필요합니다. 또한 PC 스피커가 연결되지 않은 노트북이나 기타 컴퓨터에서는 작동하지 않습니다.
알자스
Linux에는 ALSA(Advanced Linux Sound Architecture)라는 커널 모듈 없이 사용할 수 있는 사운드 시스템이 함께 제공됩니다. 다른 Unix 시스템에서는흉내내다, 비록 그것이 얼마나 좋은지는 잘 모르겠습니다. 더 많은 설정이 필요하고 ioctl을 통해 직접 제어할 수 있는 것처럼 보이지만 일반적으로 외부 라이브러리(alsa-lib)가 필요합니다. 인터페이스는 순수 어셈블리에서는 보기 흉하지만 C에서는 가능합니다. Linux에서 ALSA는 본질적으로 동적으로 연결되며 dlopen을 사용하여 구성에 따라 런타임에 추가 라이브러리(펄스, 파이프와이어)를 로드합니다. 제가 작업 중인 어셈블리 코드는 32비트인데 대부분의 64비트 컴퓨터에는 32비트 버전의 libalsa, pulse 및 파이프라인와이어가 설치되어 있지 않기 때문에 이것이 문제가 됩니다.
파이프에서 포크/실행된 프로그램으로
시스템 호출을
pipe(2)
사용하여 게임이 sox와 같은 프로그램에 원시 데이터를 보낼 수 있도록 파일 설명자를 만들 수 있습니다.놀다포크에서 실행되는 명령입니다. 이는 이식성 문제를 다른 프로그램에 적용하는 좋은 특성을 가지고 있지만 외부 프로그램에 대한 런타임 종속성을 피하고 싶습니다.
답변1
어셈블리 언어에서 호출할 수 있을 만큼 간단합니다.
UNIX 시스템은 모두 C 호출 규칙을 통해 함수를 호출할 수 있다는 아이디어를 기반으로 합니다. 이것은 매우 기본적인 원칙입니다! C와 UNIX의 역사가 서로 얽혀 있는 이유가 있습니다.
가능한 한 많은 UNIX 시스템에 걸쳐 이식 가능합니다. (특히 {Net,Free,Open}BSD. Solaris, MacOS 및 WSL에는 이 작업을 수행하는 고유한 방법이 있을 것으로 예상됩니다.)
목록에서 제거할 수 있습니다. 어셈블리에 머무르면 특정 ABI를 작성하게 됩니다. 이에 대해 변경할 수 있는 것은 없습니다. C는 어셈블리를 다른 운영 체제로 포팅하는 데 필요한 엄청난 노력으로 인해 개발되었습니다. 당신은 50년 뒤쳐져 있어요!
제가 작업 중인 어셈블리 코드는 32비트인데 대부분의 64비트 컴퓨터에는 32비트 버전의 libalsa, pulse 및 파이프라인와이어가 설치되어 있지 않기 때문에 이것이 문제가 됩니다.
다행히도 여전히 32비트 호환성 계층을 제공하는 대규모 데스크탑 배포판의 경우에는 그렇지 않습니다. (따라서 32비트 라이브러리)
하지만 솔직히 말해서 20년 동안 대부분의 컴퓨터에서 본질적으로 레거시 모델이었던 플랫폼을 지원할 때 뛰어넘어야 하는 장애물입니다. 최소한 32비트 환경이 필요합니다.
순수한 어셈블리 인터페이스는 보기 흉하지만 C 언어로 수행할 수 있습니다.
그렇습니다. x86-Linux ABI C 호출 규칙에 따라 레지스터를 설정하고 C 함수를 호출해야 합니다. (어쨌든 그래픽, 키보드 IO 및 기타 사항에 대해서도 이 작업을 수행해야 할 것으로 생각됩니다.)
그러나 다른 종속성이 추가되더라도 libalsa를 직접 호출하는 것은 권장되지 않지만 portaudio
슬림 라이브러리를 사용하면 수행해야 하는 복잡한 함수 호출 수를 줄일 수 있습니다.
따라서 솔직히 말해서 이러한 종류의 어셈블리를 최신 컴퓨터의 최신 운영 체제로 포팅하는 것은 일반적으로 C 또는 다른 언어로 다소 다시 작성됩니다. 32비트 x86의 한계를 감수할 수 있다면 아마도 가장 현명한 접근 방식은 "이것이 메인 루프입니다", "이것은 게임에서 이미지를 업데이트하기 위한 서브루틴입니다"와 같이 게임의 기능적 핵심 단위를 식별하는 것입니다. 프레임 버퍼", "이것은 키보드 상태를 얻기 위해 호출되는 서브루틴입니다." 외부 구조를 C로 변환하고인라인 어셈블리이 C 뼈대에서. 그런 다음 프로그램이 키보드 컨트롤러의 메모리 주소를 쿼리하는 C에서 직접 입력 라이브러리를 호출할 수 있습니다. 샘플을 사운드 시스템으로 전송해야 하는 경우 어셈블리가 샘플 버퍼를 채우고 주소를 올바른 portaudio 함수에 전달하는 등의 작업을 수행하게 됩니다.
관리형 플랫폼의 어셈블리 학습은 본질적으로 호환되지 않는 어셈블리에서 플랫폼을 호출하는 대신 플랫폼의 패러다임과 호환되는 런타임으로 프로그래밍 언어를 채우는 형태를 취한다는 것을 알 수 있습니다.