Exemple #1
0
        int MixBuffer(bool stereo, short[] buf, ref int bufOffset, byte[] data, Offset offset, int rate, int neededSamples, int bufSize, byte volume, byte panning)
        {
            int samples;

            for (samples = 0; samples < neededSamples && offset.int_off < bufSize; ++samples)
            {
                var d   = (sbyte)data[offset.int_off];
                var tmp = d * volume;
                if (stereo)
                {
                    buf[bufOffset++] += (short)((tmp * (255 - panning)) >> 7);
                    buf[bufOffset++] += (short)((tmp * panning) >> 7);
                }
                else
                {
                    buf[bufOffset++] += (short)tmp;
                }

                // Step to next source sample
                offset.rem_off += rate;
                if (offset.rem_off >= FixedPointFractionHelper.FRAC_ONE)
                {
                    offset.int_off += FixedPointFractionHelper.FracToInt(offset.rem_off);
                    offset.rem_off  = (int)(offset.rem_off & FixedPointFractionHelper.FRAC_LO_MASK);
                }
            }

            return(samples);
        }
Exemple #2
0
        int ReadBufferIntern(bool stereo, short[] buffer, int count)
        {
            var bufOffset  = 0;
            var numSamples = count;
            int samples    = _stereo ? numSamples / 2 : numSamples;

            while (samples > 0)
            {
                // Handle 'interrupts'. This gives subclasses the chance to adjust the channel data
                // (e.g. insert new samples, do pitch bending, whatever).
                if (SingleInterrupt == 0)
                {
                    SingleInterrupt = _intFreq;
                    Interrupt();
                }

                // Compute how many samples to generate: at most the requested number of samples,
                // of course, but we may stop earlier when an 'interrupt' is expected.
                uint nSamples = Math.Min((uint)samples, SingleInterrupt);

                // Loop over the four channels of the emulated Paula chip
                for (int voice = 0; voice < NUM_VOICES; voice++)
                {
                    // No data, or paused -> skip channel
                    if (_voice[voice].data == null || (_voice[voice].period <= 0))
                    {
                        continue;
                    }

                    // The Paula chip apparently run at 7.0937892 MHz in the PAL
                    // version and at 7.1590905 MHz in the NTSC version. We divide this
                    // by the requested the requested output sampling rate _rate
                    // (typically 44.1 kHz or 22.05 kHz) obtaining the value _periodScale.
                    // This is then divided by the "period" of the channel we are
                    // processing, to obtain the correct output 'rate'.
                    var rate = FixedPointFractionHelper.DoubleToFrac(_periodScale / _voice[voice].period);
                    // Cap the volume
                    _voice[voice].volume = Math.Min((byte)0x40, _voice[voice].volume);


                    var ch            = _voice[voice];
                    var p             = buffer;
                    var pOff          = bufOffset;
                    int neededSamples = (int)nSamples;

                    // NOTE: A Protracker (or other module format) player might actually
                    // push the offset past the sample length in its interrupt(), in which
                    // case the first mixBuffer() call should not mix anything, and the loop
                    // should be triggered.
                    // Thus, doing an assert(ch.offset.int_off < ch.length) here is wrong.
                    // An example where this happens is a certain Protracker module played
                    // by the OS/2 version of Hopkins FBI.

                    // Mix the generated samples into the output buffer
                    neededSamples -= MixBuffer(stereo, p, ref pOff, ch.data, ch.offset, rate, neededSamples, ch.length, ch.volume, ch.panning);

                    // Wrap around if necessary
                    if (ch.offset.int_off >= ch.length)
                    {
                        // Important: Wrap around the offset *before* updating the voice length.
                        // Otherwise, if length != lengthRepeat we would wrap incorrectly.
                        // Note: If offset >= 2*len ever occurs, the following would be wrong;
                        // instead of subtracting, we then should compute the modulus using "%=".
                        // Since that requires a division and is slow, and shouldn't be necessary
                        // in practice anyway, we only use subtraction.
                        ch.offset.int_off -= ch.length;
                        ch.dmaCount++;

                        ch.data   = ch.dataRepeat;
                        ch.length = ch.lengthRepeat;
                    }

                    // If we have not yet generated enough samples, and looping is active: loop!
                    if (neededSamples > 0 && ch.length > 2)
                    {
                        // Repeat as long as necessary.
                        while (neededSamples > 0)
                        {
                            // Mix the generated samples into the output buffer
                            neededSamples -= MixBuffer(stereo, p, ref pOff, ch.data, ch.offset, rate, neededSamples, ch.length, ch.volume, ch.panning);

                            if (ch.offset.int_off >= ch.length)
                            {
                                // Wrap around. See also the note above.
                                ch.offset.int_off -= ch.length;
                                ch.dmaCount++;
                            }
                        }
                    }
                }
                bufOffset       += _stereo ? (int)nSamples * 2 : (int)nSamples;
                SingleInterrupt -= nSamples;
                samples         -= (int)nSamples;
            }
            return(numSamples);
        }