QtMultimedia にパッチを送ってみました 2

# はじめに

これは [Qt Advent Calendar 2024](https://qiita.com/advent-calendar/2024/qt) 14日目の記事です。

昨日書いた [QtMultimedia にパッチを送ってみました](https://qiita.com/task_jp/items/574ce63e7a5dbb3b4d16) と前提の部分が共通なので、まずはそちらを読んでいただければと思います。


# マイクから音声データを読み込む処理のコードを読んでみよう

https://codereview.qt-project.org/gitweb?p=qt/qtmultimedia.git;a=blob;f=src/multimedia/pulseaudio/qpulseaudiosource.cpp;h=87bba2427fd865761d23aedc106f5bf128bc8a28;hb=66812f93da86ed395bda30101ac15fda4fa501dd#l332

```cpp:qtmultimedia/src/multimedia/pulseaudio/qpulseaudiosource.cpp
qint64 QPulseAudioSource::read(char *data, qint64 len)
{
    using namespace QPulseAudioInternal;

    Q_ASSERT(data != nullptr || len == 0);

    m_stateMachine.updateActiveOrIdle(true, QAudio::NoError);
    int readBytes = 0;

    if (!m_pullMode && !m_tempBuffer.isEmpty()) {
        readBytes = qMin(static_cast<int>(len), m_tempBuffer.size());
        if (readBytes)
            memcpy(data, m_tempBuffer.constData(), readBytes);
        m_totalTimeValue += readBytes;

        if (readBytes < m_tempBuffer.size()) {
            m_tempBuffer.remove(0, readBytes);
            return readBytes;
        }

        m_tempBuffer.clear();
    }

    while (pa_stream_readable_size(m_stream) > 0) {
        ...
    }
    ...
}
```

ここの処理はざっと以下のような感じになっています。

- `Pull Mode` ではなかったら `m_tempBuffer` に保持しているものを先に処理する
- while 文内で、PulseAudio からデータを取得する


# bytesAvailable() の処理を読んでみよう

https://codereview.qt-project.org/gitweb?p=qt/qtmultimedia.git;a=blob;f=src/multimedia/pulseaudio/qpulseaudiosource.cpp;h=87bba2427fd865761d23aedc106f5bf128bc8a28;hb=66812f93da86ed395bda30101ac15fda4fa501dd#l314

```cpp:qtmultimedia/src/multimedia/pulseaudio/qpulseaudiosource.cpp
qsizetype QPulseAudioSource::bytesReady() const
{
    using namespace QPulseAudioInternal;

    if (!m_stateMachine.isActiveOrIdle())
        return 0;

    std::lock_guard lock(*QPulseAudioEngine::instance());

    int bytes = pa_stream_readable_size(m_stream);
    if (bytes < 0) {
        qWarning() << "pa_stream_readable_size() failed:" << currentError(m_stream);
        return 0;
    }

    return static_cast<qsizetype>(bytes);
}
```

基本的には前述の `read` と同じ構成であるべきなのですが、

- `Pull Mode` ではなかったら `m_tempBuffer` に保持しているものを先に処理する

の部分が見当たりません。

# 修正してみました

```diff
diff --git a/src/multimedia/pulseaudio/qpulseaudiosource.cpp b/src/multimedia/pulseaudio/qpulseaudiosource.cpp
index 87bba24..5a75ac2 100644
--- a/src/multimedia/pulseaudio/qpulseaudiosource.cpp
+++ b/src/multimedia/pulseaudio/qpulseaudiosource.cpp
@@ -320,13 +320,17 @@
 
     std::lock_guard lock(*QPulseAudioEngine::instance());
 
+    qsizetype tempBufferSize = 0;
+    if (!m_pullMode && !m_tempBuffer.isEmpty()) {
+        tempBufferSize = m_tempBuffer.size();
+    }
     int bytes = pa_stream_readable_size(m_stream);
     if (bytes < 0) {
         qWarning() << "pa_stream_readable_size() failed:" << currentError(m_stream);
-        return 0;
+        return tempBufferSize;
     }
 
-    return static_cast<qsizetype>(bytes);
+    return static_cast<qsizetype>(bytes) + tempBufferSize;
 }
 
 qint64 QPulseAudioSource::read(char *data, qint64 len)
```

`read` に合わせて、`Push Mode` の際には m_tempBuffer を考慮するようにしました。

[PulseAudio: fix bytesAavailable of QAudioSource in push mode](https://codereview.qt-project.org/c/qt/qtmultimedia/+/611209/) ~~にて Qt 本体へ取り込んでもらうためのレビューを今行っています。~~ にて無事取り込まれました。(2024/12/19)

QAudioSource クラスは単体テストも存在しないので（実デバイスとの兼ね合いで難しい）、上記の変更そのままです。

# おわりに

QAudioSource を使って遊んでいたら見つけたバグの修正を昨日して、今日はその周辺で見つけたバグの修正をしました。
Qt 自体のビルドも最近は簡単なので、Qt のバグを見つけたら直して、本体に取り込んでもらえるようにしてみましょう。

明日は @hermit4 さんが、なにか書いてくれるようです。お楽しみに！