1 /* aylet 0.3, a .AY music file player.
2 * Copyright (C) 2001-2002 Russell Marks and Ian Collier.
3 * See main.c for licence.
4 *
5 * sound.c - the sound emulation itself, based on the beeper/AY code I
6 * wrote for Fuse.
7 */
8
9 /* some AY details (volume levels, white noise RNG algorithm) based on
10 * info from MAME's ay8910.c - MAME's licence explicitly permits free
11 * use of info (even encourages it).
12 */
13
14 /* NB: I know some of this stuff looks fairly CPU-hogging.
15 * For example, the AY code tracks changes with sub-frame timing
16 * in a rather hairy way, and there's subsampling here and there.
17 * But if you measure the CPU use, it doesn't actually seem
18 * very high at all. And I speak as a Cyrix owner. :-)
19 *
20 * (I based that on testing in Fuse, but I doubt it's that much
21 * worse here. (It's actually a bit better, I think.))
22 */
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <unistd.h>
29
30 #include "main.h"
31 #include "z80.h"
32
33 #include "sound.h"
34 #include "driver.h"
35
36 /* configuration */
37 int soundfd=-1; /* file descriptor for the sound device */
38 int sixteenbit=1; /* use sixteen-bit audio? */
39
40 int sound_enabled=0;
41 int sound_freq=44100;
42 int sound_stereo=1; /* true for stereo *output sample* (only) */
43 int sound_stereo_beeper=0; /* beeper pseudo-stereo */
44 int sound_stereo_ay=1; /* AY stereo separation */
45 int sound_stereo_ay_abc=0; /* (AY stereo) true for ABC stereo, else ACB */
46 int sound_stereo_ay_narrow=0; /* (AY stereo) true for narrow AY st. sep. */
47
48
49 #define AY_CLOCK 1773400
50 #define AY_CLOCK_CPC 1000000
51
52
53 /* assume all three tone channels together match the beeper volume.
54 * (XXX maybe not - that makes beeper stuff annoyingly loud)
55 * Must be <=127 for all channels; 40+(28*3) = 124.
56 * (Now scaled up for 16-bit.)
57 */
58 #define AMPL_BEEPER (40*256)
59 #define AMPL_AY_TONE (28*256) /* three of these */
60
61 /* full range of beeper volume */
62 #define VOL_BEEPER (AMPL_BEEPER*2)
63
64 /* max. number of sub-frame AY port writes allowed;
65 * given the number of port writes theoretically possible in a
66 * 50th I think this should be plenty.
67 */
68 #define AY_CHANGE_MAX 8000
69
70 static int sound_framesiz;
71
72 static unsigned int ay_tone_levels[16];
73
74 static signed short *sound_buf;
75 static int sound_oldpos,sound_fillpos,sound_oldval,sound_oldval_orig;
76
77 /* foo_subcycles are fixed-point with low 16 bits as fractional part.
78 * The other bits count as the chip does.
79 */
80 static unsigned int ay_tone_tick[3],ay_tone_high[3],ay_noise_tick;
81 static unsigned int ay_tone_subcycles,ay_env_subcycles;
82 static unsigned int ay_env_internal_tick,ay_env_tick;
83 static unsigned int ay_tick_incr;
84 static unsigned int ay_tone_period[3],ay_noise_period,ay_env_period;
85
86 static int beeper_last_subpos=0;
87
88 /* AY registers */
89 /* we have 16 so we can fake an 8910 if needed (XXX any point?) */
90 static unsigned char sound_ay_registers[16];
91
92 struct ay_change_tag
93 {
94 unsigned long tstates;
95 unsigned short ofs;
96 unsigned char reg,val;
97 };
98
99 static struct ay_change_tag ay_change[AY_CHANGE_MAX];
100 static int ay_change_count;
101
102
103 static int fading=0,fadetotal;
104 static int sfadetime;
105
106 #define STEREO_BUF_SIZE 1024
107
108 static int pstereobuf[STEREO_BUF_SIZE];
109 static int pstereobufsiz,pstereopos;
110 static int psgap=250;
111 static int rstereobuf_l[STEREO_BUF_SIZE],rstereobuf_r[STEREO_BUF_SIZE];
112 static int rstereopos,rchan1pos,rchan2pos,rchan3pos;
113
114
115
sound_ay_init(void)116 void sound_ay_init(void)
117 {
118 /* AY output doesn't match the claimed levels; these levels are based
119 * on the measurements posted to comp.sys.sinclair in Dec 2001 by
120 * Matthew Westcott, adjusted as I described in a followup to his post,
121 * then scaled to 0..0xffff.
122 */
123 static int levels[16]=
124 {
125 0x0000, 0x0385, 0x053D, 0x0770,
126 0x0AD7, 0x0FD5, 0x15B0, 0x230C,
127 0x2B4C, 0x43C1, 0x5A4B, 0x732F,
128 0x9204, 0xAFF1, 0xD921, 0xFFFF
129 };
130 int f;
131
132 /* scale the values down to fit */
133 for(f=0;f<16;f++)
134 ay_tone_levels[f]=(levels[f]*AMPL_AY_TONE+0x8000)/0xffff;
135
136 ay_noise_tick=ay_noise_period=0;
137 ay_env_internal_tick=ay_env_tick=ay_env_period=0;
138 ay_tone_subcycles=ay_env_subcycles=0;
139 for(f=0;f<3;f++)
140 ay_tone_tick[f]=ay_tone_high[f]=0,ay_tone_period[f]=1;
141
142 #define CLOCK_RESET(clock) ay_tick_incr=(int)(65536.*clock/sound_freq)
143
144 CLOCK_RESET(AY_CLOCK);
145
146 ay_change_count=0;
147 }
148
149
sound_init(void)150 int sound_init(void)
151 {
152 int f;
153
154 if(!driver_init(&sound_freq,&sound_stereo))
155 return(0);
156
157 /* important to override these if not using stereo */
158 if(!sound_stereo)
159 {
160 sound_stereo_ay=0;
161 sound_stereo_beeper=0;
162 }
163
164 sound_enabled=1;
165 sound_framesiz=sound_freq/50;
166
167 if((sound_buf=malloc(sizeof(signed short)*sound_framesiz*(sound_stereo+1)))==NULL)
168 {
169 sound_end();
170 return(0);
171 }
172
173 sound_oldval=sound_oldval_orig=0;
174 sound_oldpos=-1;
175 sound_fillpos=0;
176
177 sound_ay_init();
178
179 if(sound_stereo_beeper)
180 {
181 for(f=0;f<STEREO_BUF_SIZE;f++)
182 pstereobuf[f]=0;
183 pstereopos=0;
184 pstereobufsiz=(sound_freq*psgap)/22000;
185 }
186
187 if(sound_stereo_ay)
188 {
189 int pos=(sound_stereo_ay_narrow?3:6)*sound_freq/8000;
190
191 for(f=0;f<STEREO_BUF_SIZE;f++)
192 rstereobuf_l[f]=rstereobuf_r[f]=0;
193 rstereopos=0;
194
195 /* the actual ACB/ABC bit :-) */
196 rchan1pos=-pos;
197 if(sound_stereo_ay_abc)
198 rchan2pos=0, rchan3pos=pos;
199 else
200 rchan2pos=pos,rchan3pos=0;
201 }
202
203 return(1);
204 }
205
206
sound_end(void)207 void sound_end(void)
208 {
209 if(sound_enabled)
210 {
211 if(sound_buf)
212 free(sound_buf);
213 driver_end();
214 sound_enabled=0;
215 }
216 }
217
218
219 /* write sample to buffer as pseudo-stereo */
sound_write_buf_pstereo(signed short * out,int c)220 void sound_write_buf_pstereo(signed short *out,int c)
221 {
222 int bl=(c-pstereobuf[pstereopos])/2;
223 int br=(c+pstereobuf[pstereopos])/2;
224
225 if(bl<-AMPL_BEEPER) bl=-AMPL_BEEPER;
226 if(br<-AMPL_BEEPER) br=-AMPL_BEEPER;
227 if(bl> AMPL_BEEPER) bl= AMPL_BEEPER;
228 if(br> AMPL_BEEPER) br= AMPL_BEEPER;
229
230 *out=bl; out[1]=br;
231
232 pstereobuf[pstereopos]=c;
233 pstereopos++;
234 if(pstereopos>=pstereobufsiz)
235 pstereopos=0;
236 }
237
238
239
240 /* not great having this as a macro to inline it, but it's only
241 * a fairly short routine, and it saves messing about.
242 * (XXX ummm, possibly not so true any more :-))
243 */
244 #define AY_GET_SUBVAL(chan) (level*2*ay_tone_tick[chan]/tone_count)
245
246 #define AY_DO_TONE(var,chan) \
247 is_low=0; \
248 if(is_on) \
249 { \
250 (var)=0; \
251 if(level) \
252 { \
253 if(ay_tone_high[chan]) \
254 (var)= (level); \
255 else \
256 (var)=-(level),is_low=1; \
257 } \
258 } \
259 \
260 ay_tone_tick[chan]+=tone_count; \
261 count=0; \
262 while(ay_tone_tick[chan]>=ay_tone_period[chan]) \
263 { \
264 count++; \
265 ay_tone_tick[chan]-=ay_tone_period[chan]; \
266 ay_tone_high[chan]=!ay_tone_high[chan]; \
267 \
268 /* has to be here, unfortunately... */ \
269 if(is_on && count==1 && level && ay_tone_tick[chan]<tone_count) \
270 { \
271 if(is_low) \
272 (var)+=AY_GET_SUBVAL(chan); \
273 else \
274 (var)-=AY_GET_SUBVAL(chan); \
275 } \
276 } \
277 \
278 /* if it's changed more than once during the sample, we can't */ \
279 /* represent it faithfully. So, just hope it's a sample. */ \
280 /* (That said, this should also help avoid aliasing noise.) */ \
281 if(is_on && count>1) \
282 (var)=-(level)
283
284
285 /* add val, correctly delayed on either left or right buffer,
286 * to add the AY stereo positioning. This doesn't actually put
287 * anything directly in soundbuf, though.
288 */
289 #define GEN_STEREO(pos,val) \
290 if((pos)<0) \
291 { \
292 rstereobuf_l[rstereopos]+=(val); \
293 rstereobuf_r[(rstereopos-pos)%STEREO_BUF_SIZE]+=(val); \
294 } \
295 else \
296 { \
297 rstereobuf_l[(rstereopos+pos)%STEREO_BUF_SIZE]+=(val); \
298 rstereobuf_r[rstereopos]+=(val); \
299 }
300
301
302
303 /* bitmasks for envelope */
304 #define AY_ENV_CONT 8
305 #define AY_ENV_ATTACK 4
306 #define AY_ENV_ALT 2
307 #define AY_ENV_HOLD 1
308
309
sound_ay_overlay(void)310 static void sound_ay_overlay(void)
311 {
312 static int rng=1;
313 static int noise_toggle=0;
314 static int env_first=1,env_rev=0,env_counter=15;
315 int tone_level[3];
316 int mixer,envshape;
317 int f,g,level,count;
318 signed short *ptr;
319 struct ay_change_tag *change_ptr=ay_change;
320 int changes_left=ay_change_count;
321 int reg,r;
322 int is_low,is_on;
323 int chan1,chan2,chan3;
324 int frametime=tsmax*50;
325 unsigned int tone_count,noise_count;
326
327 /* convert change times to sample offsets */
328 for(f=0;f<ay_change_count;f++)
329 ay_change[f].ofs=(ay_change[f].tstates*sound_freq)/frametime;
330
331 for(f=0,ptr=sound_buf;f<sound_framesiz;f++)
332 {
333 /* update ay registers. All this sub-frame change stuff
334 * is pretty hairy, but how else would you handle the
335 * samples in Robocop? :-) It also clears up some other
336 * glitches.
337 */
338 while(changes_left && f>=change_ptr->ofs)
339 {
340 sound_ay_registers[reg=change_ptr->reg]=change_ptr->val;
341 change_ptr++; changes_left--;
342
343 /* fix things as needed for some register changes */
344 switch(reg)
345 {
346 case 0: case 1: case 2: case 3: case 4: case 5:
347 r=reg>>1;
348 /* a zero-len period is the same as 1 */
349 ay_tone_period[r]=(sound_ay_registers[reg&~1]|
350 (sound_ay_registers[reg|1]&15)<<8);
351 if(!ay_tone_period[r])
352 ay_tone_period[r]++;
353
354 /* important to get this right, otherwise e.g. Ghouls 'n' Ghosts
355 * has really scratchy, horrible-sounding vibrato.
356 */
357 if(ay_tone_tick[r]>=ay_tone_period[r]*2)
358 ay_tone_tick[r]%=ay_tone_period[r]*2;
359 break;
360 case 6:
361 ay_noise_tick=0;
362 ay_noise_period=(sound_ay_registers[reg]&31);
363 break;
364 case 11: case 12:
365 /* this one *isn't* fixed-point */
366 ay_env_period=sound_ay_registers[11]|(sound_ay_registers[12]<<8);
367 break;
368 case 13:
369 ay_env_internal_tick=ay_env_tick=ay_env_subcycles=0;
370 env_first=1;
371 env_rev=0;
372 env_counter=(sound_ay_registers[13]&AY_ENV_ATTACK)?0:15;
373 break;
374 }
375 }
376
377 /* the tone level if no enveloping is being used */
378 for(g=0;g<3;g++)
379 tone_level[g]=ay_tone_levels[sound_ay_registers[8+g]&15];
380
381 /* envelope */
382 envshape=sound_ay_registers[13];
383 level=ay_tone_levels[env_counter];
384
385 for(g=0;g<3;g++)
386 if(sound_ay_registers[8+g]&16)
387 tone_level[g]=level;
388
389 /* envelope output counter gets incr'd every 16 AY cycles.
390 * Has to be a while, as this is sub-output-sample res.
391 */
392 ay_env_subcycles+=ay_tick_incr;
393 noise_count=0;
394 while(ay_env_subcycles>=(16<<16))
395 {
396 ay_env_subcycles-=(16<<16);
397 noise_count++;
398 ay_env_tick++;
399 while(ay_env_tick>=ay_env_period)
400 {
401 ay_env_tick-=ay_env_period;
402
403 /* do a 1/16th-of-period incr/decr if needed */
404 if(env_first ||
405 ((envshape&AY_ENV_CONT) && !(envshape&AY_ENV_HOLD)))
406 {
407 if(env_rev)
408 env_counter-=(envshape&AY_ENV_ATTACK)?1:-1;
409 else
410 env_counter+=(envshape&AY_ENV_ATTACK)?1:-1;
411 if(env_counter<0) env_counter=0;
412 if(env_counter>15) env_counter=15;
413 }
414
415 ay_env_internal_tick++;
416 while(ay_env_internal_tick>=16)
417 {
418 ay_env_internal_tick-=16;
419
420 /* end of cycle */
421 if(!(envshape&AY_ENV_CONT))
422 env_counter=0;
423 else
424 {
425 if(envshape&AY_ENV_HOLD)
426 {
427 if(env_first && (envshape&AY_ENV_ALT))
428 env_counter=(env_counter?0:15);
429 }
430 else
431 {
432 /* non-hold */
433 if(envshape&AY_ENV_ALT)
434 env_rev=!env_rev;
435 else
436 env_counter=(envshape&AY_ENV_ATTACK)?0:15;
437 }
438 }
439
440 env_first=0;
441 }
442
443 /* don't keep trying if period is zero */
444 if(!ay_env_period) break;
445 }
446 }
447
448 /* generate tone+noise... or neither.
449 * (if no tone/noise is selected, the chip just shoves the
450 * level out unmodified. This is used by some sample-playing
451 * stuff.)
452 */
453 chan1=tone_level[0];
454 chan2=tone_level[1];
455 chan3=tone_level[2];
456 mixer=sound_ay_registers[7];
457
458 ay_tone_subcycles+=ay_tick_incr;
459 tone_count=ay_tone_subcycles>>(3+16);
460 ay_tone_subcycles&=(8<<16)-1;
461
462 level=chan1; is_on=!(mixer&1);
463 AY_DO_TONE(chan1,0);
464 if((mixer&0x08)==0 && noise_toggle)
465 chan1=0;
466
467 level=chan2; is_on=!(mixer&2);
468 AY_DO_TONE(chan2,1);
469 if((mixer&0x10)==0 && noise_toggle)
470 chan2=0;
471
472 level=chan3; is_on=!(mixer&4);
473 AY_DO_TONE(chan3,2);
474 if((mixer&0x20)==0 && noise_toggle)
475 chan3=0;
476
477 /* write the sample(s) */
478 if(!sound_stereo)
479 {
480 /* mono */
481 (*ptr++)+=chan1+chan2+chan3;
482 }
483 else
484 {
485 if(!sound_stereo_ay)
486 {
487 /* stereo output, but mono AY sound; still,
488 * incr separately in case of beeper pseudostereo.
489 */
490 (*ptr++)+=chan1+chan2+chan3;
491 (*ptr++)+=chan1+chan2+chan3;
492 }
493 else
494 {
495 /* stereo with ACB AY positioning.
496 * Here we use real stereo positions for the channels.
497 * Just because, y'know, it's cool and stuff. No, really. :-)
498 * This is a little tricky, as it works by delaying sounds
499 * on the left or right channels to model the delay you get
500 * in the real world when sounds originate at different places.
501 */
502 GEN_STEREO(rchan1pos,chan1);
503 GEN_STEREO(rchan2pos,chan2);
504 GEN_STEREO(rchan3pos,chan3);
505 (*ptr++)+=rstereobuf_l[rstereopos];
506 (*ptr++)+=rstereobuf_r[rstereopos];
507 rstereobuf_l[rstereopos]=rstereobuf_r[rstereopos]=0;
508 rstereopos++;
509 if(rstereopos>=STEREO_BUF_SIZE)
510 rstereopos=0;
511 }
512 }
513
514 /* update noise RNG/filter */
515 ay_noise_tick+=noise_count;
516 while(ay_noise_tick>=ay_noise_period)
517 {
518 ay_noise_tick-=ay_noise_period;
519
520 if((rng&1)^((rng&2)?1:0))
521 noise_toggle=!noise_toggle;
522
523 /* rng is 17-bit shift reg, bit 0 is output.
524 * input is bit 0 xor bit 2.
525 */
526 rng|=((rng&1)^((rng&4)?1:0))?0x20000:0;
527 rng>>=1;
528
529 /* don't keep trying if period is zero */
530 if(!ay_noise_period) break;
531 }
532 }
533 }
534
535
536 /* don't make the change immediately; record it for later,
537 * to be made by sound_frame() (via sound_ay_overlay()).
538 */
sound_ay_write(int reg,int val,unsigned long tstates)539 void sound_ay_write(int reg,int val,unsigned long tstates)
540 {
541 if(!sound_enabled) return;
542
543 if(reg>=15) return;
544
545 if(ay_change_count<AY_CHANGE_MAX)
546 {
547 ay_change[ay_change_count].tstates=tstates;
548 ay_change[ay_change_count].reg=reg;
549 ay_change[ay_change_count].val=val;
550 ay_change_count++;
551 }
552 }
553
554
555 /* no need to call this initially, but should be called
556 * on reset otherwise.
557 */
sound_ay_reset(void)558 void sound_ay_reset(void)
559 {
560 int f;
561
562 ay_change_count=0;
563 for(f=0;f<16;f++)
564 sound_ay_write(f,0,0);
565 for(f=0;f<3;f++)
566 ay_tone_high[f]=0;
567 ay_tone_subcycles=ay_env_subcycles=0;
568 fading=sfadetime=0;
569 sound_oldval=sound_oldval_orig=0;
570
571 CLOCK_RESET(AY_CLOCK); /* in case it was CPC before */
572 }
573
574
sound_ay_reset_cpc(void)575 void sound_ay_reset_cpc(void)
576 {
577 sound_ay_reset();
578
579 CLOCK_RESET(AY_CLOCK_CPC);
580 }
581
582
583 /* write stereo or mono beeper sample, and incr ptr */
584 #define SOUND_WRITE_BUF_BEEPER(ptr,val) \
585 do \
586 { \
587 if(sound_stereo_beeper) \
588 { \
589 sound_write_buf_pstereo((ptr),(val)); \
590 (ptr)+=2; \
591 } \
592 else \
593 { \
594 *(ptr)++=(val); \
595 if(sound_stereo) \
596 *(ptr)++=(val); \
597 } \
598 } \
599 while(0)
600
601
602 /* returns zero if this frame was completely silent */
sound_frame(int really_play)603 int sound_frame(int really_play)
604 {
605 static int silent_level=-1;
606 signed short *ptr;
607 int f,silent,chk;
608 int fulllen=sound_framesiz*(sound_stereo+1);
609
610 ptr=sound_buf+(sound_stereo?sound_fillpos*2:sound_fillpos);
611 for(f=sound_fillpos;f<sound_framesiz;f++)
612 SOUND_WRITE_BUF_BEEPER(ptr,sound_oldval);
613
614 sound_ay_overlay();
615
616 /* check for a silent frame.
617 * bit nasty, but it's the only way to be sure. :-)
618 * We check pre-fade, and make a separate check for having faded-out
619 * later. This is to avoid problems with beeper `silence' which is
620 * really a constant high/low level (something similar is also
621 * possible with the AY).
622 *
623 * To cope with beeper and arguably-buggy .ay files, we have to treat
624 * *any* non-varying level as silence. Fair enough in a way, as it
625 * will indeed be silent, but a bit of a pain.
626 */
627 silent=1;
628 ptr=sound_buf;
629 chk=*ptr++;
630 for(f=1;f<fulllen;f++)
631 {
632 if(*ptr++!=chk)
633 {
634 silent=0;
635 break;
636 }
637 }
638
639 /* even if they're all the same, it doesn't count if the
640 * level's changed since last time...
641 */
642 if(chk!=silent_level)
643 silent=0;
644
645 /* save last sample for comparison next time */
646 silent_level=sound_buf[fulllen-1];
647
648 /* apply overall fade if we're in the middle of one. */
649 if(fading)
650 {
651 if(sfadetime<=0)
652 memset(sound_buf,0,fulllen*sizeof(signed short)),silent=1;
653 else
654 {
655 ptr=sound_buf;
656 for(f=0;f<sound_framesiz;f++,ptr++)
657 {
658 /* XXX kludgey, but needed to avoid overflow */
659 sfadetime--;
660 *ptr=(*ptr)*(sfadetime>>4)/(fadetotal>>4);
661 if(sound_stereo)
662 {
663 ptr++;
664 *ptr=(*ptr)*(sfadetime>>4)/(fadetotal>>4);
665 }
666 }
667 }
668 }
669
670 if(really_play)
671 driver_frame(sound_buf,fulllen);
672
673 sound_oldpos=-1;
674 sound_fillpos=0;
675
676 ay_change_count=0;
677
678 return(!silent);
679 }
680
681
682 /* don't do a real frame, just play silence to keep things sane. */
sound_frame_blank(void)683 void sound_frame_blank(void)
684 {
685 static int first=1;
686 static signed short buf[2048]; /* should be plenty */
687 int fulllen=sound_framesiz*(sound_stereo+1);
688
689 if(first)
690 {
691 first=0;
692 memset(buf,0,sizeof(buf));
693 }
694
695 /* just in case it's *not* plenty... :-) */
696 if(sizeof(buf)<fulllen)
697 {
698 usleep(20000);
699 return;
700 }
701
702 driver_frame(buf,fulllen);
703 }
704
705
sound_start_fade(int fadetime_in_sec)706 void sound_start_fade(int fadetime_in_sec)
707 {
708 fading=1;
709 sfadetime=fadetotal=fadetime_in_sec*sound_freq;
710 }
711
712
sound_beeper(int on)713 void sound_beeper(int on)
714 {
715 signed short *ptr;
716 int newpos;
717 int subpos;
718 int val,subval;
719 int f;
720
721 if(!sound_enabled) return;
722
723 val=(on?-AMPL_BEEPER:AMPL_BEEPER);
724
725 if(val==sound_oldval_orig) return;
726
727 /* XXX a lookup table might help here... */
728 newpos=(tstates*sound_framesiz)/tsmax;
729 /* XXX long long may be dodgy portability-wise... */
730 subpos=(((long long)tstates)*sound_framesiz*VOL_BEEPER)/tsmax-VOL_BEEPER*newpos;
731
732 /* if we already wrote here, adjust the level.
733 */
734 if(newpos==sound_oldpos)
735 {
736 /* adjust it as if the rest of the sample period were all in
737 * the new state. (Often it will be, but if not, we'll fix
738 * it later by doing this again.)
739 */
740 if(on)
741 beeper_last_subpos+=VOL_BEEPER-subpos;
742 else
743 beeper_last_subpos-=VOL_BEEPER-subpos;
744 }
745 else
746 beeper_last_subpos=(on?VOL_BEEPER-subpos:subpos);
747
748 subval=AMPL_BEEPER-beeper_last_subpos;
749
750 if(newpos>=0)
751 {
752 /* fill gap from previous position */
753 ptr=sound_buf+(sound_stereo?sound_fillpos*2:sound_fillpos);
754 for(f=sound_fillpos;f<newpos && f<sound_framesiz;f++)
755 SOUND_WRITE_BUF_BEEPER(ptr,sound_oldval);
756
757 if(newpos<sound_framesiz)
758 {
759 /* newpos may be less than sound_fillpos, so... */
760 ptr=sound_buf+(sound_stereo?newpos*2:newpos);
761
762 /* write subsample value */
763 SOUND_WRITE_BUF_BEEPER(ptr,subval);
764 }
765 }
766
767 sound_oldpos=newpos;
768 sound_fillpos=newpos+1;
769 sound_oldval=sound_oldval_orig=val;
770 }
771