128 | | // FIXME: some tests - attempt to recover sync after loss due to missed cycles |
---|
129 | | static signed int next_cycle = -1; |
---|
130 | | //static suseconds_t us_stack[10] = {0,0,0,0,0,0,0,0,0,0}; |
---|
131 | | //static int us_i = 0; |
---|
132 | | //struct timeval tv; |
---|
133 | | //gettimeofday(&tv, NULL); |
---|
134 | | //us_stack[us_i] = tv.tv_usec + (tv.tv_sec%10)*1000000; |
---|
135 | | //if (++us_i == 10) |
---|
136 | | // us_i = 0; |
---|
137 | | |
---|
138 | | if (!m_disabled && next_cycle>=0 && cycle!=next_cycle) { |
---|
139 | | debugOutput(DEBUG_LEVEL_VERBOSE, "tx cycle miss: %d requested, %d expected\n",cycle,next_cycle); |
---|
140 | | debugOutput(DEBUG_LEVEL_VERBOSE, "tx stream: cycle=%d, ofs=%g\n",m_cycle_count, m_cycle_ofs); |
---|
141 | | |
---|
142 | | // Print the times of the last 10 calls to this function |
---|
143 | | //int i = us_i, l = -1; |
---|
144 | | // fprintf(stderr,"usec stack:\n"); |
---|
145 | | // do { |
---|
146 | | // fprintf(stderr, " %d",us_stack[i]); |
---|
147 | | // if (l != -1) |
---|
148 | | // fprintf(stderr," (%d)",us_stack[i]-us_stack[l]); |
---|
149 | | // l = i; |
---|
150 | | // if (++i == 10) |
---|
151 | | // i = 0; |
---|
152 | | // } while (i != us_i); |
---|
153 | | // fprintf(stderr,"\n"); |
---|
154 | | |
---|
155 | | #if 0 |
---|
156 | | // This "simple" way out doesn't work, probably because there's no |
---|
157 | | // guarantee that ofs 0 in the current cycle is anywhere near an audio |
---|
158 | | // sample point. |
---|
159 | | m_cycle_count = cycle; |
---|
160 | | m_cycle_ofs = 0.0; |
---|
161 | | m_tx_dbc = 0; |
---|
162 | | #else |
---|
163 | | float ftmp; |
---|
164 | | signed int ccount; |
---|
165 | | |
---|
166 | | ccount = next_cycle; |
---|
167 | | while (ccount!=cycle) { |
---|
168 | | |
---|
169 | | corrected_cycle = ccount; |
---|
170 | | if (m_cycle_count-ccount > 7900) |
---|
171 | | corrected_cycle += 8000; |
---|
172 | | |
---|
173 | | if (corrected_cycle < m_cycle_count) { |
---|
174 | | if (++ccount == 8000) |
---|
175 | | ccount = 0; |
---|
176 | | continue; |
---|
177 | | } |
---|
178 | | m_tx_dbc += n_events; |
---|
179 | | incrementFrameCounter(n_events); |
---|
180 | | |
---|
181 | | ftmp = m_cycle_ofs+n_events*(*m_ticks_per_frame); |
---|
182 | | m_cycle_count += (unsigned int)ftmp/3072; |
---|
183 | | m_cycle_count %= 8000; |
---|
184 | | m_cycle_ofs = fmod(ftmp, 3072); |
---|
185 | | |
---|
186 | | if (++ccount == 8000) |
---|
187 | | ccount = 0; |
---|
188 | | } |
---|
189 | | m_tx_dbc &= 0xff; |
---|
190 | | |
---|
191 | | #endif |
---|
192 | | debugOutput(DEBUG_LEVEL_VERBOSE, " resuming with cyclecount=%d, cycleofs=%g (ticksperfame=%g)\n", |
---|
193 | | m_cycle_count, m_cycle_ofs, *m_ticks_per_frame); |
---|
194 | | } |
---|
195 | | if (!m_disabled) |
---|
196 | | next_cycle = (cycle+1)%8000; |
---|
197 | | else |
---|
198 | | next_cycle = -1; |
---|
| 130 | // Size of data to read from the event buffer, in bytes. |
---|
| 131 | unsigned int read_size = n_events * m_event_size; |
---|
| 132 | |
---|
| 133 | // Detect a missed cycle and attempt to "catch up". |
---|
| 134 | if (!m_disabled && m_next_cycle>=0 && cycle!=m_next_cycle) { |
---|
| 135 | float ftmp; |
---|
| 136 | signed int ccount = m_next_cycle; |
---|
| 137 | debugOutput(DEBUG_LEVEL_VERBOSE, "tx cycle miss: %d requested, %d expected\n",cycle,m_next_cycle); |
---|
| 138 | |
---|
| 139 | while (ccount!=cycle) { |
---|
| 140 | unwrapped_cycle = ccount; |
---|
| 141 | if (m_cycle_count-ccount > 7900) |
---|
| 142 | unwrapped_cycle += 8000; |
---|
| 143 | |
---|
| 144 | if (unwrapped_cycle < m_cycle_count) { |
---|
| 145 | if (++ccount == 8000) |
---|
| 146 | ccount = 0; |
---|
| 147 | continue; |
---|
| 148 | } |
---|
| 149 | // Advance buffers and counters as if this cycle had been dealt with |
---|
| 150 | m_tx_dbc += n_events; |
---|
| 151 | incrementFrameCounter(n_events); |
---|
| 152 | |
---|
| 153 | ftmp = m_cycle_ofs+n_events*(*m_ticks_per_frame); |
---|
| 154 | m_cycle_count += (unsigned int)ftmp/3072; |
---|
| 155 | m_cycle_count %= 8000; |
---|
| 156 | m_cycle_ofs = fmod(ftmp, 3072); |
---|
| 157 | |
---|
| 158 | if (++ccount == 8000) |
---|
| 159 | ccount = 0; |
---|
| 160 | |
---|
| 161 | // Also advance the event buffer to keep things in sync |
---|
| 162 | freebob_ringbuffer_read_advance(m_event_buffer,read_size); |
---|
| 163 | } |
---|
| 164 | m_tx_dbc &= 0xff; |
---|
| 165 | debugOutput(DEBUG_LEVEL_VERBOSE, " resuming with cyclecount=%d, cycleofs=%g (ticksperfame=%g)\n", |
---|
| 166 | m_cycle_count, m_cycle_ofs, *m_ticks_per_frame); |
---|
| 167 | |
---|
| 168 | m_next_cycle = cycle; |
---|
| 169 | } |
---|
| 170 | |
---|
| 171 | |
---|
| 172 | if (!m_disabled) { |
---|
| 173 | if (++m_next_cycle >= 8000) |
---|
| 174 | m_next_cycle -= 8000; |
---|
| 175 | } else |
---|
| 176 | m_next_cycle = -1; |
---|
284 | | // FIXME: once working, make more efficient by removing 1 of the |
---|
285 | | // "trim to 8000" operations. |
---|
| 259 | // |
---|
| 260 | // CYCLE_DELAY accounts for the delay between the cycle |
---|
| 261 | // audio is sent in and when the MOTU can actually play |
---|
| 262 | // that audio. The SPH timestamp must account for this |
---|
| 263 | // it doesn't demand to be played before it's possible. |
---|
| 264 | // For the duration of the event loop, account for the |
---|
| 265 | // CYCLE_DELAY within m_cycle_count to save having to wrap |
---|
| 266 | // (m_cycle_count+CYCLE_DELAY) and m_cycle_count separately |
---|
| 267 | // within the event loop. Once the loop is finished we |
---|
| 268 | // reset m_cyle_count to once again refer to the send |
---|
| 269 | // cycle rather than the audio presentation cycle. |
---|
| 270 | // |
---|
| 271 | // This seemingly messy treatment saves one modulo operation |
---|
| 272 | // per loop iteration. Since the loop count ranges from 8 |
---|
| 273 | // (for 1x sample rates) to 32 there are considerable |
---|
| 274 | // savings to be made even at 1x rates. |
---|
| 275 | if ((m_cycle_count+=CYCLE_DELAY) >= 8000) |
---|
| 276 | m_cycle_count -= 8000; |
---|
287 | | *quadlet = htonl( (((m_cycle_count+CYCLE_DELAY)%8000)<<12) + |
---|
288 | | (int)m_cycle_ofs); |
---|
289 | | // FIXME: remove this hacked in 1 kHz test signal to analog-1 when testing |
---|
290 | | // is complete. Note that the tone is *not* added during closedown. |
---|
291 | | if (m_closedown_count<0) { |
---|
292 | | //static signed int a_cx=0, a_ofs=0; |
---|
293 | | static signed int a_cx = 0; |
---|
294 | | signed int val; |
---|
295 | | // val = (int)(0x7fffff*sin(1000.0*2.0*M_PI*(m_cycle_count+((m_cycle_ofs)/3072.0))/8000.0)); |
---|
296 | | val = (int)(0x7fffff*sin(1000.0*2.0*M_PI*(a_cx/24576000.0))); |
---|
297 | | if ((a_cx+=512) >= 24576000) { |
---|
298 | | a_cx -= 24576000; |
---|
299 | | } |
---|
300 | | //val = (int)(0x7fffff*sin(1000.0*2.0*M_PI*(a_cx+(float(a_ofs)/3072.0))/8000.0)); |
---|
301 | | //if ((a_ofs+=512) >= 3072) { |
---|
302 | | // a_ofs -= 3072; |
---|
303 | | // if (++a_cx > 7999) |
---|
304 | | // a_cx -= 8000; |
---|
305 | | //} |
---|
306 | | *(data+8+i*m_event_size+16) = (val >> 16) & 0xff; |
---|
307 | | *(data+8+i*m_event_size+17) = (val >> 8) & 0xff; |
---|
308 | | *(data+8+i*m_event_size+18) = val & 0xff; |
---|
309 | | } |
---|
310 | | m_cycle_ofs += *m_ticks_per_frame; |
---|
311 | | if (m_cycle_ofs >= 3072) { |
---|
| 278 | *quadlet = htonl( (m_cycle_count<<12) + (int)m_cycle_ofs); |
---|
| 279 | #if TESTTONE |
---|
| 280 | // FIXME: remove this hacked in 1 kHz test signal to |
---|
| 281 | // analog-1 when testing is complete. Note that the tone is |
---|
| 282 | // *never* added during closedown. |
---|
| 283 | if (m_closedown_count<0) { |
---|
| 284 | static signed int a_cx = 0; |
---|
| 285 | signed int val; |
---|
| 286 | val = (int)(0x7fffff*sin(1000.0*2.0*M_PI*(a_cx/24576000.0))); |
---|
| 287 | if ((a_cx+=512) >= 24576000) { |
---|
| 288 | a_cx -= 24576000; |
---|
| 289 | } |
---|
| 290 | *(data+8+i*m_event_size+16) = (val >> 16) & 0xff; |
---|
| 291 | *(data+8+i*m_event_size+17) = (val >> 8) & 0xff; |
---|
| 292 | *(data+8+i*m_event_size+18) = val & 0xff; |
---|
| 293 | } |
---|
| 294 | #endif |
---|
| 295 | if ((m_cycle_ofs+=*m_ticks_per_frame) >= 3072) { |
---|
827 | | // FIXME: ideally we want to include a condition which tests if the |
---|
828 | | // stream shutdown is in response to an xrun due to a problem at |
---|
829 | | // startup, and unconditionally return true. This saves a few |
---|
830 | | // seconds delay since under these conditions the iso transmit |
---|
831 | | // callback doesn't appear to be called and therefore |
---|
832 | | // m_closedown_count is never decremented. We can't just test for |
---|
833 | | // an xrun however since sometimes these can occur "normally" during |
---|
834 | | // shutdown. This is probably all tied up with the sync recovery |
---|
835 | | // issue which hasn't really been explored yet. |
---|
836 | | if (m_disabled || !isRunning()) |
---|
| 819 | // If the stream is disabled or isn't running there's no need to |
---|
| 820 | // wait since the MOTU *should* still be in a "zero data" state. |
---|
| 821 | // |
---|
| 822 | // If the m_streaming_active flag is 0 it indicates that the |
---|
| 823 | // transmit callback hasn't been called since a closedown was |
---|
| 824 | // requested when this function was last called. This effectively |
---|
| 825 | // signifies that the streaming thread has been exitted due to an |
---|
| 826 | // xrun in either the receive or transmit handlers. In this case |
---|
| 827 | // there's no point in waiting for the closedown count to hit zero |
---|
| 828 | // because it never will; the zero data will never get to the MOTU. |
---|
| 829 | // It's best to allow an immediate stop and let the xrun handler |
---|
| 830 | // proceed as best it can. |
---|
| 831 | // |
---|
| 832 | // The ability to detect the lack of streaming also prevents the |
---|
| 833 | // "wait for stop" in the stream processor manager's stop() method |
---|
| 834 | // from hitting its timeout which in turn seems to increase the |
---|
| 835 | // probability of a successful recovery. |
---|
| 836 | if (m_disabled || !isRunning() || !m_streaming_active) |
---|
899 | | |
---|
900 | | // FIXME: just for debugging, print out the sph ofs DLL value |
---|
901 | | // once a second |
---|
902 | | //if (cycle==0) { |
---|
903 | | // fprintf(stderr, "m_ticks_per_frame=%g\n",*m_ticks_per_frame); |
---|
904 | | //} |
---|
905 | | |
---|
906 | | // FIXME: more debugging |
---|
907 | | static signed int last_cycle=-1; |
---|
908 | | if (last_cycle>=0 && (signed)cycle!=(last_cycle+1)%8000) { |
---|
909 | | debugOutput(DEBUG_LEVEL_VERBOSE, "lost rx cycles; received %d, expected %d\n", |
---|
910 | | cycle, (last_cycle+1)%8000); |
---|
911 | | } |
---|
912 | | last_cycle=cycle; |
---|
| 904 | signed int have_lost_cycles = 0; |
---|
| 905 | |
---|
| 906 | // Detect missed receive cycles |
---|
| 907 | // FIXME: it would be nice to advance the rx buffer by the amount of |
---|
| 908 | // frames missed. However, since the MOTU transmits more frames |
---|
| 909 | // per cycle than the average and "catches up" with period emty |
---|
| 910 | // cycles it's not trivial to work out precisely how many frames |
---|
| 911 | // were missed. Ultimately we need to do so if sync is to be |
---|
| 912 | // maintained across a transient receive failure. |
---|
| 913 | if (m_next_cycle < 0) |
---|
| 914 | m_next_cycle = cycle; |
---|
| 915 | if ((signed)cycle != m_next_cycle) { |
---|
| 916 | debugOutput(DEBUG_LEVEL_VERBOSE, "lost rx cycles; received %d, expected %d\n", |
---|
| 917 | cycle, m_next_cycle); |
---|
| 918 | m_next_cycle = cycle; |
---|
| 919 | have_lost_cycles = 1; |
---|
| 920 | } |
---|
| 921 | if (++m_next_cycle >= 8000) |
---|
| 922 | m_next_cycle -= 8000; |
---|
935 | | #if 1 |
---|
936 | | /* FIXME: test whether things are improved by doing this in the |
---|
937 | | * actual receive handler. The advantage is that we don't have to |
---|
938 | | * wait until the stream is enabled before the DLL starts tracking. |
---|
939 | | * This has the desireable side-effect of having a relatively |
---|
940 | | * accurate value in the DLL by the time the transmit stream is |
---|
941 | | * enabled. A disadvantage of having this in here is that it |
---|
942 | | * increases the time spent in this function. Whether this is |
---|
943 | | * important in practice remains to be seen. Ad hoc evidence thus |
---|
944 | | * far seems to suggest that it does increase the chances of a |
---|
945 | | * faulty startup, but more tests are needed. |
---|
946 | | */ |
---|
947 | | /* Push cycle offset differences from each event's SPH into the DLL. |
---|
948 | | * If this is the very first block received, use the first event to |
---|
949 | | * initialise the last cycle offset. |
---|
950 | | * FIXME: it might be best to use differences only within the given |
---|
951 | | * block rather than keeping a store of the last cycle offset. |
---|
952 | | * Otherwise in the event of a lost incoming packet the DLL will |
---|
953 | | * have an abnormally large value sent to it. Perhaps this doesn't |
---|
954 | | * matter? |
---|
| 945 | /* Send actual ticks-per-frame values (as deduced by the incoming |
---|
| 946 | * SPHs) to the DLL for averaging. Doing this here means the DLL |
---|
| 947 | * should acquire a reasonable estimation of the ticks per frame |
---|
| 948 | * even while the stream is formally disabled. This in turn means |
---|
| 949 | * the transmit stream should have access to a very realistic |
---|
| 950 | * estimate by the time it is enabled. The major disadvantage |
---|
| 951 | * is a small increase in the overheads of this function compared |
---|
| 952 | * to what would be the case if this was delayed by pushing it into |
---|
| 953 | * the decode functions. |
---|
957 | | signed int sph_ofs = ntohl(*(quadlet_t *)(data+8)) & 0xfff; |
---|
958 | | |
---|
959 | | // if (m_last_cycle_ofs < 0) { |
---|
960 | | // m_last_cycle_ofs = sph_ofs-(int)(*m_ticks_per_frame); |
---|
961 | | // } |
---|
962 | | m_last_cycle_ofs = sph_ofs; |
---|
963 | | for (ev=1; ev<n_events; ev++) { |
---|
| 956 | signed int sph_ofs; |
---|
| 957 | |
---|
| 958 | /* If this is the first block received or we have lost cycles, |
---|
| 959 | * initialise the m_last_cycle_ofs to a value which won't cause the |
---|
| 960 | * DLL to become polluted with an inappropriate ticks-per-frame |
---|
| 961 | * estimate. |
---|
| 962 | */ |
---|
| 963 | if (m_last_cycle_ofs<0 || have_lost_cycles) { |
---|
| 964 | sph_ofs = ntohl(*(quadlet_t *)(data+8)) & 0xfff; |
---|
| 965 | m_last_cycle_ofs = sph_ofs-(int)(m_ticks_per_frame); |
---|
| 966 | } |
---|
| 967 | for (ev=0; ev<n_events; ev++) { |
---|
1261 | | #if 0 |
---|
1262 | | /* Push cycle offset differences from each event's SPH into the DLL. |
---|
1263 | | * If this is the very first block received, use the first event to |
---|
1264 | | * initialise the last cycle offset. |
---|
1265 | | * FIXME: it might be best to use differences only within the given |
---|
1266 | | * block rather than keeping a store of the last cycle offset. |
---|
1267 | | * Otherwise in the event of a lost incoming packet the DLL will |
---|
1268 | | * have an abnormally large value sent to it. Perhaps this doesn't |
---|
1269 | | * matter? |
---|
1270 | | */ |
---|
1271 | | unsigned int ev; |
---|
1272 | | signed int sph_ofs = ntohl(*(quadlet_t *)data) & 0xfff; |
---|
1273 | | |
---|
1274 | | if (m_last_cycle_ofs < 0) { |
---|
1275 | | // m_last_cycle_ofs = sph_ofs-(int)(*m_ticks_per_frame); |
---|
1276 | | m_last_cycle_ofs = sph_ofs - m_ticks_per_frame; |
---|
1277 | | } |
---|
1278 | | for (ev=0; ev<nevents; ev++) { |
---|
1279 | | sph_ofs = ntohl(*(quadlet_t *)(data+ev*m_event_size)) & 0xfff; |
---|
1280 | | signed int sph_diff = (m_last_cycle_ofs<sph_ofs)? |
---|
1281 | | sph_ofs-m_last_cycle_ofs:sph_ofs+3072-m_last_cycle_ofs; |
---|
1282 | | float err = sph_diff - m_ticks_per_frame; |
---|
1283 | | // FIXME: originally we used a value of 0.0005 for the coefficient |
---|
1284 | | // which mirrored the value used in |
---|
1285 | | // AmdtpReceiveStreamProcessor::putPacket() for a similar purpose. |
---|
1286 | | // However, tests showed that this introduced discontinuities in |
---|
1287 | | // the output audio signal, so an alternative value was sought. |
---|
1288 | | // Further tests are needed, but a value of 0.01 seems to work |
---|
1289 | | // well, at least at a sample rate of 48 kHz. |
---|
1290 | | m_ticks_per_frame += 0.01*err; |
---|
1291 | | |
---|
1292 | | m_last_cycle_ofs = sph_ofs; |
---|
1293 | | } |
---|
1294 | | #endif |
---|