Beispiel #1
0
        /// <summary>Reads a new WAVE format section from the specified stream.</summary>
        /// <param name="preRead">Pre-parsed RIFF chunk header.</param>
        /// <param name="source">Source stream to read data from.</param>
        /// <param name="waveFormat">Format of the data section to be parsed.</param>
        /// <exception cref="InvalidOperationException">WAVE format or extra parameters section too small, wave file corrupted.</exception>
        public WaveDataChunk(RiffChunk preRead, Stream source, WaveFormatChunk waveFormat)
            : base(preRead, RiffTypeID)
        {
            m_waveFormat = waveFormat;
            m_sampleBlocks = new List<LittleBinaryValue[]>();

            int blockSize = waveFormat.BlockAlignment;
            int sampleSize = waveFormat.BitsPerSample / 8;
            byte[] buffer = new byte[blockSize];
            int channels = waveFormat.Channels;
            TypeCode sampleTypeCode = m_waveFormat.GetSampleTypeCode();
            LittleBinaryValue[] sampleBlock;

            int bytesRead = source.Read(buffer, 0, blockSize);

            while (bytesRead == blockSize)
            {
                // Create a new sample block, one little-endian formatted binary sample value for each channel
                sampleBlock = new LittleBinaryValue[channels];

                for (int x = 0; x < channels; x++)
                {
                    sampleBlock[x] = new LittleBinaryValue(sampleTypeCode, buffer, x * sampleSize, sampleSize);
                }

                m_sampleBlocks.Add(sampleBlock);

                bytesRead = source.Read(buffer, 0, blockSize);
            }
        }
Beispiel #2
0
        /// <summary>Reads a new WAVE format section from the specified stream.</summary>
        /// <param name="preRead">Pre-parsed <see cref="RiffChunk"/> header.</param>
        /// <param name="source">Source stream to read data from.</param>
        /// <param name="waveFormat">Format of the data section to be parsed.</param>
        /// <exception cref="InvalidOperationException">WAVE format or extra parameters section too small, wave file corrupted.</exception>
        public WaveDataChunk(RiffChunk preRead, Stream source, WaveFormatChunk waveFormat)
            : base(preRead, RiffTypeID)
        {
            m_waveFormat   = waveFormat;
            m_sampleBlocks = new List <LittleBinaryValue[]>();
            m_chunkSize    = -1;

            int      blockSize      = waveFormat.BlockAlignment;
            int      sampleSize     = waveFormat.BitsPerSample / 8;
            int      channels       = waveFormat.Channels;
            TypeCode sampleTypeCode = m_waveFormat.GetSampleTypeCode();

            LittleBinaryValue[] sampleBlock;
            byte[] buffer = new byte[blockSize];

            int bytesRead = source.Read(buffer, 0, blockSize);

            while (bytesRead == blockSize)
            {
                // Create a new sample block, one little-endian formatted binary sample value for each channel
                sampleBlock = new LittleBinaryValue[channels];

                for (int x = 0; x < channels; x++)
                {
                    sampleBlock[x] = new LittleBinaryValue(sampleTypeCode, buffer, x * sampleSize, sampleSize);
                }

                m_sampleBlocks.Add(sampleBlock);

                bytesRead = source.Read(buffer, 0, blockSize);
            }
        }
Beispiel #3
0
        private void WaveIn_DataAvailable(object sender, WaveInEventArgs waveInEventArgs)
        {
            int index      = 0;
            int numSamples = waveInEventArgs.BytesRecorded / m_sampleSize / m_channels;
            List <IMeasurement> measurements = new List <IMeasurement>();

            long sampleTime;
            LittleBinaryValue sampleValue;

            // Get the timestamp for the first recorded sample
            if (Interlocked.Read(ref m_lastSampleTime) == 0L)
            {
                Interlocked.Exchange(ref m_lastSampleTime, DateTime.UtcNow.Ticks - (m_ticksPerSample * numSamples));
            }

            // Get the timestamp for the first sample in this block of data
            sampleTime = Interlocked.Read(ref m_lastSampleTime) + m_ticksPerSample;

            // Parse each recorded sample in this block of data
            for (int sampleIndex = 0; sampleIndex < numSamples; sampleIndex++)
            {
                // Parse one value per channel per sample
                for (int channelIndex = 0; channelIndex < m_channels; channelIndex++)
                {
                    if (channelIndex < OutputMeasurements.Length)
                    {
                        // Create a measurement for this value
                        sampleValue = new LittleBinaryValue(m_sampleTypeCode, waveInEventArgs.Buffer, index + (channelIndex * m_sampleSize), m_sampleSize);
                        measurements.Add(Measurement.Clone(OutputMeasurements[channelIndex], ConvertToPCM16(sampleValue.ConvertToType(TypeCode.Double).ToDouble()), sampleTime));
                    }
                }

                // Update the last sample time
                Interlocked.Exchange(ref m_lastSampleTime, sampleTime);

                // Get the timestamp for the next sample in this block of data
                sampleTime += m_ticksPerSample;

                // Update the index to the next sample in this block of data
                index += m_sampleSize * m_channels;
            }

            // Publish streaming microphone data
            OnNewMeasurements(measurements);
            m_samplesProcessed += numSamples;
        }
