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) */
37
static snd_pcm_uframes_t periodsize = 16384; /* Periodsize (bytes) */
38
static unsigned int periods = 2;       /* Number of periods */
38
39
39
// The queue for audio blocks ready for alsa
40
// The queue for audio blocks ready for alsa
40
static const unsigned int qs = 200;
41
static const unsigned int qs = 100;
42
// Queue size target including alsa buffers. This gets recomputed as
43
// soon as we have the actual bit/chans params
41
static const unsigned int qt = qs/2;
44
static unsigned int qstarg = qs/2;
42
// the 40 value should be computed from the alsa buffer size. It's
43
// there becausee we have a jump on the first alsa write (alsa buffer
44
// is empty).
45
static const unsigned int qit = qs/2 + periodsize/1024;
46
45
47
static WorkQueue<AudioMessage*> alsaqueue("alsaqueue", qs);
46
static WorkQueue<AudioMessage*> alsaqueue("alsaqueue", qs);
48
static snd_pcm_t *pcm;
47
static snd_pcm_t *pcm;
49
static bool qinit = false;
48
static bool qinit = false;
50
49
51
static void *alsawriter(void *p)
50
static void *alsawriter(void *p)
52
{
51
{
53
    while (true) {
52
    while (true) {
54
        if (!qinit) {
53
        if (!qinit) {
55
            if (!alsaqueue.waitminsz(qit)) {
54
            if (!alsaqueue.waitminsz(qstarg)) {
56
                LOGERR("alsawriter: waitminsz failed\n");
55
                LOGERR("alsawriter: waitminsz failed\n");
56
                alsaqueue.workerExit();
57
                return (void *)1;
57
                return (void *)1;
58
            }
58
            }
59
        }
59
        }
60
        AudioMessage *tsk = 0;
60
        AudioMessage *tsk = 0;
61
        size_t qsz;
61
        size_t qsz;
...
...
81
    }
81
    }
82
}
82
}
83
83
84
static bool alsa_init(const string& dev, AudioMessage *tsk)
84
static bool alsa_init(const string& dev, AudioMessage *tsk)
85
{
85
{
86
    snd_pcm_hw_params_t *hw_params;
86
    snd_pcm_hw_params_t *hwparams;
87
    int err;
87
    int err;
88
    const char *cmd = "";
88
    const char *cmd = "";
89
    int dir=0;
89
    unsigned int actual_rate = tsk->m_freq;
90
    unsigned int actual_rate = tsk->m_freq;
90
    int dir=0;
91
    int periods = 2;       /* Number of periods */
92
91
93
    if ((err = snd_pcm_open(&pcm, dev.c_str(), 
92
    if ((err = snd_pcm_open(&pcm, dev.c_str(), 
94
                            SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
93
                            SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
95
        LOGERR("alsa_init: snd_pcm_open " << dev << " " << 
94
        LOGERR("alsa_init: snd_pcm_open " << dev << " " << 
96
               snd_strerror(err) << endl);
95
               snd_strerror(err) << endl);
97
        return false;;
96
        return false;;
98
    }
97
    }
99
         
100
    if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
98
    if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0) {
101
        LOGERR("alsa_init: snd_pcm_hw_params_malloc " << 
99
        LOGERR("alsa_init: snd_pcm_hw_params_malloc " << 
102
               snd_strerror(err) << endl);
100
               snd_strerror(err) << endl);
103
        snd_pcm_close(pcm);
101
        snd_pcm_close(pcm);
104
        return false;
102
        return false;
105
    }
103
    }
106
104
107
    cmd = "snd_pcm_hw_params_any";
105
    cmd = "snd_pcm_hw_params_any";
108
    if ((err = snd_pcm_hw_params_any(pcm, hw_params)) < 0) {
106
    if ((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0) {
109
        goto error;
107
        goto error;
110
    }
108
    }
111
  
112
    cmd = "snd_pcm_hw_params_set_access";
109
    cmd = "snd_pcm_hw_params_set_access";
113
    if ((err = 
110
    if ((err = 
114
         snd_pcm_hw_params_set_access(pcm, hw_params, 
111
         snd_pcm_hw_params_set_access(pcm, hwparams, 
115
                                      SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
112
                                      SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
116
        goto error;
113
        goto error;
117
    }
114
    }
118
115
119
    cmd = "snd_pcm_hw_params_set_format";
116
    cmd = "snd_pcm_hw_params_set_format";
120
    if ((err = 
117
    if ((err = 
121
         snd_pcm_hw_params_set_format(pcm, hw_params, 
118
         snd_pcm_hw_params_set_format(pcm, hwparams, 
122
                                      SND_PCM_FORMAT_S16_LE)) < 0) {
119
                                      SND_PCM_FORMAT_S16_LE)) < 0) {
123
        goto error;
120
        goto error;
124
    }
121
    }
122
    cmd = "snd_pcm_hw_params_set_channels";
123
    if ((err = snd_pcm_hw_params_set_channels(pcm, hwparams, 
124
                                              tsk->m_chans)) < 0) {
125
        goto error;
126
    }
125
    cmd = "snd_pcm_hw_params_set_rate_near";
127
    cmd = "snd_pcm_hw_params_set_rate_near";
126
    if ((err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, 
128
    if ((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, 
127
                                               &actual_rate, &dir)) < 0) {
129
                                               &actual_rate, &dir)) < 0) {
128
        goto error;
130
        goto error;
129
    }
131
    }
130
132
131
    cmd = "snd_pcm_hw_params_set_channels";
133
    unsigned int periodsmin, periodsmax;
132
    if ((err = snd_pcm_hw_params_set_channels(pcm, hw_params, 
134
    snd_pcm_hw_params_get_periods_min(hwparams, &periodsmin, &dir);
133
                                              tsk->m_chans)) < 0) {
135
    snd_pcm_hw_params_get_periods_max(hwparams, &periodsmax, &dir);
134
        goto error;
136
    LOGDEB("Alsa: periods min " << periodsmin << 
135
    }
137
           " max " << periodsmax << endl);
136
    /* Set number of periods. Periods used to be called fragments. */ 
138
    periods = 2;
139
    if (periods < periodsmin || periods > periodsmax)
140
        periods = periodsmin;
137
    cmd = "snd_pcm_hw_params_set_periods";
141
    cmd = "snd_pcm_hw_params_set_periods";
138
    if (snd_pcm_hw_params_set_periods(pcm, hw_params, periods, 0) < 0) {
142
    if ((err = snd_pcm_hw_params_set_periods(pcm, hwparams, periods, 0)) < 0) {
139
        goto error;
143
        goto error;
140
    }
144
    }
141
  
145
    snd_pcm_uframes_t bsmax, bsmin;
142
    /* Set buffer size (in frames). The resulting latency is given by */
146
    snd_pcm_hw_params_get_buffer_size_min(hwparams, &bsmin);
143
    /* latency = periodsize * periods / (rate * bytes_per_frame)     */
147
    snd_pcm_hw_params_get_buffer_size_max(hwparams, &bsmax);
148
    unsigned int bufferframes;
149
    bufferframes = periodsize * periods / (2*tsk->m_chans);
150
    if (bufferframes < bsmin || bufferframes > bsmax) {
151
        bufferframes = bsmin;
152
        periodsize = bufferframes / periods * (2 * tsk->m_chans);
153
    }
144
    cmd = "snd_pcm_hw_params_set_buffer_size";
154
    cmd = "snd_pcm_hw_params_set_buffer_size";
155
    LOGDEB("Alsa: set buffer_size: min " << bsmin << " max " << bsmax << 
156
           " val " << bufferframes << endl);
145
    if (snd_pcm_hw_params_set_buffer_size(pcm, hw_params, 
157
    if ((err = snd_pcm_hw_params_set_buffer_size(pcm, hwparams, bufferframes)) 
146
                                          (periodsize * periods)>>2) < 0) {
158
        < 0) {
147
        goto error;
159
        goto error;
148
    }
160
    }
149
  
161
  
150
    cmd = "snd_pcm_hw_params";
162
    cmd = "snd_pcm_hw_params";
151
    if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0) {
163
    if ((err = snd_pcm_hw_params(pcm, hwparams)) < 0) {
152
        goto error;
164
        goto error;
153
    }
165
    }
154
    
166
    
155
    snd_pcm_hw_params_free(hw_params);
167
    snd_pcm_hw_params_free(hwparams);
156
    return true;
168
    return true;
157
169
158
error:
170
error:
171
    LOGERR("alsa_init: " << cmd << " error:" << snd_strerror(err) << endl);
159
    snd_pcm_hw_params_free(hw_params);
172
    snd_pcm_hw_params_free(hwparams);
160
    LOGERR("alsa_init: " << cmd << " " << snd_strerror(err) << endl);
161
    return false;;
173
    return false;
174
}
175
176
// Current in-driver delay in samples
177
static int alsadelay()
178
{
179
    snd_pcm_sframes_t delay;
180
    if (snd_pcm_delay(pcm, &delay) >= 0) {
181
        return delay;
182
    } else {
183
        return 0;
184
    }
162
}
185
}
163
186
164
static void *audioEater(void *cls)
187
static void *audioEater(void *cls)
165
{
188
{
189
    LOGDEB("audioEater: alsadirect\n");
166
    AudioEater::Context *ctxt = (AudioEater::Context*)cls;
190
    AudioEater::Context *ctxt = (AudioEater::Context*)cls;
167
168
    LOGDEB("alsaEater: queue " << ctxt->queue << endl);
169
191
170
    WorkQueue<AudioMessage*> *queue = ctxt->queue;
192
    WorkQueue<AudioMessage*> *queue = ctxt->queue;
171
    string alsadevice = ctxt->alsadevice;
193
    string alsadevice = ctxt->alsadevice;
172
194
173
    delete ctxt;
195
    delete ctxt;
196
    ctxt = 0;
174
197
175
    qinit = false;
198
    qinit = false;
199
200
    float samplerate_ratio = 1.0;
201
176
    int src_error = 0;
202
    int src_error = 0;
177
    SRC_STATE *src_state = 0;
203
    SRC_STATE *src_state = 0;
178
    SRC_DATA src_data;
204
    SRC_DATA src_data;
179
    memset(&src_data, 0, sizeof(src_data));
205
    memset(&src_data, 0, sizeof(src_data));
206
180
    alsaqueue.start(1, alsawriter, 0);
207
    alsaqueue.start(1, alsawriter, 0);
181
    float samplerate_ratio = 1.0;
182
208
183
    while (true) {
209
    while (true) {
184
        AudioMessage *tsk = 0;
210
        AudioMessage *tsk = 0;
185
        size_t qsz;
211
        size_t qsz;
186
        if (!queue->take(&tsk, &qsz)) {
212
        if (!queue->take(&tsk, &qsz)) {
187
            // TBD: reset alsa?
213
            LOGDEB("audioEater: alsadirect: queue take failed\n");
214
            alsaqueue.setTerminateAndWait();
188
            queue->workerExit();
215
            queue->workerExit();
189
            return (void*)1;
216
            return (void*)1;
190
        }
217
        }
191
218
219
        if (tsk->m_bytes == 0 || tsk->m_chans == 0 || tsk->m_bits == 0) {
220
            LOGDEB("Zero buf\n");
221
            continue;
222
        }
223
224
        int bufframes = 441;
192
        if (src_state == 0) {
225
        if (src_state == 0) {
193
            if (!alsa_init(alsadevice, tsk)) {
226
            if (!alsa_init(alsadevice, tsk)) {
227
                alsaqueue.setTerminateAndWait();
194
                queue->workerExit();
228
                queue->workerExit();
195
                return (void *)1;
229
                return (void *)1;
196
            }
230
            }
197
            // BEST_QUALITY yields approx 25% cpu on a core i7
231
            // BEST_QUALITY yields approx 25% cpu on a core i7
198
            // 4770T. Obviously too much, actually might not be
232
            // 4770T. Obviously too much, actually might not be
...
...
201
            // FASTEST is 4-5%. Given that this is process-wide, probably
235
            // FASTEST is 4-5%. Given that this is process-wide, probably
202
            // a couple % in fact.
236
            // a couple % in fact.
203
            // To be re-evaluated on the pi... FASTEST is 30% CPU on a Pi 2
237
            // 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.
238
            // with USB audio. Curiously it's 25-30% on a Pi1 with i2s audio.
205
            src_state = src_new(SRC_SINC_FASTEST, tsk->m_chans, &src_error);
239
            src_state = src_new(SRC_SINC_FASTEST, tsk->m_chans, &src_error);
206
        }
207
240
241
            // This is constant for a given stream (depends on fe, buffers 
242
            // are 10mS)
243
            bufframes = tsk->m_bytes / (tsk->m_chans * (tsk->m_bits/8));
244
            // period size is in bytes
245
            LOGDEB("audioEater: alsadirect: qstarg " << qstarg << endl);
246
        }
247
248
        float qs = alsaqueue.qsize();
208
        if (qinit) {
249
        if (qinit) {
209
            float qs = alsaqueue.qsize();
250
            qs = alsaqueue.qsize() + alsadelay() / bufframes;
210
            float t = ((qt - qs) / qt);
251
            float t = ((qstarg - qs) / qstarg);
211
            float adj = t * t  / 10;
252
            float adj = t * t;
212
            if (alsaqueue.qsize() < qt) {
253
            if (qs < qstarg) {
213
                samplerate_ratio =  1.0 + adj;
254
                samplerate_ratio =  1.0 + adj;
214
                if (samplerate_ratio > 1.1)
255
                if (samplerate_ratio > 1.1)
215
                    samplerate_ratio = 1.1;
256
                    samplerate_ratio = 1.1;
216
            } else {
257
            } else {
217
                samplerate_ratio = 1.0 - adj;
258
                samplerate_ratio = 1.0 - adj;
...
...
254
        }
295
        }
255
        {
296
        {
256
            static int cnt;
297
            static int cnt;
257
            if (cnt++ == 100) {
298
            if (cnt++ == 100) {
258
                LOGDEB("samplerate: " 
299
                LOGDEB("samplerate: " 
300
                       " qstarg " << qstarg <<
259
                       " qsize " << alsaqueue.qsize() << 
301
                       " iqsz " << alsaqueue.qsize() <<
302
                       " qsize " << int(qs) << 
260
                       " ratio " << samplerate_ratio <<
303
                       " ratio " << samplerate_ratio <<
261
                       " in " << src_data.input_frames << 
304
                       " in " << src_data.input_frames << 
262
                       " consumed " << src_data.input_frames_used << 
305
                       " consumed " << src_data.input_frames_used << 
263
                       " out " << src_data.output_frames_gen << endl);
306
                       " out " << src_data.output_frames_gen << endl);
264
                cnt = 0;
307
                cnt = 0;
...
...
295
#endif
338
#endif
296
        }
339
        }
297
340
298
        if (!alsaqueue.put(tsk)) {
341
        if (!alsaqueue.put(tsk)) {
299
            LOGERR("alsaEater: queue put failed\n");
342
            LOGERR("alsaEater: queue put failed\n");
343
            queue->workerExit();
300
            return (void *)1;
344
            return (void *)1;
301
        }
345
        }
302
    }
346
    }
303
}
347
}
304
348