Beispiel #1
0
        //-------------------------------------------------
        //  generate_samples - generate the requested
        //  number of samples for a stream, making sure
        //  all inputs have the appropriate number of
        //  samples generated
        //-------------------------------------------------
        void generate_samples(int samples)
        {
            ListPointer <stream_sample_t> [] inputs  = null; //stream_sample_t **inputs = nullptr;
            ListPointer <stream_sample_t> [] outputs = null; //stream_sample_t **outputs = nullptr;

            sound_global.VPRINTF("generate_samples({0}, {1})\n", this, samples);
            assert(samples > 0);

            // ensure all inputs are up to date and generate resampled data
            for (int inputnum = 0; inputnum < m_input.size(); inputnum++)
            {
                // update the stream to the current time
                stream_input input = m_input[inputnum];
                if (input.m_source != null)
                {
                    input.m_source.m_stream.update();
                }

                // generate the resampled data
                m_input_array[inputnum] = generate_resampled_data(input, (UInt32)samples);
            }

            if (!m_input.empty())
            {
                inputs = m_input_array;
            }

            // loop over all outputs and compute the output pointer
            for (int outputnum = 0; outputnum < m_output.size(); outputnum++)
            {
                stream_output output = m_output[outputnum];
                m_output_array[outputnum] = new ListPointer <stream_sample_t>(output.m_buffer, m_output_sampindex - m_output_base_sampindex);  // m_output_array[outputnum] = &output.m_buffer[m_output_sampindex - m_output_base_sampindex];
            }

            if (!m_output.empty())
            {
                outputs = m_output_array;
            }

            // run the callback
            sound_global.VPRINTF("  callback({0}, {1})\n", this, samples);
            m_callback(this, inputs, outputs, samples);
            sound_global.VPRINTF("  callback done\n");
        }
Beispiel #2
0
        //const char *input_name(int inputnum) const;
        //device_t *input_source_device(int inputnum) const;
        //int input_source_outputnum(int inputnum) const;
        //float user_gain(int inputnum) const;
        //float input_gain(int inputnum) const;
        //float output_gain(int outputnum) const;


        // operations

        //-------------------------------------------------
        //  set_input - configure a stream's input
        //-------------------------------------------------
        public void set_input(int index, sound_stream input_stream, int output_index = 0, float gain = 1.0f)
        {
            sound_global.VPRINTF("stream_set_input({0}, '{1}', {2}, {3}, {4}, {5})\n", this, m_device.tag(), index, input_stream, output_index, gain);

            // make sure it's a valid input
            if (index >= m_input.size())
            {
                throw new emu_fatalerror("stream_set_input attempted to configure nonexistant input {0} ({1} max)\n", index, m_input.size());
            }

            // make sure it's a valid output
            if (input_stream != null && output_index >= input_stream.m_output.size())
            {
                throw new emu_fatalerror("stream_set_input attempted to use a nonexistant output {0} ({1} max)\n", output_index, m_output.size());
            }

            // if this input is already wired, update the dependent info
            stream_input input = m_input[index];

            if (input.m_source != null)
            {
                input.m_source.m_dependents--;
            }

            // wire it up
            input.m_source    = (input_stream != null) ? input_stream.m_output[output_index] : null;
            input.m_gain      = (Int16)(int)(0x100 * gain);
            input.m_user_gain = 0x100;

            // update the dependent info
            if (input.m_source != null)
            {
                input.m_source.m_dependents++;
            }

            // update sample rates now that we know the input
            recompute_sample_rate_data();
        }