Beispiel #4
0
        /// <summary>
        /// Reads and returns the next sample from the stream.
        /// </summary>
        /// <returns>
        /// The next sample from the stream. The sample is returned as
        /// an array of <see cref="LittleBinaryValue"/>s. Each
        /// LittleBinaryValue represents a different channel.
        /// A return value of null indicates the end of stream has been reached.
        /// </returns>
        public LittleBinaryValue[] GetNextSample()
        {
            byte[] buffer    = new byte[m_blockSize];
            int    bytesRead = m_waveStream.Read(buffer, 0, m_blockSize);

            LittleBinaryValue[] sample;

            if (bytesRead == m_blockSize)
            {
                sample = new LittleBinaryValue[m_channels];

                for (int i = 0; i < m_channels; i++)
                {
                    sample[i] = new LittleBinaryValue(m_sampleType, buffer, i * m_sampleSize, m_sampleSize);
                }

                return(sample);
            }

            return(null);
        }
Beispiel #5
0
        /// <summary>
        /// Add the sample to the wave file.
        /// </summary>
        /// <param name="sample">Sample to add to the wave file.</param>
        /// <remarks>
        /// <para>
        /// Sample is applied to all channels and cast to the appropriate size.  Sample should be scaled
        /// by <see cref="AmplitudeScalar"/> for integer based wave file formats to make sure sample will
        /// fit into <see cref="BitsPerSample"/> defined by wave file.
        /// </para>
        /// <para>
        /// If you have samples to apply to individual channels (e.g., for a stereo format), use the
        /// <see cref="AddSamples"/> method instead.
        /// </para>
        /// </remarks>
        public void AddSample(double sample)
        {
            LittleBinaryValue[] binaryValues;

            // Create a new sample block for wave file
            binaryValues = new LittleBinaryValue[m_waveFormat.Channels];

            // Iterate through each channel in WaveFile applying same value to each channel
            for (int x = 0; x < m_waveFormat.Channels; x++)
            {
                // Cast sample value to appropriate data type based on bit size
                binaryValues[x] = m_waveFormat.CastSample(sample);
            }

            // Add sample block to WaveFile
            AddSampleBlock(binaryValues);
        }
Beispiel #6
0
        // Data type independent binary sample combination
        private static LittleBinaryValue CombineBinarySamples(int bitsPerSample, LittleBinaryValue value1, LittleBinaryValue value2, double value2Volume)
        {
            // Algorithm:
            //      1) Convert value into double data type to prevent arithmetic overflow. Note that you
            //         cannot directly cast to a double as there are only enough bytes in the binary
            //         value to cast it to its native data type.
            //      2) Add both data samples together, appling volume adjustment to second value to make
            //         sure values get desired amplitude weight so as to not adversely affect the volume
            //         of the resultant sample.
            //      3) Convert arithmetic result back to proper data type for wave file and return.

            LittleBinaryValue result;

            // Must handle 24-bit as a special case since there is no 24-bit type code in .NET
            if (bitsPerSample == 24)
            {
                Int24 _value1 = value1;
                Int24 _value2 = value1;

                result = (double)_value1 + (double)_value2 * value2Volume;
            }
            else
            {
                result =
                    (double)value1.ConvertToType(TypeCode.Double) +
                    (double)value2.ConvertToType(TypeCode.Double) * value2Volume;
            }
            
            return result.ConvertToType(value1.TypeCode);
        }
