ゲームエンジンの LÖVE (Love2D) なんですが、最新の Raspbian Stretch でうまく動かない問題があった。音を鳴らそうとするとトラブルが起きる。
- Raspberry Pi1 では、
Illegal Instruction
で落ちてしまう(たぶん Zero でも)。 - Raspberry Pi3 では、落ちはしないが、発音がひどく遅れる(たぶん Pi2 でも)。
Illegal Instruction
はダメでしょ。Gdb で動作させて調べてみたところ、libopenal.so
の中で落ちていることがわかった。OpenAL が壊れているらしい。試しに、こんなコードで検証してみた(openal.c
とする)。
#include <stdlib.h>
#include <stdio.h>
#include <AL/alc.h>
#include <AL/al.h>
int ReadHeaderWav(FILE* fp, int *channel, int* bit, int *size, int* freq) {
int16_t res16;
int32_t res32, dataSize, chunkSize;
int16_t channelCnt, bitParSample, blockSize;
int32_t samplingRate,byteParSec;
int dataPos, i, flag = 0;
fread(&res32, 4, 1, fp);
if (res32 != 0x46464952) { /* "RIFF" */
return 1; //error 1
}
fread(&dataSize, 4, 1, fp); /* Data size */
/* WAVE header */
fread(&res32, 4, 1, fp);
if (res32 != 0x45564157){ /* "WAVE" */
return 2; //error 2
}
/* Chunks */
while (flag != 3) {
fread(&res32, 4, 1, fp);
fread(&chunkSize, 4, 1, fp);
switch (res32) {
case 0x20746d66: /* "fmt " */
fread(&res16, 2, 1, fp);
if (res16 != 1) {
return 4; /* Not supported */
}
fread(&channelCnt, 2, 1, fp);
if (res16 > 2) {
return 5; /* Not stereo nor monoral */
}
fread(&samplingRate, 4, 1, fp);
fread(&byteParSec, 4, 1, fp);
fread(&blockSize, 2, 1, fp);
fread(&bitParSample, 2, 1, fp);
*channel = (int)channelCnt;
*bit = (int)bitParSample;
*freq = samplingRate;
flag |= 1;
break;
case 0x61746164: /* "data" */
*size = chunkSize;
dataPos = ftell(fp);
flag |= 2;
break;
}
}
fseek(fp, dataPos, SEEK_SET);
return 0;
}
int main (int argc, char **argv)
{
ALuint buffer, source;
FILE *fp;
int wavChannel, wavBit, wavSize, wavFreq;
unsigned char *data;
int i, fmt;
ALCdevice *device;
ALCcontext *context;
/* Initialize */
device = alcOpenDevice(NULL);
context = alcCreateContext(device, NULL);
alcMakeContextCurrent(context);
fp = fopen("bgm.wav","rb");
if (ReadHeaderWav(fp, &wavChannel, &wavBit, &wavSize, &wavFreq)){
printf("Format not supported\n");
return 1;
}
data = (unsigned char *)malloc(wavSize);
fread(data, wavSize, 1, fp);
fclose(fp);
alGenBuffers(1, &buffer);
if (wavChannel == 1)
fmt = (wavBit == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16);
else
fmt = (wavBit == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16);
alBufferData(buffer, fmt, data, wavSize, wavFreq);
alGenSources(1, &source);
alSourcei(source, AL_BUFFER, buffer);
for (i = 0; i < 5; i++) {
fprintf(stderr, "play\n");
alSourcePlay(source);
sleep(1);
alSourceStop(source);
}
alDeleteBuffers(1, &buffer);
alDeleteSources(1, &source);
free(data);
return 0;
}
gcc -g -o altest altest.c -lopenal
でビルドして、gdb で実行してみた。Pi3 では "play" と表示されてから約 0.6 秒遅れで音が鳴る。Pi1 では、"play" が1回表示された直後に下のエラーで落ちる。
Thread 4 "altest" received signal SIGILL, Illegal instruction.
[Switching to Thread 0xa59ff460 (LWP 593)]
0xb6f3ead4 in ?? () from /usr/lib/arm-linux-gnueabihf/libopenal.so.1
これは libopenal.so
をビルドし直して差し替えるしかないね。ついでに、pulseaudio
と jack
が問題を起こすので、非対応にする。
$ wget http://kcat.strangesoft.net/openal-releases/openal-soft-1.18.2.tar.bz2
$ tar xvjf openal-soft-1.18.2.tar.bz2
$ cd openal-soft-1.18.2/build
$ cmake -DALSOFT_BACKEND_JACK=OFF -DALSOFT_BACKEND_PULSEAUDIO=OFF -DCMAKE_INSTALL_PREFIX=/usr/local/games/love ..
$ sudo mkdir -p /usr/local/games/love; sudo chown pi /usr/local/games/love
$ make && make install
さっきの altest
を試してみる。ちゃんと音が出るようになった。新しく作った libopenal.so
をリンクするため、LD_LIBRARY_PATH
環境変数を設定する。
$ LD_LIBRARY_PATH=/usr/local/games/love/lib ./altest
これに対応した Love2D のビルドスクリプトを作りました。→「LÖVE (Love2D) プログラミング:インストール」