summaryrefslogtreecommitdiffstats
path: root/src/multimedia/pulseaudio/qpulseaudiosink_p.h
blob: c73c7ee8f33a6ed185b8824e8c61f6ddfbcd1c68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QAUDIOOUTPUTPULSE_H
#define QAUDIOOUTPUTPULSE_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <QtCore/qfile.h>
#include <QtCore/qtimer.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qiodevice.h>

#include "qaudio.h"
#include "qaudiodevice.h"
#include <private/qaudiosystem_p.h>

#include <pulse/pulseaudio.h>

QT_BEGIN_NAMESPACE

class QPulseAudioSink : public QPlatformAudioSink
{
    friend class PulseOutputPrivate;
    Q_OBJECT

public:
    QPulseAudioSink(const QByteArray &device);
    ~QPulseAudioSink();

    void start(QIODevice *device) override;
    QIODevice *start() override;
    void stop() override;
    void reset() override;
    void suspend() override;
    void resume() override;
    qsizetype bytesFree() const override;
    void setBufferSize(qsizetype value) override;
    qsizetype bufferSize() const override;
    qint64 processedUSecs() const override;
    QAudio::Error error() const override;
    QAudio::State state() const override;
    void setFormat(const QAudioFormat &format) override;
    QAudioFormat format() const override;

    void setVolume(qreal volume) override;
    qreal volume() const override;

    void streamUnderflowCallback();
    void streamDrainedCallback();

protected:
    void timerEvent(QTimerEvent *event) override;

private:
    void setState(QAudio::State state);
    void setError(QAudio::Error error);
    void startReading();

    bool open();
    void close();
    qint64 write(const char *data, qint64 len);

private Q_SLOTS:
    void userFeed();
    void onPulseContextFailed();

private:
    pa_sample_spec m_spec = {};
    // calculate timing manually, as pulseaudio doesn't give us good enough data
    mutable timeval lastTimingInfo = {};

    mutable QList<qint64> latencyList; // last latency values

    QByteArray m_device;
    QByteArray m_streamName;
    QAudioFormat m_format;
    QBasicTimer m_tickTimer;

    QIODevice *m_audioSource = nullptr;
    pa_stream *m_stream = nullptr;
    char *m_audioBuffer = nullptr;

    qint64 m_totalTimeValue = 0;
    qint64 m_elapsedTimeOffset = 0;
    mutable qint64 averageLatency = 0; // average latency
    mutable qint64 lastProcessedUSecs = 0;
    qreal m_volume = 1.0;

    QAudio::Error m_errorState = QAudio::NoError;
    QAudio::State m_deviceState = QAudio::StoppedState;
    int m_periodSize = 0;
    int m_bufferSize = 0;
    int m_maxBufferSize = 0;
    int m_periodTime = 0;
    bool m_pullMode = true;
    bool m_opened = false;
    bool m_resuming = false;
};

class PulseOutputPrivate : public QIODevice
{
    friend class QPulseAudioSink;
    Q_OBJECT

public:
    PulseOutputPrivate(QPulseAudioSink *audio);
    virtual ~PulseOutputPrivate() {}

protected:
    qint64 readData(char *data, qint64 len) override;
    qint64 writeData(const char *data, qint64 len) override;

private:
    QPulseAudioSink *m_audioDevice;
};

QT_END_NAMESPACE

#endif