Beispiel #7
0
        /// <summary>
        /// Performs a deep clone of all the channel samples in a sample block.
        /// </summary>
        /// <param name="samples">Sample block to clone.</param>
        /// <returns>A deep clone of all the channel samples in a sample block.</returns>
        public static LittleBinaryValue[] CloneSampleBlock(LittleBinaryValue[] samples)
        {
            LittleBinaryValue[] clonedSamples = new LittleBinaryValue[samples.Length];
            byte[] copiedBytes;

            // Perform a deep clone of source samples
            for (int x = 0; x < samples.Length; x++)
            {
                copiedBytes = new byte[samples[x].Buffer.Length];
                Buffer.BlockCopy(samples[x].Buffer, 0, copiedBytes, 0, copiedBytes.Length);
                clonedSamples[x] = new LittleBinaryValue(samples[x].TypeCode, copiedBytes);
            }

            return clonedSamples;
        }
Beispiel #8
0
        /// <summary>
        /// Combines wave files together, all starting at the same time, into a single file.
        /// This has the effect of playing two sound tracks simultaneously.
        /// </summary>
        /// <param name="waveFiles">Wave files to combine</param>
        /// <param name="volumes">Volume for each wave file (0.0 to 1.0)</param>
        /// <returns>Combined wave files.</returns>
        /// <remarks>
        /// <para>
        /// Cumulatively, volumes cannot exceed 1.0 - these volumes represent a fractional percentage
        /// of volume to be applied to each wave file.
        /// </para>
        /// <para>
        /// Resulting sounds will overlap; no truncation is performed. Final wave file length will equal length of
        /// longest source file.
        /// </para>
        /// <para>
        /// Combining sounds files with non-PCM based audio formats will have unexpected results.
        /// </para>
        /// </remarks>
        public static WaveFile Combine(WaveFile[] waveFiles, double[] volumes)
        {
            if (waveFiles.Length > 1)
            {
                // Validate volumes
                if (volumes.Length != waveFiles.Length)
                    throw new ArgumentOutOfRangeException("volumes", "There must be one volume per each wave file");

                if (volumes.Sum() > 1.0D)
                    throw new ArgumentOutOfRangeException("volumes", "Cumulatively, volumes cannot exceed 1.0");

                // Deep clone first wave file - this will become the base of the new combined wave file
                WaveFile next, source = waveFiles[0].Clone();
                int maxLength, nextLength, sourceLength = source.SampleBlocks.Count;

                // Validate compatibility of wave files to be combined
                for (int x = 1; x < waveFiles.Length; x++)
                {
                    next = waveFiles[x];

                    if (source.AudioFormat != next.AudioFormat ||
                        source.SampleRate != next.SampleRate ||
                        source.BitsPerSample != next.BitsPerSample ||
                        source.Channels != next.Channels)
                        throw new ArgumentException("All wave files to be combined must have the same audio format, sample rate, bits per sample and number of channels");
                }

                // Apply volume adjustment to source file
                for (int x = 0; x < sourceLength; x++)
                {
                    for (int y = 0; y < source.Channels; y++)
                    {
                        // Apply volume adjustment to source
                        source.SampleBlocks[x][y] = CombineBinarySamples(source.BitsPerSample, 0, source.SampleBlocks[x][y], volumes[0]);
                    }
                }

                // Combine subsequent wave files
                for (int x = 1; x < waveFiles.Length; x++)
                {
                    next = waveFiles[x];
                    nextLength = next.SampleBlocks.Count;
                    maxLength = sourceLength > nextLength ? sourceLength : nextLength;

                    for (int y = 0; y < maxLength; y++)
                    {
                        if (y < sourceLength && y < nextLength)
                        {
                            for (int z = 0; z < source.Channels; z++)
                            {
                                // Combine each data channel from the source and current wave files
                                source.SampleBlocks[y][z] = CombineBinarySamples(source.BitsPerSample, source.SampleBlocks[y][z], next.SampleBlocks[y][z], volumes[x]);
                            }
                        }
                        else
                        {
                            // Extend source if necessary - note that extended samples still need to be equalized
                            if (nextLength > sourceLength)
                            {
                                LittleBinaryValue[] samples = new LittleBinaryValue[source.Channels];

                                for (int z = 0; z < source.Channels; z++)
                                {
                                    // Combine extended samples with "0" from source to maintain amplitude equalization
                                    samples[z] = CombineBinarySamples(source.BitsPerSample, 0, next.SampleBlocks[y][z], volumes[x]);
                                }

                                source.SampleBlocks.Add(samples);
                            }
                            else
                                break;
                        }
                    }
                }

                return source;
            }
            else
                throw new ArgumentException("You must provide at least two wave files to combine.", "waveFiles");
        }
