1 //
2 // OGG decoding using Tremor (libvorbisidec), with optional
3 // looping.
4 //
5 // (c) 1999-2004 Jim Peters <jim@uazu.net>. All Rights Reserved.
6 // For latest version see http://sbagen.sf.net/ or
7 // http://uazu.net/sbagen/. Released under the GNU GPL version 2.
8 //
9 // See comments lower down for OGG looper parameters.
10 //
11
12 #include "libs/ivorbiscodec.h"
13 #include "libs/ivorbisfile.h"
14
15 extern FILE *mix_in;
16 extern int mix_cnt;
17 extern void *Alloc(size_t);
18 extern void error(char *fmt, ...);
19 extern int out_rate, out_rate_def;
20
21 void ogg_init() ;
22 void ogg_term() ;
23 int ogg_read(int *dst, int dlen) ;
24 void looper_init() ;
25 void looper_term() ;
26 int looper_read(int *dst, int dlen) ;
27 static void looper_sched() ;
28 static void looper_sched2() ;
29
30 static OggVorbis_File oggfile;
31 static short *ogg_buf0, *ogg_buf1, *ogg_rd, *ogg_end;
32 static int ogg_mult;
33
34 void
35 ogg_init() {
36 vorbis_info *vi;
37 vorbis_comment *vc;
38 int len= 2048;
39 int a;
40
41 // Setup OGG decoder
42 if (0 > ov_open(mix_in, &oggfile, NULL, 0))
43 error("Input does not appear to be an Ogg bitstream");
44
45 // Check for ReplayGain
46 vc= ov_comment(&oggfile, -1);
47 ogg_mult= 16;
48 for (a= 0; a<vc->comments; a++) {
49 char *str= vc->user_comments[a];
50 if (0 == memcmp(str, "REPLAYGAIN_TRACK_GAIN=", 22)) {
51 char *end;
52 double val= strtod(str += 22, &end);
53 if (end == str)
54 warn("Ignoring bad REPLAYGAIN_TRACK_GAIN: %s", str);
55 else {
56 val -= 3; // Adjust vorbisgain's 89dB to 86dB
57 ogg_mult= (int)(floor(0.5 + 16 * pow(10, val/20)));
58 warn("ReplayGain setting detected, Ogg scaling by %.2f", ogg_mult/16.0);
59 }
60 }
61 }
62
63 // Check to see is this is a looping OGG
64 for (a= 0; a<vc->comments; a++) {
65 if (0 == memcmp(vc->user_comments[a], "SBAGEN_LOOPER=", 14)) {
66 vc= 0; break;
67 }
68 }
69 if (!vc) {
70 // This is a looping OGG. Load the OGG into memory instead;
71 // handle with looper_* code. ov_clear is called after because
72 // it closes mix_in.
73 looper_init();
74 ov_clear(&oggfile);
75 return;
76 }
77
78 // Pick up sampling rate and override default if -r not used
79 vi= ov_info(&oggfile, -1);
80 if (out_rate_def) out_rate= vi->rate;
81 out_rate_def= 0;
82
83 // Setup buffer so that we can be consistent in our calls to ov_read
84 ogg_buf0= ALLOC_ARR(len, short);
85 ogg_buf1= ogg_buf0 + len;
86 ogg_rd= ogg_end= ogg_buf0;
87
88 // Start a thread to handle generation of the mix stream
89 inbuf_start(ogg_read, 256*1024); // 1024K buffer: 3s@44.1kHz
90 }
91
92 void
93 ogg_term() {
94 ov_clear(&oggfile);
95 if (ogg_buf0) { free(ogg_buf0); ogg_buf0= 0; }
96 looper_term();
97 }
98
99 int
100 ogg_read(int *dst, int dlen) {
101 int *dst0= dst;
102 int *dst1= dst + dlen;
103
104 while (dst < dst1) {
bits_of(const OnigCodePoint c,const int n)105 int rv, sect;
106
107 // Copy data from buffer
108 if (ogg_rd != ogg_end) {
109 while (ogg_rd != ogg_end && dst != dst1)
110 *dst++= *ogg_rd++ * ogg_mult;
bits_at(const OnigCodePoint * c,const int n)111 continue;
112 }
113
114 // Refill buffer
115 rv= ov_read(&oggfile, (char*)ogg_buf0, (ogg_buf1-ogg_buf0)*sizeof(short), §);
116 //debug("ov_read %d/%d", rv, (ogg_buf1-ogg_buf0)*sizeof(short));
code1_equal(const OnigCodePoint x,const OnigCodePoint y)117 if (rv < 0) {
118 warn("Recoverable error in Ogg stream ");
119 continue;
120 }
121 if (rv == 0) // EOF
122 return dst-dst0;
123 if (rv & 3)
code2_equal(const OnigCodePoint * x,const OnigCodePoint * y)124 error("UNEXPECTED: ov_read() returned a partial sample count: %d", rv);
125 ogg_rd= ogg_buf0;
126 ogg_end= ogg_buf0 + (rv/2);
127 }
128 return dst-dst0;
129 }
130
131 //
code3_equal(const OnigCodePoint * x,const OnigCodePoint * y)132 // Looping OGG decoding using Tremor (libvorbisidec)
133 //
134 // (c) 1999-2004 Jim Peters <jim@uazu.net>. All Rights Reserved.
135 // For latest version see http://sbagen.sf.net/ or
136 // http://uazu.net/sbagen/. Released under the GNU GPL version 2.
137 //
138 // This code preloads the OGG file into memory, and loops and
139 // cross-fades the audio in segments to provide an endless output
140 // stream.
141 //
142 // The type of cross-fading and looping for any particular OGG
143 // file is controlled by a spec-string embedded in the file with
144 // the tag "SBAGEN_LOOPER". This may be set using the standard
145 // OGG 'vorbiscomment' tool, for example:
146 //
147 // vorbiscomment -a -t "SBAGEN_LOOPER=s4-16 f0.2" in.ogg out.ogg
148 //
149 // The tag contains entries as follows, optionally separated by
150 // white space:
151 //
152 // s<dur> Set segment size to given duration in seconds
153 // s<min>-<max> Set segment size to randomly vary within given range
154 // f<dur> Set duration for cross-fades
155 // c<cnt> Number of channels: 1 or 2
156 // w<bool> Swap stereo on second channel? 0 no, or 1 yes
157 // d<min>-<max> Set part of OGG file data to use as source audio (in seconds)
158 // #<digits> Following settings apply only to section <digits>
159 //
160 // The default is something like "s99999999f1c1w1d0-99999999",
161 // i.e. segments the full length of the audio, taken out of the
162 // whole of the audio file, repeated forever with a 1 second
163 // cross-fade at start/end.
164 //
165 // The # part works as follows. The .ogg filename on the command
166 // line may be followed by #<digits>. If this is missing, it is
167 // equivalent to #0. This allows different groups of settings to
168 // be selected out of the SBAGEN_LOOPER string. As an example
169 // (spaced to make it more readable):
170 //
171 // SBAGEN_LOOPER=s4-16 #0 f0.2 #1 f0.5 #2 f1
172 //
173 // The initial part is read in all cases, and then the #0, #1 and
174 // #2 select different cross-fade times.
175 //
176
177 static char *data; // OGG data
178 static char *data_end; // OGG data end +1
179 static int datlen; // Length of OGG data in bytes (data_end-data)
180 static int datcnt; // Length of OGG data in samples
181 static int datcnt0; // Original datcnt value, before 'd' modifications
onigenc_unicode_is_code_ctype(OnigCodePoint code,unsigned int ctype,OnigEncoding enc ARG_UNUSED)182 static int datbase; // Base sample offset for seeking
183 static int datrate; // Sampling rate of file, even if the output rate has been overridden
184
185 static int fade_cnt; // Count for fade-in or fade-out in samples
186 static int seg0, seg1; // Segment size range in samples (min/max) including fade in/out
187 static int ch2; // Channel 2 mode: 0 off, 1 on
188 static int ch2_swap; // Channel 2 swapped-stereo? 0 off, 1 on
189 static uint del_amp; // Fade in/out delta in amplitude per sample
190
191 typedef struct {
192 OggVorbis_File ogg; // File
193 char *rd; // Current read position in 'data'
194 int off; // Sample-offset that we started playing at for this segment
195 int chan; // Which channel do we below to? 0 or 1; if 1 then ch2_swap applies
196 int mode; // Current mode: 0 inactive, 1 waiting, 2 fade-in, 3 hold, 4 fade-out
197 int cnt; // Current count-down
198 int cnt_all; // Total sample count for fade-in/hold/fade-out
199 uint amp; // Current amplitude 0-0xFFFFFFFF
200 uint del; // Current amplitude delta (i.e. increment)
onigenc_unicode_ctype_code_range(int ctype,const OnigCodePoint * ranges[])201 short *buf0, *buf1; // Buffer for samples, buf0 to buf1-1
202 short *b_rd, *b_end; // Buffer read position, end pos +1
203 } AStream;
204 AStream str[3]; // Three read streams from the data
205
206 // Simple/crude 16-bit pseudo-random number generator, from ZX Spectrum
207 static unsigned short zxrand_seed; // Current random seed
208 static int
209 zxrand_0_65536() {
210 zxrand_seed= (1 + (int)zxrand_seed) * 75 % 65537 - 1;
211 return zxrand_seed;
212 }
onigenc_utf16_32_get_ctype_code_range(OnigCtype ctype,OnigCodePoint * sb_out,const OnigCodePoint * ranges[],OnigEncoding enc ARG_UNUSED)213 static int // Returns crude pseudo-random value from 0 to mult-1
214 zxrand_0(int mult) {
215 long long tmp= mult;
216 tmp *= zxrand_0_65536();
217 tmp >>= 16;
218 return (int)tmp;
219 }
220 // Returns crude pseudo-random value from r0 to r1-1, or r0 if the range is invalid
221 static int
222 zxrand(int r0, int r1) {
223 if (r1 <= r0) return r0;
onigenc_unicode_property_name_to_ctype(OnigEncoding enc,const UChar * name,const UChar * end)224 return r0 + zxrand_0(r1-r0);
225 }
226
227 // Return random value out of multiple ranges. Each range is
228 // represented by two values (lo, hi), and it represents a range
229 // lo->(hi-1). It is fine if any range is invalid (i.e. 0 or negative
230 // length), as it will be ignored. However, if all ranges are
231 // invalid, then the default value 'def' is returned instead.
232 // 'fmt' is a string of format characters, indicating the arguments
233 // to follow:
234 // 'r' indicates a range (int lo, int hi), from lo->(hi-1)
235 // 'o' indicates a range (int lo, int hi) that will be used to
236 // limit all the following 'r' ranges
237 static int
238 zxrandM(int def, char *fmt, ...) {
239 va_list ap;
240 int cnt= 0;
241 int val;
242 char *p;
243 int olo, ohi; // Overall limiting range
244
245 va_start(ap, fmt);
246 olo= 0x80000000; ohi= 0x7FFFFFFF;
247 for (p= fmt; *p; p++) {
248 int lo= va_arg(ap, int);
249 int hi= va_arg(ap, int);
250 if (*p == 'o') { olo= lo; ohi= hi; }
251 else if (*p == 'r') {
252 if (lo < olo) lo= olo;
253 if (hi > ohi) hi= ohi;
254 if (hi-lo > 0) cnt += hi-lo;
255 } else
256 error("Bad zxrandM format: %s", fmt);
257 }
258 va_end(ap);
259
260 if (!cnt) return def;
261 val= zxrand_0(cnt);
262
263 va_start(ap, fmt);
264 olo= 0x80000000; ohi= 0x7FFFFFFF;
265 for (p= fmt; *p; p++) {
onigenc_unicode_mbc_case_fold(OnigEncoding enc,OnigCaseFoldType flag ARG_UNUSED,const UChar ** pp,const UChar * end,UChar * fold)266 int lo= va_arg(ap, int);
267 int hi= va_arg(ap, int);
268 if (*p == 'o') { olo= lo; ohi= hi; }
269 else if (*p == 'r') {
270 if (lo < olo) lo= olo;
271 if (hi > ohi) hi= ohi;
272 cnt= hi-lo;
273 if (cnt > 0) {
274 if (val < cnt) return lo + val;
275 val -= cnt;
276 }
277 }
278 }
279 va_end(ap);
280 return def;
281 }
282
283 // File buffer operations
284 size_t
285 oc_read(void *ptr, size_t size, size_t nmemb, void *datasource) {
286 AStream *ss= (AStream*)datasource;
287 int len= size * nmemb;
288 if (len > data_end - ss->rd)
289 len= data_end - ss->rd;
290 if (len) {
291 memcpy(ptr, ss->rd, len);
292 ss->rd += len;
293 }
294 return len;
295 }
296
297 int
298 oc_seek(void *datasource, ogg_int64_t offset, int whence) {
299 AStream *ss= (AStream*)datasource;
300 int pos= ss->rd - data;
301 switch (whence) {
302 case SEEK_SET:
303 pos= offset; break;
304 case SEEK_CUR:
305 pos += offset; break;
306 case SEEK_END:
307 pos= datlen += offset; break;
308 }
309 if (pos < 0) pos= 0;
310 if (pos > datlen) pos= datlen;
311 ss->rd= data + pos;
312 return pos;
313 }
314
315 int
316 oc_close(void *datasource) {
317 return 0;
onigenc_unicode_apply_all_case_fold(OnigCaseFoldType flag,OnigApplyAllCaseFoldFunc f,void * arg,OnigEncoding enc ARG_UNUSED)318 }
319
320 long
321 oc_tell(void *datasource) {
322 AStream *ss= (AStream*)datasource;
323 return ss->rd - data;
324 }
325
326 // Initialise all
327 void
328 looper_init() {
329 vorbis_info *vi;
330 int a;
331 ov_callbacks oc;
332 vorbis_comment *vc;
333 char *looper;
334 int prev_flag= 0;
335 int on;
336
337 // Init random seed
338 zxrand_seed= 0xFFFF & time(NULL);
339
340 // Find the length of the OGG file and load it into memory
341 if (0 != fseek(mix_in, 0, SEEK_END) ||
342 0 > (datlen= ftell(mix_in)))
343 error("Can't determine length of OGG file: %s", strerror(errno));
344
345 data= ALLOC_ARR(datlen, char);
346 data_end= data + datlen;
347 if (0 != fseek(mix_in, 0, SEEK_SET) ||
348 1 != fread(data, datlen, 1, mix_in))
349 error("Can't read loopable OGG file into memory: %s", strerror(errno));
350
351 // Open each of the three OGG streamers
352 oc.read_func= oc_read;
353 oc.seek_func= oc_seek;
354 oc.close_func= oc_close;
355 oc.tell_func= oc_tell;
356 for (a= 0; a<3; a++) {
357 AStream *aa= &str[a];
358 aa->rd= data;
359 aa->mode= 0;
360 aa->buf0= ALLOC_ARR(2048, short);
361 aa->buf1= aa->buf0 + 2048;
362 aa->b_rd= aa->b_end= aa->buf0;
363 if (0 > ov_open_callbacks(aa, &aa->ogg, NULL, 0, oc))
364 error("Problem opening OGG bitstream");
365 }
366
367 // Find the total length of the file
368 datcnt= ov_pcm_total(&str[0].ogg, -1);
369 datcnt0= datcnt;
370 datbase= 0;
371
372 // Pick up sampling rate and override default if -r not used
373 vi= ov_info(&str[0].ogg, -1);
374 datrate= vi->rate;
375 if (out_rate_def) out_rate= vi->rate;
376 out_rate_def= 0;
377
378 // Find the SBAGEN_LOOPER tag
379 looper= "";
380 vc= ov_comment(&str[0].ogg, -1);
381 for (a= 0; a<vc->comments; a++)
382 if (0 == memcmp(vc->user_comments[a], "SBAGEN_LOOPER=", 14))
383 looper= vc->user_comments[a] + 14;
384
385 // Setup info from SBAGEN_LOOPER tag
386 seg0= seg1= datcnt;
387 fade_cnt= datrate;
388 ch2= 0;
389 ch2_swap= 1;
390 on= 1;
391 if (mix_cnt < 0) mix_cnt= 0;
392 while (*looper) {
393 char flag, *p;
394 double val;
395
396 flag= *looper++;
397 if (isspace(flag)) continue;
398 if (!strchr("s-fcwd#", flag)) {
399 warn("Bad SBAGEN_LOOPER flag: %c", flag);
400 continue;
401 }
402 if (flag == '-') switch (prev_flag) {
403 case 's': flag= 'S'; break;
404 case 'd': flag= 'D'; break;
405 default:
406 warn("SBAGEN_LOOPER '-' found not in form s<val>-<val>");
407 continue;
408 }
409 prev_flag= flag;
410
411 val= strtod(looper, &p);
412 if (p == looper) {
413 warn("Bad SBAGEN_LOOPER value for flag '%c': %s", flag, p);
414 continue;
415 }
416 looper= p;
417
418 if (flag == '#')
419 on= (val == mix_cnt);
420 else if (on) switch (flag) {
421 case 's': seg0= seg1= val * datrate; break;
422 case 'S': seg1= val * datrate; break;
423 case 'd': datbase= val * datrate; datcnt= datcnt0 - datbase; break;
424 case 'D': datcnt= val * datrate - datbase; break;
425 case 'f': fade_cnt= val * datrate; break;
426 case 'c': ch2= val > 1.5; break;
427 case 'w': ch2_swap= val > 0.5; break;
428 }
429 }
430
431 // Tidy up LOOPER settings
432 if (fade_cnt < datrate/50) fade_cnt= datrate/50; // 20ms min fade
433 if (datcnt + datbase > datcnt0) datcnt= datcnt0-datbase;
434 if (datcnt < 0)
435 error("Source data range invalid in SBAGEN_LOOPER settings");
436 if (datcnt <= 3 * fade_cnt)
437 error("Length of source data 'd' too short for fade-length of %gs\n"
438 " in SBAGEN_LOOPER settings", fade_cnt * 1.0 / datrate);
439 if (seg0 > datcnt) seg0= datcnt;
440 if (seg1 > datcnt) seg1= datcnt;
441 if (seg0 > seg1) seg0= seg1;
442 if (seg0 < 3 * fade_cnt) {
443 seg0= 3 * fade_cnt;
444 warn("SBAGEN_LOOPER segment size too short for fade-length of %gs; adjusted.",
445 fade_cnt * 1.0 / datrate);
446 }
447 if (seg1 < seg0) seg1= seg0;
448
449 // Calculate delta to use for fades
450 del_amp= 0xFFFFFFFFU/fade_cnt; // Rely on rounding down here
451 if (del_amp * (uint)fade_cnt < 0xF0000000) // Paranoid check
452 error("Internal rounding error in calculating amplitude delta");
453 if (ch2) del_amp >>= 1;
onigenc_unicode_get_case_fold_codes_by_str(OnigEncoding enc,OnigCaseFoldType flag,const OnigUChar * p,const OnigUChar * end,OnigCaseFoldCodeItem items[])454
455 // debug("Segment range %d-%d, fade %d, amp delta per sample %d",
456 // seg0, seg1, fade_cnt, del_amp);
457
458 // Init three streams and start off
459 looper_sched();
460
461 // Start a thread to handle generation of the mix stream
462 inbuf_start(looper_read, 256*1024); // 1024K buffer: 3s@44.1kHz
463 }
464
465 void
466 looper_term() {
467 int a;
468 for (a= 0; a<3; a++) {
469 ov_clear(&str[a].ogg);
470 free(str[a].buf0);
471 }
472 }
473
474 int
475 looper_read(int *dst, int dlen) {
476 int *dst0= dst;
477 int *dst1= dst + dlen;
478
479 // Clear the whole thing
480 memset(dst0, 0, (char*)dst1 - (char*)dst0);
481
482 // Go through in chunks
483 while (dst0 < dst1) {
484 int a;
485 int len= (dst1-dst0)/2;
486 int resched= 0;
487
488 for (a= 0; a<3; a++) {
489 AStream *aa= &str[a];
490 if (aa->mode && aa->cnt < len)
491 len= aa->cnt;
492 }
493
494 // Process 'len' samples in each channel
495 for (a= 0; a<3; a++) {
496 AStream *aa= &str[a];
497 int cnt= len;
498
499 if (!aa->mode) continue;
500
501 dst= dst0;
502 while (cnt > 0) {
503 // Refill buffer if necessary
504 if (aa->mode > 1 &&
505 aa->b_rd == aa->b_end) {
506 int sect;
507 char *buf= (char*)aa->buf0;
508 int len= (aa->buf1-aa->buf0)*sizeof(aa->buf0[0]);
509 int rv= ov_read(&aa->ogg, buf, len, §);
510 if (rv < 0) {
511 warn("Recoverable error in Ogg stream ");
512 continue;
513 }
514 if (rv == 0) { // EOF; internal error?
515 warn("Hit EOF in looping OGG stream, filling with zeros");
516 memset(buf, 0, len);
517 rv= len;
518 }
519 if (rv & 3)
520 error("UNEXPECTED: ov_read() returned a partial sample count: %d", rv);
521 aa->b_rd= aa->buf0;
522 aa->b_end= aa->buf0 + (rv/2);
523 }
524
525 // Waiting to start playing
526 if (aa->mode == 1) {
527 aa->cnt -= cnt;
528 cnt= 0;
529 continue;
530 }
531
532 // Playing fade-in / main part / fade-out
533 if (aa->chan && ch2_swap) {
534 // Output with swapped L+R
535 while (cnt > 0 && aa->b_rd != aa->b_end) {
536 uint amp= (~aa->amp) >> 16; amp= (~(amp*amp))>>21; amp *= ogg_mult;
537 cnt--;
538 *dst++ += ((int)(aa->b_rd[1] * amp)) >> 11;
539 *dst++ += ((int)(aa->b_rd[0] * amp)) >> 11;
540 aa->b_rd += 2;
541 aa->amp += aa->del;
542 aa->cnt--;
543 }
544 } else {
545 // Output with normal L+R
546 while (cnt > 0 && aa->b_rd != aa->b_end) {
547 uint amp= (~aa->amp) >> 16; amp= (~(amp*amp))>>21; amp *= ogg_mult;
548 cnt--;
549 *dst++ += ((int)(*aa->b_rd++ * amp)) >> 11;
550 *dst++ += ((int)(*aa->b_rd++ * amp)) >> 11;
551 aa->amp += aa->del;
552 aa->cnt--;
553 }
554 }
555 }
556
557 if (!aa->cnt) switch (aa->mode) {
558 case 1:
559 aa->mode= 2;
560 aa->cnt= fade_cnt;
561 aa->del= del_amp;
562 break;
563 case 2:
564 aa->mode= 3;
565 aa->cnt= aa->cnt_all - 2 * fade_cnt;
566 aa->del= 0;
567 break;
568 case 3:
569 aa->mode= 4;
570 aa->cnt= fade_cnt;
571 aa->del= -del_amp;
572 break;
573 case 4:
574 aa->mode= 0;
575 resched= 1;
576 break;
577 }
578 }
579
580 dst0 += len*2;
581
582 // Schedule in another section if necessary
583 if (resched) looper_sched();
584 }
585
586 return dlen; // Always returns a full buffer
587 }
588
589 // Calculate sample-count before end of playback on this channel
590 #define CNT_TO_END(aa) \
591 (aa->mode == 1 ? aa->cnt_all + aa->cnt : \
592 aa->mode == 2 ? aa->cnt_all - fade_cnt + aa->cnt : \
593 aa->mode == 3 ? fade_cnt + aa->cnt : \
594 aa->mode == 4 ? aa->cnt : 0)
595
596 //
597 // Schedule in the next segment or segments
598 //
599
600 static void
601 looper_sched() {
602 int update= 0;
603
604 // Handle 2-channel separately
605 if (ch2) {
606 looper_sched2();
607 return;
608 }
609
610 while (1) {
611 AStream *aa= &str[0];
612 AStream *bb= &str[1];
613 int cnt_all, rv;
614
615 // Complete when both are set up ready
616 if (aa->mode && bb->mode) break;
617 update= 1;
618
619 // Make 'bb' the one we are filling in
620 if (bb->mode && !aa->mode) {
621 AStream *tmp= aa; aa= bb; bb= tmp;
622 }
623
624 // Make 'aa' zero if it is not active
625 if (!aa->mode) aa= 0;
626
627 // Setup a new segment for 'bb'
628 bb->off= -1;
629 bb->mode= 1;
630 bb->cnt= 0;
631 bb->amp= 0;
632 bb->b_rd= bb->b_end= bb->buf0;
633
634 // Position segment to follow on from segment currently playing
635 // on 'aa'
636 if (aa) {
637 bb->cnt= CNT_TO_END(aa) - fade_cnt;
638 if (bb->cnt < 0) bb->cnt= 0;
639 }
640
641 // Select the segment length
642 bb->cnt_all= cnt_all= zxrand(seg0, seg1+1);
643
644 // Look for a segment starting-point which doesn't overlap the
645 // segment playing on the other channel. First range below is
646 // before 'aa' segment, second is after 'aa' segment.
647 if (aa) {
648 bb->off= zxrandM(-1, "rr", // Default -1 if no range is available
649 0, aa->off - cnt_all,
650 aa->off + aa->cnt_all, datcnt - cnt_all);
651 }
652
653 // If we haven't been able to work around 'aa', or 'aa' is not
654 // active, then just randomly choose a position
655 if (bb->off < 0)
656 bb->off= zxrand(0, datcnt - cnt_all);
657
onigenc_unicode_case_map(OnigCaseFoldType * flagP,const OnigUChar ** pp,const OnigUChar * end,OnigUChar * to,OnigUChar * to_end,const struct OnigEncodingTypeST * enc)658 // Reseek the OGG stream
659 rv= ov_pcm_seek(&bb->ogg, datbase + bb->off);
660 if (rv) error("UNEXPECTED: Can't reseek the looping OGG stream: %d", rv);
661 }
662
663 // //DEBUG
664 // if (update) {
665 // int a;
666 // for (a= 0; a<2; a++) {
667 // AStream *aa= &str[a];
668 // printf("STR%d ", a);
669 // if (!aa->mode) { printf("off\n"); continue; }
670 // printf("mode %d, cnt %d, cnt_all %d\n",
671 // aa->mode, aa->cnt, aa->cnt_all);
672 // }
673 // }
674 }
675
676 // This is for two tracks playing constantly
677 static void
678 looper_sched2() {
679 while (1) {
680 AStream *aa= &str[0];
681 AStream *bb= &str[1];
682 AStream *cc= &str[2];
683 AStream *tmp;
684 int cnt_all, rv;
685
686 // Complete when all are ready
687 if (aa->mode && bb->mode && cc->mode) break;
688
689 // Put the used slots in order: aa first, then bb, then cc
690 if (!aa->mode && bb->mode) {
691 tmp= aa; aa= bb; bb= tmp;
692 }
693 if (!aa->mode && cc->mode) {
694 tmp= aa; aa= cc; cc= tmp;
695 }
696 if (!bb->mode && cc->mode) {
697 tmp= bb; bb= cc; cc= tmp;
698 }
699
700 // If there are only 0 or 1 in use, we can add a new one on the
701 // other channel, so long as its fade-out time doesn't clash
702 // with 'aa', if active.
703 if (!bb->mode) {
704 // Setup a new segment for 'bb'
705 bb->chan= aa ? !aa->chan : 0;
706 bb->off= -1;
707 bb->mode= 1;
708 bb->amp= 0;
709 bb->cnt= 0;
710 bb->b_rd= bb->b_end= bb->buf0;
711
712 // Select the segment length and start-point, avoiding a
713 // length that would cause a clash with 'aa's fade-out
714 if (!aa) {
715 cnt_all= zxrand(seg0, seg1+1);
716 } else {
717 int end= CNT_TO_END(aa); // Count before aa fade-out end
718 cnt_all= zxrandM(-1, "orr",
719 seg0, seg1+1, // Overall limit to valid range seg0->seg1
720 seg0, end-fade_cnt, // To fit before the 'aa' fade-out
721 end+fade_cnt, seg1+1); // To fit after
722
723 // If -1 then we can't fit it in with the provided
724 // segment-length range, so start late instead
725 if (cnt_all < 0) {
726 bb->cnt= end+fade_cnt - seg1;
727 cnt_all= seg1;
728 }
729 }
730 bb->cnt_all= cnt_all;
731 if (bb->cnt < 0) bb->cnt= 0;
732
733 // Look for a segment starting-point which doesn't overlap
734 // the segment playing on the other channel, or if that
735 // didn't work, just choose a random position
736 if (aa) {
737 bb->off= zxrandM(-1, "rr", // Default -1 if no range is available
738 0, aa->off - cnt_all,
739 aa->off + aa->cnt_all, datcnt - cnt_all);
740 }
741 if (bb->off < 0)
742 bb->off= zxrand(0, datcnt - cnt_all);
743
744 // Reseek the OGG stream
745 rv= ov_pcm_seek(&bb->ogg, datbase + bb->off);
746 if (rv) error("UNEXPECTED: Can't reseek the looping OGG stream: %d", rv);
747 continue;
748 }
749
750 // Okay, so we have 'aa' and 'bb' and we need to fill in 'cc'.
751 // 'aa' and 'bb' should be on different channels. We need to
752 // create a 'cc' that continues where the shorter of 'aa' and
753 // 'bb' leaves off.
754 if (aa->chan == bb->chan)
755 error("UNEXPECTED: internal error, looper_sched2(), aa/bb on same chan");
756
757 // Make 'aa' the shorter one
758 if (CNT_TO_END(aa) > CNT_TO_END(bb)) {
759 tmp= aa; aa= bb; bb= tmp;
760 }
761
762 // Setup a new segment for 'cc'
763 cc->chan= aa->chan;
764 cc->cnt= CNT_TO_END(aa) - fade_cnt;
765 cc->off= -1;
766 cc->mode= 1;
767 cc->amp= 0;
768 cc->b_rd= cc->b_end= cc->buf0;
769
770 // Select the segment length and start-point, avoiding a
771 // length that would cause a clash with 'bb's fade-out
772 if (!bb) {
773 cnt_all= zxrand(seg0, seg1+1);
774 } else {
775 int end= CNT_TO_END(bb); // Count before bb fade-out end
776 end -= cc->cnt; // Take account of the offset we are starting at
777 cnt_all= zxrandM(-1, "orr",
778 seg0, seg1+1, // Overall limit to valid range seg0->seg1
779 seg0, end-fade_cnt, // To fit before the 'bb' fade-out
780 end+fade_cnt, seg1+1); // To fit after
781
782 // If -1 then we can't fit it in with the provided
783 // segment-length range, so go shorter or longer
784 if (cnt_all < 0) {
785 if (end-fade_cnt > fade_cnt * 2)
786 cnt_all= end-fade_cnt;
787 else
788 cnt_all= end+fade_cnt; // Might overflow datcnt, so this is last resort
789 }
790 }
791 cc->cnt_all= cnt_all;
792
793 // Look for a segment starting-point which doesn't overlap
794 // either of the segments playing on the other two channels
795 {
796 int r0= aa->off;
797 int r1= aa->off + aa->cnt_all;
798 int r2= bb->off;
799 int r3= bb->off + bb->cnt_all;
800
801 if (r0 > r2) {
802 int tmp;
803 tmp= r0; r0= r2; r2= tmp;
804 tmp= r1; r1= r3; r3= tmp;
805 }
806
807 cc->off= zxrandM(-1, "rrr", // Default -1 if no range is available
808 0, r0 - cnt_all,
809 r1, r2 - cnt_all,
810 r3, datcnt - cnt_all);
811 }
812
813 // Failing that, choose a segment at random
814 if (cc->off < 0)
815 cc->off= zxrand(0, datcnt - cnt_all);
816
817 // Reseek the OGG stream
818 rv= ov_pcm_seek(&cc->ogg, datbase + cc->off);
819 if (rv) error("UNEXPECTED: Can't reseek the looping OGG stream: %d", rv);
820 continue;
821 }
822 }
823
824 // END //
825