Beispiel #3
0
        //-------------------------------------------------
        //  generate_resampled_data - generate the
        //  resample buffer for a given input
        //-------------------------------------------------
        ListPointer <stream_sample_t> generate_resampled_data(stream_input input, UInt32 numsamples)
        {
            // if we don't have an output to pull data from, generate silence
            ListPointer <stream_sample_t> dest = new ListPointer <stream_sample_t>(input.m_resample);  // stream_sample_t *dest = input.m_resample;

            if (input.m_source == null || input.m_source.m_stream.m_attoseconds_per_sample == 0)
            {
                memset(dest, 0, numsamples);  //memset(dest, 0, numsamples * sizeof(*dest));
                return(new ListPointer <stream_sample_t>(input.m_resample));
            }

            // grab data from the output
            stream_output output       = input.m_source;  // stream_output &output = *input.m_source;
            sound_stream  input_stream = output.m_stream; // sound_stream &input_stream = *output.m_stream;
            s64           gain         = (input.m_gain * input.m_user_gain * output.m_gain) >> 16;

            // determine the time at which the current sample begins, accounting for the
            // latency we calculated between the input and output streams
            attoseconds_t basetime = m_output_sampindex * m_attoseconds_per_sample - input.m_latency_attoseconds;

            // now convert that time into a sample in the input stream
            s32 basesample;

            if (basetime >= 0)
            {
                basesample = (int)(basetime / input_stream.m_attoseconds_per_sample);
            }
            else
            {
                basesample = (int)(-(-basetime / input_stream.m_attoseconds_per_sample) - 1);
            }

            // compute a source pointer to the first sample
            assert(basesample >= input_stream.m_output_base_sampindex);
            ListPointer <stream_sample_t> source = new ListPointer <stream_sample_t>(output.m_buffer, basesample - input_stream.m_output_base_sampindex);  // stream_sample_t *source = &output.m_buffer[basesample - input_stream.m_output_base_sampindex];

            // determine the current fraction of a sample, expressed as a fraction of FRAC_ONE
            // (Note: this formula is valid as long as input_stream.m_attoseconds_per_sample signficantly exceeds FRAC_ONE > attoseconds = 4.2E-12 s)
            u32 basefrac = (u32)((basetime - basesample * input_stream.m_attoseconds_per_sample) / ((input_stream.m_attoseconds_per_sample + FRAC_ONE - 1) >> (int)FRAC_BITS));

            assert(basefrac < FRAC_ONE);

            // compute the stepping fraction
            u32 step = (u32)(((u64)(input_stream.m_sample_rate) << (int)FRAC_BITS) / m_sample_rate);

            // if we have equal sample rates, we just need to copy
            if (step == FRAC_ONE)
            {
                while (numsamples-- != 0)
                {
                    // compute the sample
                    s64 sample = source[0];                // *source++;
                    source++;
                    dest[0] = (int)((sample * gain) >> 8); // *dest++ = (sample * gain) >> 8;
                    dest++;
                }
            }

            // input is undersampled: point sample except where our sample period covers a boundary
            else if (step < FRAC_ONE)
            {
                while (numsamples != 0)
                {
                    // fill in with point samples until we hit a boundary
                    int nextfrac;
                    while ((nextfrac = (int)(basefrac + step)) < FRAC_ONE && numsamples-- != 0)
                    {
                        dest[0] = (int)((source[0] * gain) >> 8);  // *dest++ = (source[0] * gain) >> 8;
                        dest++;
                        basefrac = (UInt32)nextfrac;
                    }

                    // if we're done, we're done
                    if ((s32)(numsamples--) < 0)
                    {
                        break;
                    }

                    // compute starting and ending fractional positions
                    int startfrac = (int)(basefrac >> (int)(FRAC_BITS - 12));
                    int endfrac   = nextfrac >> (int)(FRAC_BITS - 12);

                    // blend between the two samples accordingly
                    s64 sample = ((s64)source[0] * (0x1000 - startfrac) + (s64)source[1] * (endfrac - 0x1000)) / (endfrac - startfrac);
                    dest[0] = (int)((sample * gain) >> 8);  // *dest++ = (sample * gain) >> 8;
                    dest++;

                    // advance
                    basefrac = (UInt32)(nextfrac & FRAC_MASK);
                    source++;
                }
            }

            // input is oversampled: sum the energy
            else
            {
                // use 8 bits to allow some extra headroom
                int smallstep = (int)(step >> (int)(FRAC_BITS - 8));
                while (numsamples-- != 0)
                {
                    s64 remainder = smallstep;
                    int tpos      = 0;

                    // compute the sample
                    s64 scale  = (FRAC_ONE - basefrac) >> (int)(FRAC_BITS - 8);
                    s64 sample = (s64)source[tpos++] * scale;
                    remainder -= scale;
                    while (remainder > 0x100)
                    {
                        sample    += (Int64)source[tpos++] * (Int64)0x100;
                        remainder -= 0x100;
                    }

                    sample += (Int64)source[tpos] * remainder;
                    sample /= smallstep;

                    dest[0] = (int)((sample * gain) >> 8);  // *dest++ = (sample * gain) >> 8;
                    dest++;

                    // advance
                    basefrac += step;
                    source   += basefrac >> (int)FRAC_BITS;
                    basefrac &= FRAC_MASK;
                }
            }

            return(new ListPointer <stream_sample_t>(input.m_resample));
        }
