2018年02月13日

LÖVE (Love2D) を Raspbian Stretch で動かす

 ゲームエンジンの 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 をビルドし直して差し替えるしかないね。ついでに、pulseaudiojack が問題を起こすので、非対応にする。

$ 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) プログラミング:インストール

Posted at 2018年02月13日 00:12:50
email.png