Switch to unified view

a/src/alsadirect.cpp b/src/alsadirect.cpp
...
...
32
32
33
#ifndef MIN
33
#ifndef MIN
34
#define MIN(A, B) ((A) < (B) ? (A) : (B))
34
#define MIN(A, B) ((A) < (B) ? (A) : (B))
35
#endif
35
#endif
36
36
37
static const snd_pcm_uframes_t periodsize = 32768; /* Periodsize (bytes) */
38
37
// The queue for audio blocks ready for alsa
39
// The queue for audio blocks ready for alsa
38
static const unsigned int qs = 200;
40
static const unsigned int qs = 200;
39
static const unsigned int qt = qs/2;
41
static const unsigned int qt = qs/2;
40
// the 40 value should be computed from the alsa buffer size. It's
42
// the 40 value should be computed from the alsa buffer size. It's
41
// there becausee we have a jump on the first alsa write (alsa buffer
43
// there becausee we have a jump on the first alsa write (alsa buffer
42
// is empty).
44
// is empty).
43
static const unsigned int qit = qs/2 + 40;
45
static const unsigned int qit = qs/2 + periodsize/1024;
44
46
45
static WorkQueue<AudioMessage*> alsaqueue("alsaqueue", qs);
47
static WorkQueue<AudioMessage*> alsaqueue("alsaqueue", qs);
46
static snd_pcm_t *pcm;
48
static snd_pcm_t *pcm;
49
static bool qinit = false;
47
50
48
static void *alsawriter(void *p)
51
static void *alsawriter(void *p)
49
{
52
{
50
    if (!alsaqueue.waitminsz(qit)) {
51
        LOGERR("alsawriter: waitminsz failed\n");
52
        return (void *)1;
53
    }
54
    while (true) {
53
    while (true) {
54
        if (!qinit) {
55
            if (!alsaqueue.waitminsz(qit)) {
56
                LOGERR("alsawriter: waitminsz failed\n");
57
                return (void *)1;
58
            }
59
        }
55
        AudioMessage *tsk = 0;
60
        AudioMessage *tsk = 0;
56
        size_t qsz;
61
        size_t qsz;
57
        if (!alsaqueue.take(&tsk, &qsz)) {
62
        if (!alsaqueue.take(&tsk, &qsz)) {
58
            // TBD: reset alsa?
63
            // TBD: reset alsa?
59
            alsaqueue.workerExit();
64
            alsaqueue.workerExit();
60
            return (void*)1;
65
            return (void*)1;
61
        }
66
        }
62
63
        // Bufs 
67
        // Bufs 
64
        snd_pcm_uframes_t frames = 
68
        snd_pcm_uframes_t frames = 
65
            tsk->m_bytes / (tsk->m_chans * (tsk->m_bits/8));
69
            tsk->m_bytes / (tsk->m_chans * (tsk->m_bits/8));
66
        snd_pcm_sframes_t ret =  snd_pcm_writei(pcm, tsk->m_buf, frames);
70
        snd_pcm_sframes_t ret =  snd_pcm_writei(pcm, tsk->m_buf, frames);
67
        if (ret != int(frames)) {
71
        if (ret != int(frames)) {
68
            LOGERR("snd-cm_writei(" << frames <<" frames) failed: ret: " <<
72
            LOGERR("snd-cm_writei(" << frames <<" frames) failed: ret: " <<
69
                   ret << endl);
73
                   ret << endl);
70
            if (ret < 0)
74
            if (ret < 0) {
75
                qinit = false;
71
                snd_pcm_prepare(pcm);
76
                snd_pcm_prepare(pcm);
72
//            return (void *)1;
77
            }
78
        } else {
79
            qinit = true;
73
        }
80
        }
74
    }
81
    }
75
}
82
}
76
83
77
static bool alsa_init(AudioMessage *tsk)
84
static bool alsa_init(const string& dev, AudioMessage *tsk)
78
{
85
{
79
    snd_pcm_hw_params_t *hw_params;
86
    snd_pcm_hw_params_t *hw_params;
80
    int err;
87
    int err;
81
//    static const string dev("plughw:CARD=PCH,DEV=0");
82
    static const string dev("hw:2,0");
83
    const char *cmd = "";
88
    const char *cmd = "";
84
    unsigned int actual_rate = tsk->m_freq;
89
    unsigned int actual_rate = tsk->m_freq;
85
    int dir=0;
90
    int dir=0;
91
    int periods = 2;       /* Number of periods */
86
92
87
    if ((err = snd_pcm_open(&pcm, dev.c_str(), 
93
    if ((err = snd_pcm_open(&pcm, dev.c_str(), 
88
                            SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
94
                            SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
89
        LOGERR("alsa_init: snd_pcm_open " << dev << " " << 
95
        LOGERR("alsa_init: snd_pcm_open " << dev << " " << 
90
               snd_strerror(err) << endl);
96
               snd_strerror(err) << endl);
...
...
125
    cmd = "snd_pcm_hw_params_set_channels";
131
    cmd = "snd_pcm_hw_params_set_channels";
126
    if ((err = snd_pcm_hw_params_set_channels(pcm, hw_params, 
132
    if ((err = snd_pcm_hw_params_set_channels(pcm, hw_params, 
127
                                              tsk->m_chans)) < 0) {
133
                                              tsk->m_chans)) < 0) {
128
        goto error;
134
        goto error;
129
    }
135
    }
130
136
    /* Set number of periods. Periods used to be called fragments. */ 
137
    cmd = "snd_pcm_hw_params_set_periods";
138
    if (snd_pcm_hw_params_set_periods(pcm, hw_params, periods, 0) < 0) {
139
        goto error;
140
    }
141
  
142
    /* Set buffer size (in frames). The resulting latency is given by */
143
    /* latency = periodsize * periods / (rate * bytes_per_frame)     */
144
    cmd = "snd_pcm_hw_params_set_buffer_size";
145
    if (snd_pcm_hw_params_set_buffer_size(pcm, hw_params, 
146
                                          (periodsize * periods)>>2) < 0) {
147
        goto error;
148
    }
149
  
131
    cmd = "snd_pcm_hw_params";
150
    cmd = "snd_pcm_hw_params";
132
    if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0) {
151
    if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0) {
133
        goto error;
152
        goto error;
134
    }
153
    }
135
    
154
    
...
...
147
    AudioEater::Context *ctxt = (AudioEater::Context*)cls;
166
    AudioEater::Context *ctxt = (AudioEater::Context*)cls;
148
167
149
    LOGDEB("alsaEater: queue " << ctxt->queue << endl);
168
    LOGDEB("alsaEater: queue " << ctxt->queue << endl);
150
169
151
    WorkQueue<AudioMessage*> *queue = ctxt->queue;
170
    WorkQueue<AudioMessage*> *queue = ctxt->queue;
171
    string alsadevice = ctxt->alsadevice;
172
152
    delete ctxt;
173
    delete ctxt;
153
174
154
    bool qinit = false;
175
    qinit = false;
155
    int src_error = 0;
176
    int src_error = 0;
156
    SRC_STATE *src_state = 0;
177
    SRC_STATE *src_state = 0;
157
    SRC_DATA src_data;
178
    SRC_DATA src_data;
158
    memset(&src_data, 0, sizeof(src_data));
179
    memset(&src_data, 0, sizeof(src_data));
159
    alsaqueue.start(1, alsawriter, 0);
180
    alsaqueue.start(1, alsawriter, 0);
...
...
167
            queue->workerExit();
188
            queue->workerExit();
168
            return (void*)1;
189
            return (void*)1;
169
        }
190
        }
170
191
171
        if (src_state == 0) {
192
        if (src_state == 0) {
172
            if (!alsa_init(tsk)) {
193
            if (!alsa_init(alsadevice, tsk)) {
173
                queue->workerExit();
194
                queue->workerExit();
174
                return (void *)1;
195
                return (void *)1;
175
            }
196
            }
176
            // BEST_QUALITY yields approx 25% cpu on a core i7
197
            // BEST_QUALITY yields approx 25% cpu on a core i7
177
            // 4770T. Obviously too much, actually might not be
198
            // 4770T. Obviously too much, actually might not be
178
            // sustainable.
199
            // sustainable.
179
            // MEDIUM_QUALITY is around 10%
200
            // MEDIUM_QUALITY is around 10%
180
            // FASTEST is 4-5%. Given that this is process-wide, probably
201
            // FASTEST is 4-5%. Given that this is process-wide, probably
181
            // a couple % in fact.
202
            // a couple % in fact.
182
            // To be re-evaluated on the pi...
203
            // To be re-evaluated on the pi... FASTEST is 30% CPU on a Pi 2
204
            // with USB audio. Curiously it's 25-30% on a Pi1 with i2s audio.
183
            src_state = src_new(SRC_SINC_FASTEST, tsk->m_chans, &src_error);
205
            src_state = src_new(SRC_SINC_FASTEST, tsk->m_chans, &src_error);
184
        }
206
        }
185
207
186
        if (qinit) {
208
        if (qinit) {
187
            float qs = alsaqueue.qsize();
209
            float qs = alsaqueue.qsize();
...
...
190
            if (alsaqueue.qsize() < qt) {
212
            if (alsaqueue.qsize() < qt) {
191
                samplerate_ratio =  1.0 + adj;
213
                samplerate_ratio =  1.0 + adj;
192
                if (samplerate_ratio > 1.1)
214
                if (samplerate_ratio > 1.1)
193
                    samplerate_ratio = 1.1;
215
                    samplerate_ratio = 1.1;
194
            } else {
216
            } else {
195
                samplerate_ratio -= adj;
217
                samplerate_ratio = 1.0 - adj;
196
                if (samplerate_ratio < 0.9) 
218
                if (samplerate_ratio < 0.9) 
197
                    samplerate_ratio = 0.9;
219
                    samplerate_ratio = 0.9;
198
            }
220
            }
221
        } else {
222
            samplerate_ratio = 1.0;
199
        }
223
        }
200
224
201
        unsigned int tot_samples = tsk->m_bytes / (tsk->m_bits/8);
225
        unsigned int tot_samples = tsk->m_bytes / (tsk->m_bits/8);
202
        if ((unsigned int)src_data.input_frames < tot_samples / tsk->m_chans) {
226
        if ((unsigned int)src_data.input_frames < tot_samples / tsk->m_chans) {
203
            int bytes = tot_samples * sizeof(float);
227
            int bytes = tot_samples * sizeof(float);
...
...
273
297
274
        if (!alsaqueue.put(tsk)) {
298
        if (!alsaqueue.put(tsk)) {
275
            LOGERR("alsaEater: queue put failed\n");
299
            LOGERR("alsaEater: queue put failed\n");
276
            return (void *)1;
300
            return (void *)1;
277
        }
301
        }
278
        if (alsaqueue.qsize() >= qit)
279
            qinit = true;
280
    }
302
    }
281
}
303
}
282
304
283
AudioEater alsaAudioEater(AudioEater::BO_HOST, &audioEater);
305
AudioEater alsaAudioEater(AudioEater::BO_HOST, &audioEater);