kitao / pyxel

A retro game engine for Python

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

thread '<unnamed>' panicked at pyxel-engine/src/channel.rs:125:37: index out of bounds: the len is 1 but the index is 1

shiromofufactory opened this issue · comments

日本語で失礼します。

Pyxelでサウンドを鳴らしているとたまに標記のエラーが発生することがあります。
RustのソースはわからないのでChatGPTにソースとともに示したところ、以下のような回答をもらいましたので共有させていただきます。

レアな状況でのエラー(使い方の問題)かもしれませんので、必要なければクローズしてください。

==========================

このエラーメッセージは、配列のインデックスが範囲外であることを示しています。具体的には、配列の長さが1であるにもかかわらず、インデックス1にアクセスしようとしているためにパニックが発生しています。

エラーが発生している行は次の部分です:

let note = Self::circular_note(&sound.notes, self.note_index);

このエラーは、おそらく sound.notes 配列が1つの要素しか持っていない場合に発生します。この場合、 self.note_index が1以上になっていると、範囲外のインデックスにアクセスしようとします。

以下の修正ポイントを確認してみてください:

circular_note 関数の確認:
circular_note 関数が正しく動作しているか確認してください。この関数は、渡されたインデックスが配列の長さに収まるようにするために設計されていますが、ここでエラーが発生する可能性があります。

self.note_index の初期化と更新の確認:
self.note_index が配列の範囲内にあることを確認してください。例えば、self.note_index が配列の長さよりも大きくならないようにする必要があります。

以下のように、 circular_note 関数に範囲チェックを追加することで、問題を緩和できます:

const fn circular_note(notes: &[Note], index: u32) -> Note {
    let len = notes.len();
    if len > 0 {
        notes[(index % len as u32) as usize]
    } else {
        0
    }
}

また、update 関数の中で self.note_index を更新する際に、正しい範囲内であることを確認するためのチェックを追加することも考慮してください:

if self.note_index >= sound.notes.len() as u32 {
    self.note_index = 0; // または適切な処理
}

このような範囲チェックを追加することで、配列の範囲外アクセスによるパニックを回避できます。

==========================

ChatGPTの指摘にも

このエラーは、おそらく sound.notes 配列が1つの要素しか持っていない場合に発生します。

とあったのですが、毎回ではないもののたしかにnotesが1文字のSound("r"だけなど)を再生した後にこの問題が生じるような気がします。

"r"だけの1文字のSoundを再生していたのは、チャンネルを1つ無音にするためで、あまり多くの方がやるようなことではないとは思っています。
"rr"だったら出なくなるのか、などはまだ調べきれていません。

「通常のPythonコード実行時」で、「同じ効果音を再生している時、音を切り替えた時」どちらもです。
コードは全体が非常に大きいのでご提示が難しいのですが、

  • px.play(3, 7) のような普通の命令(効果音7は非常に短い)をAボタンを押すごとに鳴らすようにしている
  • Aボタンはpx.btnp(btn_a, 8, 2)のような設定で、連打や長押しで短い間隔で押せるようにしている

といった状況でAボタンを短い時間に連打するとほぼ確実に再現します。

どうやら前の音が鳴っているときに次の音が鳴ると発生するように思われることと、「"r"だけの1文字のSound」と書きましたが、1文字でなくても発生することは突き止めました。

ありがとうございます。
私の手元ではまだ再現はできていませんが、だいぶ状況絞り込めましたので調査がしやすくなりました。

2点お願いがあるのですが、
・Rustのエラー表示の情報をいただけないでしょうか
・Pyxel Editorのサウンド編集画面で、キーボードのz,c,x,vなどを連打すると同じ状況になるのですが、こちらでも同様のエラーは出ますでしょうか

ちなみにChatGPTの修正案で出てきている「範囲チェック付きのコード」というのは元のPyxelコードと同じ処理内容になりますのでハルシネーションの類が起きているようです。

すみません。もう一点

Pyxelのexample09はスペースキーを押すとショット音を再生するようになっているのですが、こちらでも同様の現象は出るか見ていただけないでしょうか。

Rustのエラー表示

