19 #ifdef HAVE_PULSEAUDIO_SUPPORT 27 #include <pulse/thread-mainloop.h> 29 #include <QApplication> 32 #include <QLatin1Char> 38 #include <KLocalizedString> 50 #define ELEMENTS_OF(__array__) (sizeof(__array__) / sizeof(__array__[0])) 56 #define TIMEOUT_WAIT_DEVICE_SCAN 10000 62 #define TIMEOUT_CONNECT_TO_SERVER 20000 68 #define TIMEOUT_CONNECT_RECORD 10000 74 #define TIMEOUT_DISCONNECT_STREAM 10000 95 PA_SAMPLE_S16LE, PA_SAMPLE_S16BE,
98 PA_SAMPLE_S24LE, PA_SAMPLE_S24BE,
101 PA_SAMPLE_S24_32LE, PA_SAMPLE_S24_32BE,
104 PA_SAMPLE_S32LE, PA_SAMPLE_S32BE,
107 PA_SAMPLE_FLOAT32LE, PA_SAMPLE_FLOAT32BE,
122 case PA_SAMPLE_FLOAT32LE:
123 case PA_SAMPLE_FLOAT32BE:
140 if (pa_sample_format_is_le(fmt) == 1)
142 if (pa_sample_format_is_be(fmt) == 1)
175 case PA_SAMPLE_S16LE:
176 case PA_SAMPLE_S16BE:
179 case PA_SAMPLE_S24LE:
180 case PA_SAMPLE_S24BE:
181 case PA_SAMPLE_S24_32LE:
182 case PA_SAMPLE_S24_32BE:
185 case PA_SAMPLE_S32LE:
186 case PA_SAMPLE_S32BE:
187 case PA_SAMPLE_FLOAT32LE:
188 case PA_SAMPLE_FLOAT32BE:
201 m_mainloop_thread(this, QVariant()),
208 m_bits_per_sample(0),
209 m_supported_formats(),
210 m_initialized(false),
211 m_pa_proplist(Q_NULLPTR),
212 m_pa_mainloop(Q_NULLPTR),
213 m_pa_context(Q_NULLPTR),
214 m_pa_stream(Q_NULLPTR),
216 m_name(i18n(
"Kwave record")),
238 const pa_sample_spec &sampleSpec =
m_device_list[device].m_sample_spec;
239 const pa_sample_format_t &formatSpec = sampleSpec.format;
242 qDebug(
"--- list of supported formats --- ");
254 qDebug(
"#%2u, %2u bit [%u byte], %s, '%s', '%s'",
266 qDebug(
"--------------------------------- ");
275 Q_ASSERT(record_plugin);
276 if (record_plugin) record_plugin->
notifyRead(p, nbytes);
295 Q_ASSERT(record_plugin);
305 pa_stream_state_t state = pa_stream_get_state(stream);
308 #define DBG_CASE(x) case x: qDebug("RecordPulseAudio -> " #x ); break 321 case PA_STREAM_CREATING:
323 case PA_STREAM_UNCONNECTED:
324 case PA_STREAM_FAILED:
325 case PA_STREAM_TERMINATED:
326 case PA_STREAM_READY:
340 Q_ASSERT(record_plugin);
348 const pa_context_state_t state = pa_context_get_state(c);
351 #define DBG_CASE(x) case x: qDebug("RecordPulseAudio -> " #x ); break 367 case PA_CONTEXT_UNCONNECTED:
368 case PA_CONTEXT_CONNECTING:
369 case PA_CONTEXT_AUTHORIZING:
370 case PA_CONTEXT_SETTING_NAME:
372 case PA_CONTEXT_READY:
373 case PA_CONTEXT_TERMINATED:
374 case PA_CONTEXT_FAILED:
389 if (
bits_of(fmt) != bits)
continue;
399 qWarning(
"RecordPulesAudio::mode2format -> no match found !?");
400 return PA_SAMPLE_INVALID;
408 return (fmt != PA_SAMPLE_INVALID) ?
432 QList<Kwave::SampleFormat::Format> list;
446 if (list.contains(sample_format))
continue;
448 list.append(sample_format);
473 QList<unsigned int> list;
478 const unsigned int bits =
bits_of(fmt);
487 if (list.contains(bits))
continue;
516 QList<Kwave::Compression::Type> list;
524 if (list.contains(compression))
continue;
527 list.append(compression);
542 if (qFuzzyCompare(new_rate,
m_rate))
554 static const unsigned int known_rates[] = {
592 uint32_t rate = sampleSpec.rate;
593 for (
unsigned int i = 0; i <
ELEMENTS_OF(known_rates); i++) {
594 if(known_rates[i] <= rate) {
595 list.append(known_rates[i]);
611 const quint8 max_tracks = std::numeric_limits<quint8>::max();
613 if (tracks > max_tracks) {
631 unsigned int channels = sampleSpec.channels;
634 max = qBound<unsigned int>(min, channels, PA_CHANNELS_MAX);
646 qDebug(
"RecordPulseAudio::close() - waiting for stream disconnect...");
649 qDebug(
"RecordPulseAudio::close() - stream disconnect DONE");
663 if (buffer.isNull() || buffer.isEmpty())
666 unsigned int length = buffer.size();
671 if (err < 0)
return err;
676 size_t freeBytes = length - offset;
677 size_t readableSize = pa_stream_readable_size(
m_pa_stream);
678 if (readableSize > freeBytes) {
679 size_t additional_size = readableSize - freeBytes;
680 buffer.resize(static_cast<int>(length + additional_size));
683 size_t readLength = 0;
684 if (readableSize > 0) {
685 const void *audioBuffer = Q_NULLPTR;
686 pa_stream_peek(
m_pa_stream, &audioBuffer, &readLength);
694 char *data = buffer.data() + offset;
696 MEMCPY(data, audioBuffer, readLength);
698 memset(data, 0x00, readLength);
719 qWarning(
"Connecting to the PulseAudio server failed!");
725 if (fmt == PA_SAMPLE_INVALID) {
728 qWarning(
"format: no matching format for compression '%s', " 729 "%d bits/sample, format '%s'",
737 pa_sample_spec sample_spec;
739 sample_spec.format = fmt;
740 sample_spec.rate =
static_cast<quint32
>(
m_rate);
742 if(!pa_sample_spec_valid(&sample_spec)) {
745 qWarning(
"no valid pulse audio format: %d, rate: %0.3g, channels: %d",
753 pa_channel_map channel_map;
754 pa_channel_map_init_extend(&channel_map, sample_spec.channels,
755 PA_CHANNEL_MAP_DEFAULT);
757 if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
758 qWarning(
"Channel map doesn't match sample specification!");
764 m_name.toUtf8().constData(),
770 qWarning(
"Failed to create a PulseAudio stream %s",
779 attr.fragsize = buffer_size;
780 attr.maxlength =
static_cast<uint32_t
>(-1);
781 attr.minreq =
static_cast<uint32_t
>(-1);
782 attr.prebuf =
static_cast<uint32_t
>(-1);
783 attr.tlength =
static_cast<uint32_t
>(-1);
784 int flags = PA_STREAM_ADJUST_LATENCY;
787 int result = pa_stream_connect_record(
791 static_cast<pa_stream_flags_t
>(flags));
795 if (pa_stream_get_state(
m_pa_stream) != PA_STREAM_READY)
804 qWarning(
"Failed to open a PulseAudio stream for record %s",
823 if (!pa_device.length())
824 return QString::number(ENODEV);
846 if (!list.isEmpty()) list.prepend(
_(
"#TREE#"));
858 qDebug(
"RecordPulseAudio::run_wrapper - done.");
862 static int poll_func(
struct pollfd *ufds,
unsigned long nfds,
863 int timeout,
void *userdata)
875 unsigned long int nfds,
879 int retval = poll(ufds, nfds, timeout);
891 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
897 pa_proplist_sets(
m_pa_proplist, PA_PROP_APPLICATION_LANGUAGE,
900 UTF8(qApp->applicationName()));
901 pa_proplist_sets(
m_pa_proplist, PA_PROP_APPLICATION_ICON_NAME,
903 pa_proplist_sets(
m_pa_proplist, PA_PROP_APPLICATION_PROCESS_BINARY,
905 pa_proplist_setf(
m_pa_proplist, PA_PROP_APPLICATION_PROCESS_ID,
906 "%ld", static_cast<long int>(qApp->applicationPid()));
908 pa_proplist_sets(
m_pa_proplist, PA_PROP_APPLICATION_PROCESS_USER,
909 UTF8(user.loginName()));
911 UTF8(qApp->applicationVersion()));
913 pa_proplist_sets(
m_pa_proplist, PA_PROP_MEDIA_ROLE,
"production");
917 signal(SIGPIPE, SIG_IGN);
935 int error = pa_context_connect(
938 static_cast<pa_context_flags_t>(0),
943 qWarning(
"RecordPulseAudio: pa_contect_connect failed (%s)",
957 if (pa_context_get_state(
m_pa_context) == PA_CONTEXT_READY) {
964 qWarning(
"RecordPulseAudio: context FAILED (%s):-(",
974 QApplication::restoreOverrideCursor();
1016 const pa_source_info *info,
1017 int eol,
void *userdata)
1021 Q_ASSERT(record_plugin);
1027 const pa_source_info *info,
1035 i.
m_name = QString::fromUtf8(info->name);
1037 i.
m_driver = QString::fromUtf8(info->driver);
1057 pa_operation *op_source_info = pa_context_get_source_info_list(
1062 if (op_source_info) {
1064 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1066 QApplication::restoreOverrideCursor();
1070 QMap<QString, source_info_t> list;
1080 if (s == source)
continue;
1088 if (!unique) description +=
_(
" [") + name +
_(
"]");
1092 QFileInfo f(driver);
1093 driver = f.baseName();
1094 driver.replace(
_(
"-"),
_(
" "));
1095 driver.replace(
_(
"_"),
_(
" "));
1096 if (driver.toLower().startsWith(
_(
"module ")))
1097 driver.remove(0, 7);
1098 description.prepend(driver +
_(
"|sound_card||"));
1102 description.append(
_(
"|sound_device"));
1104 description.append(
_(
"|sound_note"));
pa_sample_format_t mode2format(int compression, int bits, Kwave::SampleFormat::Format sample_format)
QList< pa_sample_format_t > m_supported_formats
void notifyRead(pa_stream *stream, size_t nbytes)
static const pa_sample_format_t _known_formats[]
virtual int setSampleFormat(Kwave::SampleFormat::Format new_format) Q_DECL_OVERRIDE
QString description(IDX type, bool localized) const
virtual void run_wrapper(const QVariant ¶ms) Q_DECL_OVERRIDE
void detectSupportedFormats(const QString &device)
pa_sample_spec m_sample_spec
#define ELEMENTS_OF(__array__)
void notifyStreamState(pa_stream *stream)
static Kwave::Compression::Type compression_of(pa_sample_format_t fmt)
virtual int bitsPerSample() Q_DECL_OVERRIDE
pa_proplist * m_pa_proplist
virtual QStringList supportedDevices() Q_DECL_OVERRIDE
virtual Kwave::Compression::Type compression() Q_DECL_OVERRIDE
#define TIMEOUT_DISCONNECT_STREAM
virtual QList< double > detectSampleRates() Q_DECL_OVERRIDE
void disconnectFromServer()
QWaitCondition m_mainloop_signal
#define TIMEOUT_CONNECT_TO_SERVER
pa_mainloop * m_pa_mainloop
virtual int setCompression(Kwave::Compression::Type new_compression) Q_DECL_OVERRIDE
static void pa_source_info_cb(pa_context *c, const pa_source_info *info, int eol, void *userdata)
static Kwave::byte_order_t endian_of(pa_sample_format_t fmt)
Kwave::WorkerThread m_mainloop_thread
unsigned int m_bits_per_sample
static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata)
pa_context * m_pa_context
int initialize(uint32_t buffer_size)
virtual ~RecordPulseAudio() Q_DECL_OVERRIDE
#define TIMEOUT_CONNECT_RECORD
virtual QList< unsigned int > supportedBits() Q_DECL_OVERRIDE
virtual int close() Q_DECL_OVERRIDE
virtual QList< Kwave::SampleFormat::Format > detectSampleFormats() Q_DECL_OVERRIDE
QMap< QString, source_info_t > m_device_list
static Kwave::SampleFormat::Format sample_format_of(pa_sample_format_t fmt)
#define TIMEOUT_WAIT_DEVICE_SCAN
virtual Kwave::byte_order_t endianness() Q_DECL_OVERRIDE
static void pa_read_cb(pa_stream *p, size_t nbytes, void *userdata)
virtual int stop(unsigned int timeout=10000)
void notifySourceInfo(pa_context *c, const pa_source_info *info, int eol)
static void pa_context_notify_cb(pa_context *c, void *userdata)
virtual int detectTracks(unsigned int &min, unsigned int &max) Q_DECL_OVERRIDE
virtual int setSampleRate(double &new_rate) Q_DECL_OVERRIDE
virtual int read(QByteArray &buffer, unsigned int offset) Q_DECL_OVERRIDE
virtual QList< Kwave::Compression::Type > detectCompressions() Q_DECL_OVERRIDE
virtual Kwave::SampleFormat::Format sampleFormat() Q_DECL_OVERRIDE
void notifyContext(pa_context *c)
virtual QString open(const QString &dev) Q_DECL_OVERRIDE
int mainloopPoll(struct pollfd *ufds, unsigned long int nfds, int timeout)
static void pa_stream_state_cb(pa_stream *p, void *userdata)
virtual double sampleRate() Q_DECL_OVERRIDE
static int bits_of(pa_sample_format_t fmt)
Kwave::SampleFormat::Format m_sample_format
IDX findFromData(const DATA &data) const
virtual int setBitsPerSample(unsigned int new_bits) Q_DECL_OVERRIDE
virtual int setTracks(unsigned int &tracks) Q_DECL_OVERRIDE
virtual int tracks() Q_DECL_OVERRIDE
Kwave::Compression::Type m_compression