xref: /qemu/hw/audio/gusemu_mixer.c (revision bae31bfa48b9caecee25da3d5333901a126a06b4)
1423d65f4Sbalrog /*
2423d65f4Sbalrog  * GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility)
3423d65f4Sbalrog  *
4423d65f4Sbalrog  * Copyright (C) 2000-2007 Tibor "TS" Schütz
5423d65f4Sbalrog  *
6423d65f4Sbalrog  * Permission is hereby granted, free of charge, to any person obtaining a copy
7423d65f4Sbalrog  * of this software and associated documentation files (the "Software"), to deal
8423d65f4Sbalrog  * in the Software without restriction, including without limitation the rights
9423d65f4Sbalrog  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10423d65f4Sbalrog  * copies of the Software, and to permit persons to whom the Software is
11423d65f4Sbalrog  * furnished to do so, subject to the following conditions:
12423d65f4Sbalrog  *
13423d65f4Sbalrog  * The above copyright notice and this permission notice shall be included in
14423d65f4Sbalrog  * all copies or substantial portions of the Software.
15423d65f4Sbalrog  *
16423d65f4Sbalrog  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17423d65f4Sbalrog  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18423d65f4Sbalrog  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19423d65f4Sbalrog  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20423d65f4Sbalrog  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21423d65f4Sbalrog  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22423d65f4Sbalrog  * THE SOFTWARE.
23423d65f4Sbalrog  */
24423d65f4Sbalrog 
256086a565SPeter Maydell #include "qemu/osdep.h"
2647b43a1fSPaolo Bonzini #include "gusemu.h"
2747b43a1fSPaolo Bonzini #include "gustate.h"
28423d65f4Sbalrog 
29423d65f4Sbalrog #define GUSregb(position)  (*            (gusptr+(position)))
304bf7792aSJuan Quintela #define GUSregw(position)  (*(uint16_t *) (gusptr+(position)))
31*58680345SAllan Peramaki #define GUSregd(position)  (*(uint32_t *)(gusptr + (position)))
32423d65f4Sbalrog 
334bf7792aSJuan Quintela #define GUSvoice(position) (*(uint16_t *)(voiceptr+(position)))
34423d65f4Sbalrog 
35423d65f4Sbalrog /* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */
gus_mixvoices(GUSEmuState * state,unsigned int playback_freq,unsigned int numsamples,int16_t * bufferpos)36423d65f4Sbalrog void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples,
37135f5ae1SJuan Quintela                    int16_t *bufferpos)
38423d65f4Sbalrog {
39423d65f4Sbalrog     /* note that byte registers are stored in the upper half of each voice register! */
400af81c56SJuan Quintela     uint8_t        *gusptr;
41423d65f4Sbalrog     int             Voice;
424bf7792aSJuan Quintela     uint16_t       *voiceptr;
43423d65f4Sbalrog 
44423d65f4Sbalrog     unsigned int    count;
45423d65f4Sbalrog     for (count = 0; count < numsamples * 2; count++)
46423d65f4Sbalrog         *(bufferpos + count) = 0;       /* clear */
47423d65f4Sbalrog 
48423d65f4Sbalrog     gusptr = state->gusdatapos;
494bf7792aSJuan Quintela     voiceptr = (uint16_t *) gusptr;
50423d65f4Sbalrog     if (!(GUSregb(GUS4cReset) & 0x01))  /* reset flag active? */
51423d65f4Sbalrog         return;
52423d65f4Sbalrog 
53423d65f4Sbalrog     for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++)
54423d65f4Sbalrog     {
55423d65f4Sbalrog         if (GUSvoice(wVSRControl)        &  0x200)
56423d65f4Sbalrog             GUSvoice(wVSRControl)        |= 0x100; /* voice stop request */
57423d65f4Sbalrog         if (GUSvoice(wVSRVolRampControl) &  0x200)
58423d65f4Sbalrog             GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */
59423d65f4Sbalrog         if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */
60423d65f4Sbalrog         {
61423d65f4Sbalrog             unsigned int    sample;
62423d65f4Sbalrog 
63423d65f4Sbalrog             unsigned int    LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */
64423d65f4Sbalrog             unsigned int    LoopEnd   = (GUSvoice(wVSRLoopEndHi)   << 16) | GUSvoice(wVSRLoopEndLo);   /* 23.9 format */
65423d65f4Sbalrog             unsigned int    CurrPos   = (GUSvoice(wVSRCurrPosHi)   << 16) | GUSvoice(wVSRCurrPosLo);   /* 23.9 format */
66423d65f4Sbalrog             int             VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) /
67423d65f4Sbalrog                                              ((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */
68423d65f4Sbalrog 
69423d65f4Sbalrog             int             PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf;
70423d65f4Sbalrog 
71423d65f4Sbalrog             unsigned int    Volume32   = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */
72423d65f4Sbalrog             unsigned int    StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32;
73423d65f4Sbalrog             unsigned int    EndVol32   = (GUSvoice(wVSRVolRampEndVol)   & 0xff00) * 32;
74423d65f4Sbalrog             int             VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */
75423d65f4Sbalrog             VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */
76423d65f4Sbalrog 
77423d65f4Sbalrog             if (GUSvoice(wVSRControl) & 0x4000)
78423d65f4Sbalrog                 VoiceIncrement    = -VoiceIncrement;    /* reverse playback */
79423d65f4Sbalrog             if (GUSvoice(wVSRVolRampControl) & 0x4000)
80423d65f4Sbalrog                 VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */
81423d65f4Sbalrog 
82423d65f4Sbalrog             for (sample = 0; sample < numsamples; sample++)
83423d65f4Sbalrog             {
84423d65f4Sbalrog                 int             sample1, sample2, Volume;
85423d65f4Sbalrog                 if (GUSvoice(wVSRControl) & 0x400)      /* 16bit */
86423d65f4Sbalrog                 {
87423d65f4Sbalrog                     int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1);
88222e0356SJuan Quintela                     int8_t *adr;
89222e0356SJuan Quintela                     adr = (int8_t *) state->himemaddr + offset;
90423d65f4Sbalrog                     sample1 = (*adr & 0xff) + (*(adr + 1) * 256);
91423d65f4Sbalrog                     sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256);
92423d65f4Sbalrog                 }
93423d65f4Sbalrog                 else            /* 8bit */
94423d65f4Sbalrog                 {
95423d65f4Sbalrog                     int offset = (CurrPos >> 9) & 0xfffff;
96222e0356SJuan Quintela                     int8_t *adr;
97222e0356SJuan Quintela                     adr = (int8_t *) state->himemaddr + offset;
98423d65f4Sbalrog                     sample1 = (*adr) * 256;
99423d65f4Sbalrog                     sample2 = (*(adr + 1)) * 256;
100423d65f4Sbalrog                 }
101423d65f4Sbalrog 
102423d65f4Sbalrog                 Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */
103423d65f4Sbalrog                 sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512;
104423d65f4Sbalrog                 sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512;
105423d65f4Sbalrog                 sample1 += sample2;
106423d65f4Sbalrog 
107423d65f4Sbalrog                 if (!(GUSvoice(wVSRVolRampControl) & 0x100))
108423d65f4Sbalrog                 {
109423d65f4Sbalrog                     Volume32 += VolumeIncrement32;
110423d65f4Sbalrog                     if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */
111423d65f4Sbalrog                     {
112423d65f4Sbalrog                         if (GUSvoice(wVSRVolRampControl) & 0x2000)
113423d65f4Sbalrog                             GUSvoice(wVSRVolRampControl) |= 0x8000;     /* volramp IRQ enabled? -> IRQ wait flag */
114423d65f4Sbalrog                         if (GUSvoice(wVSRVolRampControl) & 0x800)       /* loop enabled */
115423d65f4Sbalrog                         {
116423d65f4Sbalrog                             if (GUSvoice(wVSRVolRampControl) & 0x1000)  /* bidir. loop */
117423d65f4Sbalrog                             {
118423d65f4Sbalrog                                 GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */
119423d65f4Sbalrog                                 VolumeIncrement32 = -VolumeIncrement32;
120423d65f4Sbalrog                             }
121423d65f4Sbalrog                             else
122423d65f4Sbalrog                                 Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */
123423d65f4Sbalrog                         }
124423d65f4Sbalrog                         else
125423d65f4Sbalrog                         {
126423d65f4Sbalrog                             GUSvoice(wVSRVolRampControl) |= 0x100;
127423d65f4Sbalrog                             Volume32 =
128423d65f4Sbalrog                                 (GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32;
129423d65f4Sbalrog                         }
130423d65f4Sbalrog                     }
131423d65f4Sbalrog                 }
132423d65f4Sbalrog                 if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000)  /* volramp IRQ set and enabled? */
133423d65f4Sbalrog                 {
134423d65f4Sbalrog                     GUSregd(voicevolrampirq) |= 1 << Voice;             /* set irq slot */
135423d65f4Sbalrog                 }
136423d65f4Sbalrog                 else
137423d65f4Sbalrog                 {
138423d65f4Sbalrog                     GUSregd(voicevolrampirq) &= (~(1 << Voice));        /* clear irq slot */
139423d65f4Sbalrog                     GUSvoice(wVSRVolRampControl) &= 0x7f00;
140423d65f4Sbalrog                 }
141423d65f4Sbalrog 
142423d65f4Sbalrog                 if (!(GUSvoice(wVSRControl) & 0x100))
143423d65f4Sbalrog                 {
144423d65f4Sbalrog                     CurrPos += VoiceIncrement;
145423d65f4Sbalrog                     if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */
146423d65f4Sbalrog                     {
147423d65f4Sbalrog                         if (GUSvoice(wVSRControl) & 0x2000)
148423d65f4Sbalrog                             GUSvoice(wVSRControl) |= 0x8000;       /* voice IRQ enabled -> IRQ wait flag */
149423d65f4Sbalrog                         if (GUSvoice(wVSRControl) & 0x800)         /* loop enabled */
150423d65f4Sbalrog                         {
151423d65f4Sbalrog                             if (GUSvoice(wVSRControl) & 0x1000)    /* pingpong loop */
152423d65f4Sbalrog                             {
153423d65f4Sbalrog                                 GUSvoice(wVSRControl) ^= 0x4000;   /* toggle dir */
154423d65f4Sbalrog                                 VoiceIncrement = -VoiceIncrement;
155423d65f4Sbalrog                             }
156423d65f4Sbalrog                             else
157423d65f4Sbalrog                                 CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */
158423d65f4Sbalrog                         }
159423d65f4Sbalrog                         else if (!(GUSvoice(wVSRVolRampControl) & 0x400))
160423d65f4Sbalrog                             GUSvoice(wVSRControl) |= 0x100;        /* loop disabled, rollover check */
161423d65f4Sbalrog                     }
162423d65f4Sbalrog                 }
163423d65f4Sbalrog                 if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000)    /* wavetable IRQ set and enabled? */
164423d65f4Sbalrog                 {
165423d65f4Sbalrog                     GUSregd(voicewavetableirq) |= 1 << Voice;      /* set irq slot */
166423d65f4Sbalrog                 }
167423d65f4Sbalrog                 else
168423d65f4Sbalrog                 {
169423d65f4Sbalrog                     GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */
170423d65f4Sbalrog                     GUSvoice(wVSRControl) &= 0x7f00;
171423d65f4Sbalrog                 }
172423d65f4Sbalrog 
173423d65f4Sbalrog                 /* mix samples into buffer */
174135f5ae1SJuan Quintela                 *(bufferpos + 2 * sample)     += (int16_t) ((sample1 * PanningPos) >> 4);        /* right */
175135f5ae1SJuan Quintela                 *(bufferpos + 2 * sample + 1) += (int16_t) ((sample1 * (15 - PanningPos)) >> 4); /* left */
176423d65f4Sbalrog             }
177423d65f4Sbalrog             /* write back voice and volume */
178423d65f4Sbalrog             GUSvoice(wVSRCurrVol)   = Volume32 / 32;
179423d65f4Sbalrog             GUSvoice(wVSRCurrPosHi) = CurrPos >> 16;
180423d65f4Sbalrog             GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff;
181423d65f4Sbalrog         }
182423d65f4Sbalrog         voiceptr += 16; /* next voice */
183423d65f4Sbalrog     }
184423d65f4Sbalrog }
185423d65f4Sbalrog 
gus_irqgen(GUSEmuState * state,unsigned int elapsed_time)186423d65f4Sbalrog void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time)
187423d65f4Sbalrog /* time given in microseconds */
188423d65f4Sbalrog {
189423d65f4Sbalrog     int             requestedIRQs = 0;
1900af81c56SJuan Quintela     uint8_t        *gusptr;
191423d65f4Sbalrog     gusptr = state->gusdatapos;
192423d65f4Sbalrog     if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */
193423d65f4Sbalrog     {
194423d65f4Sbalrog         unsigned int    timer1fraction = state->timer1fraction;
195423d65f4Sbalrog         int             newtimerirqs;
196423d65f4Sbalrog         newtimerirqs          = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1)));
197423d65f4Sbalrog         state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1)));
198423d65f4Sbalrog         if (newtimerirqs)
199423d65f4Sbalrog         {
200423d65f4Sbalrog             if (!(GUSregb(TimerDataReg2x9) & 0x40))
201423d65f4Sbalrog                 GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */
202423d65f4Sbalrog             if (GUSregb(GUS45TimerCtrl) & 4)     /* timer1 irq enable */
203423d65f4Sbalrog             {
204423d65f4Sbalrog                 GUSregb(TimerStatus2x8) |= 4;    /* nonmaskable bit */
205423d65f4Sbalrog                 GUSregb(IRQStatReg2x6)  |= 4;    /* timer 1 irq pending */
206423d65f4Sbalrog                 GUSregw(TimerIRQs) += newtimerirqs;
207423d65f4Sbalrog                 requestedIRQs += newtimerirqs;
208423d65f4Sbalrog             }
209423d65f4Sbalrog         }
210423d65f4Sbalrog     }
211423d65f4Sbalrog     if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */
212423d65f4Sbalrog     {
213423d65f4Sbalrog         unsigned int timer2fraction = state->timer2fraction;
214423d65f4Sbalrog         int             newtimerirqs;
215423d65f4Sbalrog         newtimerirqs          = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2)));
216423d65f4Sbalrog         state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2)));
217423d65f4Sbalrog         if (newtimerirqs)
218423d65f4Sbalrog         {
219423d65f4Sbalrog             if (!(GUSregb(TimerDataReg2x9) & 0x20))
220423d65f4Sbalrog                 GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */
221423d65f4Sbalrog             if (GUSregb(GUS45TimerCtrl) & 8)     /* timer2 irq enable */
222423d65f4Sbalrog             {
223423d65f4Sbalrog                 GUSregb(TimerStatus2x8) |= 2;    /* nonmaskable bit */
224423d65f4Sbalrog                 GUSregb(IRQStatReg2x6)  |= 8;    /* timer 2 irq pending */
225423d65f4Sbalrog                 GUSregw(TimerIRQs) += newtimerirqs;
226423d65f4Sbalrog                 requestedIRQs += newtimerirqs;
227423d65f4Sbalrog             }
228423d65f4Sbalrog         }
229423d65f4Sbalrog     }
230423d65f4Sbalrog     if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */
231423d65f4Sbalrog     {
232423d65f4Sbalrog         if (GUSregd(voicewavetableirq))
233423d65f4Sbalrog             GUSregb(IRQStatReg2x6) |= 0x20;
234423d65f4Sbalrog         if (GUSregd(voicevolrampirq))
235423d65f4Sbalrog             GUSregb(IRQStatReg2x6) |= 0x40;
236423d65f4Sbalrog     }
237423d65f4Sbalrog     if ((!requestedIRQs) && GUSregb(IRQStatReg2x6))
238423d65f4Sbalrog         requestedIRQs++;
239423d65f4Sbalrog     if (GUSregb(IRQStatReg2x6))
240423d65f4Sbalrog         GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs);
241423d65f4Sbalrog }
242