Beispiel #9
0
        /// <summary>
        /// Adds a series of samples, one per channel, to the wave file.
        /// </summary>
        /// <param name="samples">Samples to add to the wave file.</param>
        /// <remarks>
        /// <para>
        /// You need to pass in one sample for each defined channel (e.g., if wave is configured for stereo
        /// you will need to pass in two parameters).
        /// </para>
        /// Each sample will be cast to the appropriate size.  Samples should be scaled by <see cref="AmplitudeScalar"/>
        /// for integer based wave file formats to make sure samples will fit into <see cref="BitsPerSample"/> defined
        /// by wave file.
        /// </remarks>
        public void AddSamples(params double[] samples)
        {
            // Validate number of samples
            if (samples.Length != m_waveFormat.Channels)
                throw new ArgumentOutOfRangeException("samples", "You must provide one sample for each defined channel");

            LittleBinaryValue[] binaryValues;

            // Create a new sample block for wave file
            binaryValues = new LittleBinaryValue[samples.Length];

            // Iterate through each channel in provided data samples
            for (int x = 0; x < samples.Length; x++)
            {
                // Cast sample value to appropriate data type based on bit size
                binaryValues[x] = m_waveFormat.CastSample(samples[x]);
            }

            // Add sample block to WaveFile
            AddSampleBlock(binaryValues);
        }
        private void WaveIn_DataAvailable(object sender, WaveInEventArgs waveInEventArgs)
        {
            int index = 0;
            int numSamples = waveInEventArgs.BytesRecorded / m_sampleSize / m_channels;
            List<IMeasurement> measurements = new List<IMeasurement>();

            long sampleTime;
            LittleBinaryValue sampleValue;

            // Get the timestamp for the first recorded sample
            if (Interlocked.Read(ref m_lastSampleTime) == 0L)
                Interlocked.Exchange(ref m_lastSampleTime, DateTime.UtcNow.Ticks - (m_ticksPerSample * numSamples));

            // Get the timestamp for the first sample in this block of data
            sampleTime = Interlocked.Read(ref m_lastSampleTime) + m_ticksPerSample;

            // Parse each recorded sample in this block of data
            for (int sampleIndex = 0; sampleIndex < numSamples; sampleIndex++)
            {
                // Parse one value per channel per sample
                for (int channelIndex = 0; channelIndex < m_channels; channelIndex++)
                {
                    if (channelIndex < OutputMeasurements.Length)
                    {
                        // Create a measurement for this value
                        sampleValue = new LittleBinaryValue(m_sampleTypeCode, waveInEventArgs.Buffer, index + (channelIndex * m_sampleSize), m_sampleSize);
                        measurements.Add(Measurement.Clone(OutputMeasurements[channelIndex], ConvertToPCM16(sampleValue.ConvertToType(TypeCode.Double).ToDouble()), sampleTime));
                    }
                }

                // Update the last sample time
                Interlocked.Exchange(ref m_lastSampleTime, sampleTime);

                // Get the timestamp for the next sample in this block of data
                sampleTime += m_ticksPerSample;

                // Update the index to the next sample in this block of data
                index += m_sampleSize * m_channels;
            }

            // Publish streaming microphone data
            OnNewMeasurements(measurements);
            m_samplesProcessed += numSamples;
        }
Beispiel #11
0
        /// <summary>
        /// Reads and returns the next sample from the stream.
        /// </summary>
        /// <returns>
        /// The next sample from the stream. The sample is returned as
        /// an array of <see cref="LittleBinaryValue"/>s. Each
        /// LittleBinaryValue represents a different channel.
        /// A return value of null indicates the end of stream has been reached.
        /// </returns>
        public LittleBinaryValue[] GetNextSample()
        {
            byte[] buffer = new byte[m_blockSize];
            int bytesRead = m_waveStream.Read(buffer, 0, m_blockSize);
            LittleBinaryValue[] sample;

            if (bytesRead == m_blockSize)
            {
                sample = new LittleBinaryValue[m_channels];

                for (int i = 0; i < m_channels; i++)
                {
                    sample[i] = new LittleBinaryValue(m_sampleType, buffer, i * m_sampleSize, m_sampleSize);
                }

                return sample;
            }

            return null;
        }