Beispiel #4
0
        // internal helpers

        //-------------------------------------------------
        //  recompute_sample_rate_data - recompute sample
        //  rate data, and all streams that are affected
        //  by this stream
        //-------------------------------------------------
        void recompute_sample_rate_data()
        {
            if (m_synchronous)
            {
                m_sample_rate = 0;
                // When synchronous, pick the sample rate for the inputs, if any
                for (int inputnum = 0; inputnum < m_input.size(); inputnum++)
                {
                    stream_input input = m_input[inputnum];
                    if (input.m_source != null)
                    {
                        if (m_sample_rate == 0)
                        {
                            m_sample_rate = input.m_source.m_stream.m_sample_rate;
                        }
                        else if (m_sample_rate != input.m_source.m_stream.m_sample_rate)
                        {
                            throw new emu_fatalerror("Incompatible sample rates as input of a synchronous stream: {0} and {1}\n", m_sample_rate, input.m_source.m_stream.m_sample_rate);
                        }
                    }
                }
            }


            // recompute the timing parameters
            attoseconds_t update_attoseconds = m_device.machine().sound().update_attoseconds();

            if (m_sample_rate != 0)
            {
                m_attoseconds_per_sample = attotime.ATTOSECONDS_PER_SECOND / m_sample_rate;
                m_max_samples_per_update = (int)((update_attoseconds + m_attoseconds_per_sample - 1) / m_attoseconds_per_sample);
            }
            else
            {
                m_attoseconds_per_sample = 0;
                m_max_samples_per_update = 0;
            }

            // update resample and output buffer sizes
            allocate_resample_buffers();
            allocate_output_buffers();

            // iterate over each input
            for (int inputnum = 0; inputnum < m_input.size(); inputnum++)  // for (auto & input : m_input)
            {
                var input = m_input[inputnum];

                // if we have a source, see if its sample rate changed
                if (input.m_source != null && input.m_source.m_stream.m_sample_rate != 0)
                {
                    // okay, we have a new sample rate; recompute the latency to be the maximum
                    // sample period between us and our input
                    attoseconds_t new_attosecs_per_sample = attotime.ATTOSECONDS_PER_SECOND / input.m_source.m_stream.m_sample_rate;
                    attoseconds_t latency = Math.Max(new_attosecs_per_sample, m_attoseconds_per_sample);

                    // if the input stream's sample rate is lower, we will use linear interpolation
                    // this requires an extra sample from the source
                    if (input.m_source.m_stream.m_sample_rate < m_sample_rate)
                    {
                        latency += new_attosecs_per_sample;
                    }

                    // if our sample rates match exactly, we don't need any latency
                    else if (input.m_source.m_stream.m_sample_rate == m_sample_rate)
                    {
                        latency = 0;
                    }

                    // we generally don't want to tweak the latency, so we just keep the greatest
                    // one we've computed thus far
                    input.m_latency_attoseconds = Math.Max(input.m_latency_attoseconds, latency);

                    //throw new emu_unimplemented();
#if false
                    assert(input.m_latency_attoseconds < update_attoseconds);
#endif
                }
                else
                {
                    input.m_latency_attoseconds = 0;
                }
            }

            // If synchronous, prime the timer
            if (m_synchronous)
            {
                attotime time = m_device.machine().time();
                if (m_attoseconds_per_sample != 0)
                {
                    attoseconds_t next_edge = m_attoseconds_per_sample - (time.attoseconds() % m_attoseconds_per_sample);
                    m_sync_timer.adjust(new attotime(0, next_edge));
                }
                else
                {
                    m_sync_timer.adjust(attotime.never);
                }
            }
        }