MeasuringSoundLatency: latency.c

File latency.c, 5.4 KB (added by bennylp, 16 years ago)
Line 
1/* $Id:$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20
21#include <pjmedia.h>
22#include <pjlib.h>
23
24#include <stdio.h>
25
26#define THIS_FILE   "lacency.c"
27
28
29/* Util to display the error message for the specified error code  */
30static int app_perror( const char *sender, const char *title,
31                       pj_status_t status)
32{
33    char errmsg[PJ_ERR_MSG_SIZE];
34
35    PJ_UNUSED_ARG(sender);
36
37    pj_strerror(status, errmsg, sizeof(errmsg));
38
39    printf("%s: %s [code=%d]\n", title, errmsg, status);
40    return 1;
41}
42
43/*
44 * Find out latency
45 */
46static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav)
47{
48    pjmedia_frame frm;
49    short *buf;
50    unsigned i, samples_per_frame, read, len;
51    unsigned start_pos;
52    pj_status_t status;
53
54    unsigned lat_sum = 0,
55             lat_cnt = 0,
56             lat_min = 10000,
57             lat_max = 0;
58
59    samples_per_frame = wav->info.samples_per_frame;
60    frm.buf = pj_pool_alloc(pool, samples_per_frame * 2);
61    frm.size = samples_per_frame * 2;
62    len = pjmedia_wav_player_get_len(wav);
63    buf = pj_pool_alloc(pool, len + samples_per_frame);
64
65    read = 0;
66    while (read < len/2) {
67        status = pjmedia_port_get_frame(wav, &frm);
68        if (status != PJ_SUCCESS)
69            break;
70
71        pjmedia_copy_samples(buf+read, (short*)frm.buf, samples_per_frame);
72        read += samples_per_frame;
73    }
74
75    if (read < 2 * wav->info.clock_rate) {
76        puts("Error: too short");
77        return -1;
78    }
79
80    start_pos = 0;
81    while (start_pos < len/2 - wav->info.clock_rate) {
82        int max_signal = 0;
83        unsigned max_signal_pos = start_pos;
84        unsigned max_echo_pos = 0;
85        unsigned pos;
86        unsigned lat;
87
88        /* Get the largest signal in the next 0.7s */
89        for (i=start_pos; i<start_pos + wav->info.clock_rate * 700 / 1000; ++i) {
90            if (abs(buf[i]) > max_signal) {
91                max_signal = abs(buf[i]);
92                max_signal_pos = i;
93            }
94        }
95
96        /* Advance 10ms from max_signal_pos */
97        pos = max_signal_pos + 10 * wav->info.clock_rate / 1000;
98
99        /* Get the largest signal in the next 500ms */
100        max_signal = 0;
101        max_echo_pos = pos;
102        for (i=pos; i<pos+wav->info.clock_rate/2; ++i) {
103            if (abs(buf[i]) > max_signal) {
104                max_signal = abs(buf[i]);
105                max_echo_pos = i;
106            }
107        }
108
109        lat = (max_echo_pos - max_signal_pos) * 1000 / wav->info.clock_rate;
110       
111#if 0
112        printf("Latency = %u\n", lat);
113#endif
114
115        lat_sum += lat;
116        lat_cnt++;
117        if (lat < lat_min)
118            lat_min = lat;
119        if (lat > lat_max)
120            lat_max = lat;
121
122        /* Advance next loop */
123        start_pos += wav->info.clock_rate;
124    }
125
126    printf("Latency average = %u\n", lat_sum / lat_cnt);
127    printf("Latency minimum = %u\n", lat_min);
128    printf("Latency maximum = %u\n", lat_max);
129    printf("Number of data  = %u\n", lat_cnt);
130    return 0;
131}
132
133/*
134To calculate sound latency:
135pjsua_vc6 --ec-tail 0 --rec-file rec.wav --play-file tock.wav
136
137cc 0 0
138cc 1 0
139cc 1 2
140cc 0 2
141sleep 30000
142cd 0 0
143cd 1 0
144cd 1 2
145cd 0 2
146q
147
148
149To calculate network latency:
150pjsua_vc6 --ec-tail 0 --rec-file rec.wav --play-file tock.wav --null-audio
151
152m
153sip:192.168.0.2:5090
154sleep 200
155cd 0 4
156cd 4 0
157cc 1 2
158cc 4 2
159cc 1 4
160sleep 30000
161cd 1 2
162cd 4 2
163cd 1 4
164h
165sleep 500
166q
167
168
169 */
170
171/*
172 * main()
173 */
174int main(int argc, char *argv[])
175{
176    enum { NSAMPLES = 160, COUNT=100 };
177    pj_caching_pool cp;
178    pj_pool_t *pool;
179    pjmedia_port *wav;
180    pj_status_t status;
181
182
183    /* Verify cmd line arguments. */
184    if (argc != 2) {
185        puts("Error: missing argument(s)");
186        puts("Usage: latency REV.WAV");
187        return 1;
188    }
189
190    pj_log_set_level(0);
191
192    status = pj_init();
193    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
194
195    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
196
197    pool = pj_pool_create( &cp.factory,     /* pool factory         */
198                           "wav",           /* pool name.           */
199                           4000,            /* init size            */
200                           4000,            /* increment size       */
201                           NULL             /* callback on error    */
202                           );
203
204    pj_register_strerror(PJMEDIA_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
205                         &pjmedia_strerror);
206
207    /* Wav */
208    status = pjmedia_wav_player_port_create(  pool,     /* memory pool      */
209                                              argv[1],  /* file to play     */
210                                              0,        /* use default ptime*/
211                                              0,        /* flags            */
212                                              0,        /* default buffer   */
213                                              &wav      /* returned port    */
214                                              );
215    if (status != PJ_SUCCESS) {
216        app_perror(THIS_FILE, argv[1], status);
217        return 1;
218    }
219
220    status = calculate_latency(pool, wav);
221    if (status != PJ_SUCCESS)
222        return 1;
223
224    status = pjmedia_port_destroy( wav );
225    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
226
227    pj_pool_release( pool );
228    pj_caching_pool_destroy( &cp );
229    pj_shutdown();
230
231    /* Done. */
232    return 0;
233}
234