thread '<unnamed>' panicked at pyxel-engine/src/channel.rs:125:37:
index out of bounds: the len is 1 but the index is 1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread '<unnamed>' panicked at library/core/src/panicking.rs:221:5:
panic in a function that cannot unwind
stack backtrace:
   0:        0x103b9f344 - <std::sys::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hbec6b2503d33dd16
   1:        0x103bb91cc - core::fmt::write::h9ce5eb0fe2948540
   2:        0x103b9d26c - std::io::Write::write_fmt::h8ccd43801f1280a2
   3:        0x103b9f19c - std::sys::backtrace::print::he23dc1960a4ca912
   4:        0x103ba0378 - std::panicking::default_hook::{{closure}}::hfa0ec7b680369458
   5:        0x103ba0044 - std::panicking::default_hook::h960a471eb1980fa8
   6:        0x103ba0d1c - std::panicking::rust_panic_with_hook::hec4137e7af2902cf
   7:        0x103ba0694 - std::panicking::begin_panic_handler::{{closure}}::h5b3e11fb511c836c
   8:        0x103b9f7d0 - std::sys::backtrace::__rust_end_short_backtrace::hcaa1c705a0087eac
   9:        0x103ba03c8 - _rust_begin_unwind
  10:        0x103bd3840 - core::panicking::panic_nounwind_fmt::hbed83ffd0fd0517f
  11:        0x103bd38b8 - core::panicking::panic_nounwind::h485169fdc552e145
  12:        0x103bd39a4 - core::panicking::panic_cannot_unwind::h38b163fe92a80ed5
  13:        0x10395b774 - pyxel_platform::audio::c_audio_callback::h19b50904939ec57a
  14:        0x103a19b64 - _outputCallback
  15:        0x1939d6ee8 - <unknown>
  16:        0x19395a8e4 - <unknown>
  17:        0x1939d6628 - <unknown>
  18:        0x1939d6434 - <unknown>
  19:        0x183b27a4c - <unknown>
  20:        0x183b279e0 - <unknown>
  21:        0x183b27750 - <unknown>
  22:        0x183b26340 - <unknown>
  23:        0x183b259ac - <unknown>
  24:        0x103a196b8 - _audioqueue_thread
  25:        0x1039c07d0 - _SDL_RunThread
  26:        0x103a43608 - _RunThread
  27:        0x183a4a034 - __pthread_joiner_wake
thread caused non-unwinding panic. aborting.
zsh: abort      pyxel run main

・Pyxel Editorのサウンド編集画面で、キーボードのz,c,x,vなどを連打すると同じ状況になるのですが、こちらでも同様のエラーは出ますでしょうか

Pyxelのexample09はスペースキーを押すとショット音を再生するようになっているのですが、こちらでも同様の現象は出るか見ていただけないでしょうか。

両方試しましたが、この現象は発生しませんでした。
example09と私のプログラムの違いが自分でもわからずにいます。

ありがとうございます。
example09やPyxel Editorでは再現されなかったという情報もそれはそれで重要なヒントですね。

現状の検討状況は以下になります。

・今回のエラーはPyxelのスレッドとは別のサウンド用のスレッドで起きているエラーなので、スレッドセーフが保証できていない箇所があった場合は一見して理由がわからないエラーが色々起きる可能性がある(エラー箇所のコードだけを見ても理由がわからない可能性がある)

・エラーが起きている箇所に注目すると、そのチャンネルで再生予約されているサウンドリストの範囲外を再生しようとしており、最初のサウンドが終了するピンポイントのタイミングで、メインスレッド側で次の音をplayすることでそういった現象が引き起こせるかもしれない。その場合、短い音だと発生確率が上がる理由は説明できる。(チャンネルへのアクセスはMutexで排他制御しており、そういったことは起こせないようにしているつもりではある)

・以前Pyxel Editorで発生したエラーもplay_posが想定外のサウンド再生位置を返すことによるものだったので、同じ原因だったかもしれない。その場合はPyxel Editorでもサウンド編集画面で何らかの操作をすることで発生する可能性がある。ただその後再現ができていないので、発生確率を上げる別の要因があると考えられる

・サウンドリストの範囲外再生への対処というだけであれば、防ぐコードを入れることは簡単だが、他の不具合も隠れている可能性があるので、再現コードを確保して、原因究明をした上で対処方法を確定させたい

あくまで推測ではあるのですが、グラフィックスなどのサウンド以外の処理は関係しておらず、エラーのトリガーとなっている特定チャンネルでの短い音の連続再生を行っている時に、サウンド周りで他にどんな操作を行っているか(他のチャンネルでも同様の連続再生をしている、頻繁にplayとstopを呼んでいる、再生しながらサウンドの書き換えをしている等々)が再現のポイントなのではないかと考えています。

@shiromofufactory
原因がわかって解決したという話ではないのですが、Pyxel 2.1.5で他の機能追加と併せて、サウンドの排他制御の単位が細かくなりすぎていたのを若干修正しました。その変更によって本件の状況ももしかすると変わるかもしれないので、お時間ある時にでも2.1.5で動作が変わるかどうかご確認いただけますとありがたいです。

@kitao
2.1.5で試したところ、2.1.0のときにはほぼ確実に再現していた操作を繰り返してもこの現象が発生しないことが確認できました。
この件はCloseいただいても大丈夫です。ありがとうございました。

では、完全に原因解決というところには至っていませんが、おおよそ調査範囲が絞れたことと、症状出なくなったということで、一旦本件クローズにしたいと思います