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