audiostream.cpp 5.46 KB
Newer Older
1
/*
2
 *  Copyright (C) 2004-2012 Savoir-Faire Linux Inc.
3 4 5 6 7 8
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
9
 *
10 11 12 13
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
14
 *
15 16 17
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 19 20 21 22 23 24 25 26 27 28
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
29 30
 */

31
#include "audiostream.h"
Yun Liu's avatar
Yun Liu committed
32
#include "pulselayer.h"
33
#include "logger.h"
34
#include <stdexcept>
35

36 37 38 39 40 41
AudioStream::AudioStream(pa_context *c,
                         pa_threaded_mainloop *m,
                         const char *desc,
                         int type,
                         unsigned samplrate,
                         const std::string &deviceName)
42
    : audiostream_(0), mainloop_(m)
Emmanuel Milou's avatar
Emmanuel Milou committed
43
{
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
44
    static const pa_channel_map channel_map = {
45 46
        1,
        { PA_CHANNEL_POSITION_MONO },
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
47 48
    };

49 50
    pa_sample_spec sample_spec = {
        PA_SAMPLE_S16LE, // PA_SAMPLE_FLOAT32LE,
51
        samplrate,
52 53 54 55 56
        1
    };

    assert(pa_sample_spec_valid(&sample_spec));
    assert(pa_channel_map_valid(&channel_map));
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
57

58
    audiostream_ = pa_stream_new(c, desc, &sample_spec, &channel_map);
Emmanuel Milou's avatar
Emmanuel Milou committed
59

60
    if (!audiostream_) {
61 62
        ERROR("%s: pa_stream_new() failed : %s" , desc, pa_strerror(pa_context_errno(c)));
        throw std::runtime_error("Could not create stream\n");
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
63
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
64

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
65
    pa_buffer_attr attributes;
66 67
    attributes.maxlength = pa_usec_to_bytes(160 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.tlength = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
68
    attributes.prebuf = 0;
69
    attributes.fragsize = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
70
    attributes.minreq = (uint32_t) -1;
71

72
    pa_threaded_mainloop_lock(mainloop_);
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
    const pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(PA_STREAM_ADJUST_LATENCY |
                                                                   PA_STREAM_AUTO_TIMING_UPDATE);

    if (type == PLAYBACK_STREAM || type == RINGTONE_STREAM) {
        pa_stream_connect_playback(audiostream_,
                                   deviceName.empty() ? NULL : deviceName.c_str(),
                                   &attributes,
                                   flags,
                                   NULL, NULL);
    } else if (type == CAPTURE_STREAM) {
        pa_stream_connect_record(audiostream_,
                                 deviceName.empty() ? NULL : deviceName.c_str(),
                                 &attributes,
                                 flags);
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
88

89
    pa_threaded_mainloop_unlock(mainloop_);
90

91
    pa_stream_set_state_callback(audiostream_, stream_state_callback, NULL);
92 93
}

Rafaël Carré's avatar
* #6629  
Rafaël Carré committed
94
AudioStream::~AudioStream()
95
{
96
    pa_threaded_mainloop_lock(mainloop_);
Emmanuel Milou's avatar
Emmanuel Milou committed
97

98
    pa_stream_disconnect(audiostream_);
99

100
    // make sure we don't get any further callback
101 102 103 104
    pa_stream_set_state_callback(audiostream_, NULL, NULL);
    pa_stream_set_write_callback(audiostream_, NULL, NULL);
    pa_stream_set_underflow_callback(audiostream_, NULL, NULL);
    pa_stream_set_overflow_callback(audiostream_, NULL, NULL);
105

106
    pa_stream_unref(audiostream_);
Emmanuel Milou's avatar
Emmanuel Milou committed
107

108
    pa_threaded_mainloop_unlock(mainloop_);
109
}
110

111
void
112
AudioStream::stream_state_callback(pa_stream* s, void* /*user_data*/)
113
{
114 115
    char str[PA_SAMPLE_SPEC_SNPRINT_MAX];

116
    switch (pa_stream_get_state(s)) {
117
        case PA_STREAM_CREATING:
118
            DEBUG("Stream is creating...");
119 120 121
            break;

        case PA_STREAM_TERMINATED:
122
            DEBUG("Stream is terminating...");
123 124 125
            break;

        case PA_STREAM_READY:
126 127 128 129 130 131 132
            DEBUG("Stream successfully created, connected to %s", pa_stream_get_device_name(s));
            DEBUG("maxlength %u", pa_stream_get_buffer_attr(s)->maxlength);
            DEBUG("tlength %u", pa_stream_get_buffer_attr(s)->tlength);
            DEBUG("prebuf %u", pa_stream_get_buffer_attr(s)->prebuf);
            DEBUG("minreq %u", pa_stream_get_buffer_attr(s)->minreq);
            DEBUG("fragsize %u", pa_stream_get_buffer_attr(s)->fragsize);
            DEBUG("samplespec %s", pa_sample_spec_snprint(str, sizeof(str), pa_stream_get_sample_spec(s)));
133 134 135
            break;

        case PA_STREAM_UNCONNECTED:
136
            DEBUG("Stream unconnected");
137 138 139 140
            break;

        case PA_STREAM_FAILED:
        default:
141
            ERROR("Sink/Source doesn't exists: %s" , pa_strerror(pa_context_errno(pa_stream_get_context(s))));
142 143
            break;
    }
144 145
}

146
bool AudioStream::isReady()
147
{
148
    if (!audiostream_)
149 150
        return false;

151
    return pa_stream_get_state(audiostream_) == PA_STREAM_READY;
152
}