サウンドフォント関連

レイテンシを修正していました。当方のAndroidManifest.xmlを確認しましたところ、このあたりが関係しそうです。

<attribution android:tag="audioPlayback" android:label="@string/description" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-feature android:name="android.hardware.audio.low_latency" android:required="false" />
<uses-feature android:name="android.software.midi" android:required="false" />

FluidSynthのオプションは、以下です。

共通

Javaで、フレーム数とサンプルレートを取得しています。
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int sampleRate = Integer.parseInt(sampleRateStr);
String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int frame = Integer.parseInt(framesPerBuffer);

int x = frame / 8;
if (x < 64) {
    x = 64;
}
if (x > 256) {
    x = 256;
}
if (sampleRate >= 48000) {
    sampleRate = 48000;
}
//取得できた場合のみ
fluid_settings_setnum(handle->settings, "synth.sample-rate", sampleRate);
//スレッド1つがスレッド同期が不要になっていいかなと
fluid_settings_setint(handle->settings, "synth.threadsafe-api", 0);
//同上
fluid_settings_setint(handle->settings, "synth.cpu-cores", 1);
//これは低レイテンシでもそれ以外でも使っています
fluid_settings_setstr(handle->settings, "audio.oboe.sharing-mode", "Exclusive");
//ロックさせないように配慮しています(効果があるかはわかりません
fluid_settings_setint(handle->settings, "audio.periods", 2);
fluid_settings_setint(handle->settings, "audio.period-size", x);
fluid_settings_setint(handle->settings, "synth.audio-channels", 1);
fluid_settings_setint(handle->settings, "audio.realtime-prio", 90);
fluid_settings_setstr(handle->settings, "player.timing-source", "sample");

通常オーディオ

    fluid_settings_setstr(handle->settings, "audio.oboe.performance-mode", "None");
    fluid_settings_setstr(handle->settings, "audio.oboe.sample-rate-conversion-quality","Fastest");

ローレテンシー(日本語にすると低遅延)です

    fluid_settings_setstr(handle->settings, "audio.oboe.performance-mode", "LowLatency");
    fluid_settings_setstr(handle->settings, "audio.oboe.sample-rate-conversion-quality","Medium");

ローレイテンシの、Gameモードだとよりレイテンシをつめられるそうですが、音質が不安らしいです。デフォルトのlibfluidsynth.soにはそのオプションはございません。

こちらのサイトさま(スクリーンリーダーも途切れずはっきり‼︎話題の「低遅延モード」とは?)が関連しそうな気がします。

Android16あたりで、かなりレイテンシを詰められるのですが、FluidSynthデフォルトでは、フレーム処理(短くなる)が間に合わないことがあるので。アプリから最大同時発音数を変更可能にしています。

    fluid_synth_set_polyphony(handle->synth, polyphony);

以下は、ためしてみたところ、効果があったのですが、アンサンブルの時、音が飛ぶとか、連打したとき、プチプチいうとか、バランスの問題で結局使わないことにしたのが、下記の3行です。

    //fluid_settings_setint(handle->settings, "synth.note-cut", 2);
    //fluid_settings_setnum(handle->settings, "synth.overflow.sustained", 0);
    //fluid_settings_setnum(handle->settings, "synth.overflow.age", -1);

みんな大好きレゾナンス。は、以下の様に収まりました。
参考サイト:https://github.com/FluidSynth/fluidsynth/wiki/FluidFeatures

	private int fit(int y) {
	    if (y < 0) return 0;
	    if (y > 16383) return 16383;
	    return y;
	}
	private int swap(int x) {
	    int h = (x >> 7) & 0x7f;
	    int l = x & 0x7f;
	    return (l << 7) | h;
	}
	private int squared0(int x) {
	    if (x >= 0x70) {
	        x ++;
	    }
	    int y = x * x;
	    return fit(y);
	}
	private int square64(int x) {
	    int pos = x - 64; // -64 to 63
	    if (pos > 40) pos ++;
	    int pos2 = pos < 0 ? - pos : pos;
	    int squared = pos * pos2 * 2; // +-64*64*2 = +-8192
	    int newpos = 8192 + squared;
	    if (newpos > 8000) newpos --;
	    return fit(newpos);
	}
	private int half64(int x) {
	    int pos = x;
	    int pos2 = pos;
	    if (pos == 0) {
	        pos2 = 1;
	    }
	    int newpos = pos2 + 4095 - 128;
	    return fit(newpos);
	}
	/** アプリでメッセージをハンドリングしています。耳で確認する限りいい感じ?です。
	 */
	if (command == MXMidiStatic.COMMAND_CH_CONTROLCHANGE) { //0xb0
	     int x = -1;
	     int y = data2;
	     if (data1 == MXMidiStatic.DATA1_CC_SOUND_RESONANCE) { //71
	          x = 22;
	          y = squared0(data2);
	      }
	      if (data1 == MXMidiStatic.DATA1_CC_SOUND_BLIGHTNESS) { //74
	          x = 21;
	          y = square64(data2);
	      }
	      if (data1 == MXMidiStatic.DATA1_CC_SOUND_ATTACKTIME) { //73
	          x = 11;
	          y = half64(data2);
	      }
	      if (data1 == MXMidiStatic.DATA1_CC_SOUND_RELEASETIME) { //72
	           x = 15;
	           y = half64(data2);
	      }
	      if (x >= 0) {
	          int channel = status & 0x0f;
	          int cc = MXMidiStatic.COMMAND_CH_CONTROLCHANGE + channel;
	          sendShortMessage(cc, MXMidiStatic.DATA1_CC_NRPN_MSB, 127);
	          sendShortMessage(cc, MXMidiStatic.DATA1_CC_NRPN_LSB, x);
	          sendShortMessage(cc, MXMidiStatic.DATA1_CC_DATAENTRY, y & 0x7f);
	          sendShortMessage(cc, MXMidiStatic.DATA1_CC_DATAENTRY2, (y>>7) & 0x7f);
	          return true;    
	      }
	      sendOneMessage(one);
	      return true;
	  }
広告を表示しています。

2025思い出せる範囲トップ