예제 #1
0
        // This method makes an approximation, namely that the pitch mark interval is roughly constant.
        // Usually, this will give a duration accurate to a few per cent (sufficient!) relative to the desired duration.
        public WAVSound ChangeDuration(WAVSound sound, List <double> pitchMarkTimeList, double relativeDuration)
        {
            /*   List<double> pitchPeriodList = new List<double>();
             * for (int ii = 1; ii < pitchMarkTimeList.Count; ii++)
             * {
             *     double pitchPeriod = pitchMarkTimeList[ii] - pitchMarkTimeList[ii - 1];
             *     pitchPeriodList.Add(pitchPeriod);
             * }
             * double averagePitchPeriod = pitchPeriodList.Average();  */

            List <short> newSamples          = new List <short>();
            int          firstPitchMarkIndex = sound.GetSampleIndexAtTime(pitchMarkTimeList[0]);

            for (int ii = 0; ii < firstPitchMarkIndex; ii++)
            {
                newSamples.Add(sound.Samples[0][ii]);
            }
            if (relativeDuration <= 1)
            {
                int removalStepInterval = (int)Math.Round(1 / (1 - relativeDuration));
                int pitchIndex          = 1;
                while (pitchIndex < pitchMarkTimeList.Count)
                {
                    if ((pitchIndex % removalStepInterval) == 0)
                    {
                        // Nothing to do here: Simply avoid adding these samples
                    }
                    else
                    {
                        int previousPitchMarkIndex = sound.GetSampleIndexAtTime(pitchMarkTimeList[pitchIndex - 1]);
                        int currentPitchMarkIndex  = sound.GetSampleIndexAtTime(pitchMarkTimeList[pitchIndex]);
                        for (int ii = previousPitchMarkIndex; ii < currentPitchMarkIndex; ii++)
                        {
                            newSamples.Add(sound.Samples[0][ii]);
                        }
                    }
                    pitchIndex++;
                }
            }
            else if (relativeDuration > 1)
            {
                int additionStepInterval = (int)Math.Round(1 / (relativeDuration - 1));
                int pitchIndex           = 1;
                while (pitchIndex < pitchMarkTimeList.Count)
                {
                    if ((pitchIndex % additionStepInterval) == 0)
                    {
                        // Insert the samples for this pitch period twice:
                        int previousPitchMarkIndex = sound.GetSampleIndexAtTime(pitchMarkTimeList[pitchIndex - 1]);
                        int currentPitchMarkIndex  = sound.GetSampleIndexAtTime(pitchMarkTimeList[pitchIndex]);
                        for (int ii = previousPitchMarkIndex; ii < currentPitchMarkIndex; ii++)
                        {
                            newSamples.Add(sound.Samples[0][ii]);
                        }
                        for (int ii = previousPitchMarkIndex; ii < currentPitchMarkIndex; ii++)
                        {
                            newSamples.Add(sound.Samples[0][ii]);
                        }
                    }
                    else
                    {
                        int previousPitchMarkIndex = sound.GetSampleIndexAtTime(pitchMarkTimeList[pitchIndex - 1]);
                        int currentPitchMarkIndex  = sound.GetSampleIndexAtTime(pitchMarkTimeList[pitchIndex]);
                        for (int ii = previousPitchMarkIndex; ii < currentPitchMarkIndex; ii++)
                        {
                            newSamples.Add(sound.Samples[0][ii]);
                        }
                    }
                    pitchIndex++;
                }
            }

            // Finally, build the sound from the new samples:
            WAVSound newSound = new WAVSound(sound.Name, sound.SampleRate, sound.NumberOfChannels, sound.BitsPerSample);

            newSound.GenerateFromSamples(new List <List <short> >()
            {
                newSamples, newSamples
            });

            return(newSound);
        }
예제 #2
0
        // Note: it is assumed that both channels (left and right) are equal.
        public WAVSound ChangePitch(WAVSound sound, List <double> pitchMarkTimeList, double relativeStartPitch, double relativeEndPitch)
        {
            // First find the pitch mark indices in the original sound:
            List <int> originalPitchMarkIndexList = new List <int>();

            foreach (double pitchMarkTime in pitchMarkTimeList)
            {
                int originalPitchMarkIndex = sound.GetSampleIndexAtTime(pitchMarkTime);
                originalPitchMarkIndexList.Add(originalPitchMarkIndex);
            }

            // Next, compute the index spacings of the pitch marks in the modified sound:
            double     originalSoundDuration             = sound.GetDuration();
            List <int> modifiedPitchMarkIndexSpacingList = new List <int>();

            modifiedPitchMarkTimeList = new List <double>();
            double firstModifiedPitchMarkTime = pitchMarkTimeList[0]; // First pitch mark unchanged

            modifiedPitchMarkTimeList.Add(firstModifiedPitchMarkTime);
            for (int ii = 1; ii < originalPitchMarkIndexList.Count; ii++)
            {
                int    originalPitchMarkSpacing      = originalPitchMarkIndexList[ii] - originalPitchMarkIndexList[ii - 1];
                double relativePitch                 = relativeStartPitch + (pitchMarkTimeList[ii] / originalSoundDuration) * (relativeEndPitch - relativeStartPitch);
                int    modifiedPitchMarkIndexSpacing = (int)Math.Round(originalPitchMarkSpacing / relativePitch);
                modifiedPitchMarkIndexSpacingList.Add(modifiedPitchMarkIndexSpacing);
                double modifiedPitchMarkTime = modifiedPitchMarkTimeList.Last() + (double)modifiedPitchMarkIndexSpacing / (double)sound.SampleRate;
                modifiedPitchMarkTimeList.Add(modifiedPitchMarkTime);
            }

            // Now build the sound, keeping the original sound data over a fraction (topFraction) of the pitch periods
            // and interpolating between pitch periods:
            List <short> newSamples = new List <short>();

            //  Special treatment of the first pitch period:
            int firstPitchMarkIndex = originalPitchMarkIndexList[0];                       // Position of the first pitch mark in the original sound
            int firstModifiedPitchMarkIndexSpacing = modifiedPitchMarkIndexSpacingList[0]; // Spacing between the first and second pitch mark in the modified sound
            int firstTopEndIndex = firstPitchMarkIndex + (int)Math.Round(topFraction * firstModifiedPitchMarkIndexSpacing);

            for (int ii = 0; ii < firstTopEndIndex; ii++)
            {
                newSamples.Add(sound.Samples[0][ii]);
            }

            for (int iPitchMark = 1; iPitchMark < originalPitchMarkIndexList.Count; iPitchMark++)
            {
                // First add samples for the transition from the previous pitch period to the current one:
                int modifiedPitchMarkIndexSpacing = modifiedPitchMarkIndexSpacingList[iPitchMark - 1]; // -1 since there are n-1 spacings for n pitch marks
                int transitionIndexDuration       = (int)Math.Round((1 - 2 * topFraction) * modifiedPitchMarkIndexSpacing);
                int previousPitchMarkIndex        = originalPitchMarkIndexList[iPitchMark - 1];
                int previousTopEndIndex           = previousPitchMarkIndex + (int)Math.Round(topFraction * modifiedPitchMarkIndexSpacing);
                int startIndexPreviousPitchPeriod = previousTopEndIndex;
                int currentPitchMarkIndex         = originalPitchMarkIndexList[iPitchMark];
                int currentTopStartIndex          = currentPitchMarkIndex - (int)Math.Round(topFraction * modifiedPitchMarkIndexSpacing);
                for (int ii = 0; ii < transitionIndexDuration; ii++)
                {
                    double alpha = (double)ii / (double)(transitionIndexDuration - 1);
                    int    previousPitchPeriodSampleIndex = previousTopEndIndex + ii;
                    int    currentPitchPeriodSampleIndex  = currentTopStartIndex - transitionIndexDuration + ii;
                    short  newSample = (short)Math.Round(((1 - alpha) * sound.Samples[0][previousPitchPeriodSampleIndex] +
                                                          alpha * sound.Samples[0][currentPitchPeriodSampleIndex]));
                    newSamples.Add(newSample);
                }
                // Next, add samples around the top of the current pitch period:
                if (iPitchMark < (originalPitchMarkIndexList.Count - 1))
                {
                    int nextModifiedPitchMarkIndexSpacing = modifiedPitchMarkIndexSpacingList[iPitchMark];
                    int currentTopEndIndex = currentPitchMarkIndex + (int)Math.Round(topFraction * nextModifiedPitchMarkIndexSpacing);
                    for (int ii = currentTopStartIndex; ii < currentTopEndIndex; ii++)
                    {
                        newSamples.Add(sound.Samples[0][ii]);
                    }
                }
                else // Special treatment of the final pitch period:
                {
                    int endIndex = sound.Samples[0].Count;
                    for (int ii = currentTopStartIndex; ii < endIndex; ii++)
                    {
                        newSamples.Add(sound.Samples[0][ii]);
                    }
                }
            }

            // Finally, build the sound from the new samples:
            WAVSound newSound = new WAVSound(sound.Name, sound.SampleRate, sound.NumberOfChannels, sound.BitsPerSample);

            newSound.GenerateFromSamples(new List <List <short> >()
            {
                newSamples, newSamples
            });

            return(newSound);
        }
        public WAVSound GenerateSound(FormantSpecification formantSpecification)
        {
            double duration           = formantSpecification.GetDuration();
            int    samplingFrequency  = formantSpecification.SamplingFrequency;
            double sampleTime         = 1.0 / (double)samplingFrequency;
            double nominalPulsePeriod = 1 / (double)formantSpecification.FundamentalFrequency;
            int    numberOfSamples    = (int)Math.Round(duration / sampleTime);

            // Generate voiced pulse train: This requires running through the sequence
            // of formantSettings in the formantSpecification, in order to determine
            // the pitch (and its inverse, the pulse period) at each time:
            List <int>    voicedPulseSpacingList = formantSpecification.GeneratePulseSpacingList();
            List <double> voicedPulseTrain       = new List <double>();

            for (int ii = 0; ii < numberOfSamples; ii++)
            {
                voicedPulseTrain.Add(0);
            }                                                                         // To be adjusted below.
            int sampleIndex = 0;

            while (sampleIndex < voicedPulseTrain.Count)
            {
                voicedPulseTrain[sampleIndex] = 1.0;
                sampleIndex += voicedPulseSpacingList[sampleIndex];
            }

            // Generate unvoiced pulse train:
            if (randomNumberGenerator == null)
            {
                randomNumberGenerator = new Random();
            }
            //     if (gaussian == null) { gaussian = new GaussianDistribution(0, 0.05, -1); }
            List <double> unvoicedPulseTrain = new List <double>();

            for (int ii = 0; ii < numberOfSamples; ii++)
            {
                unvoicedPulseTrain.Add(0);
                if (randomNumberGenerator.NextDouble() < 0.5)
                {
                    unvoicedPulseTrain[ii] = -WHITE_NOISE_LEVEL + 2 * WHITE_NOISE_LEVEL * randomNumberGenerator.NextDouble(); // gaussian.GetSample();
                }
            }

            // Set up sinusoids:
            int numberOfSinusoids = formantSpecification.GetNumberOfSinusoids();
            List <DampedSinusoid> sinusoidList = new List <DampedSinusoid>();

            for (int iSinusoid = 0; iSinusoid < numberOfSinusoids; iSinusoid++)
            {
                DampedSinusoid sinusoid = new DampedSinusoid(samplingFrequency);
                sinusoidList.Add(sinusoid);
            }

            // Prepare for storing pitch:
            if (storePitch)
            {
                //        pitchList = new List<double>();
                timePitchPeriodList = new List <List <double> >();
            }

            // Generate the relative amplitude list: Must be done separately, to handle
            // transitions:
            List <double> relativeAmplitudeList = formantSpecification.GenerateRelativeAmplitudeList();

            // Generate the unscaled samples:
            List <double> unscaledSampleList = new List <double>();
            double        time = 0;

            sampleIndex = 0;
            while (sampleIndex < numberOfSamples)
            {
                time = sampleIndex * sampleTime;
                FormantSettings formantSettings = formantSpecification.GetInterpolatedSettings(sampleIndex);
                for (int iSinusoid = 0; iSinusoid < sinusoidList.Count; iSinusoid++)
                {
                    sinusoidList[iSinusoid].SetParameters(formantSettings.AmplitudeList[iSinusoid],
                                                          formantSettings.FrequencyList[iSinusoid],
                                                          formantSettings.BandwidthList[iSinusoid]);
                }
                double x = formantSettings.VoicedFraction * voicedPulseTrain[sampleIndex] +
                           (1 - formantSettings.VoicedFraction) * unvoicedPulseTrain[sampleIndex];

                // 20170407
                if (storePitch)
                {
                    if (formantSettings.VoicedFraction > minimumVoicedFractionForPitch)
                    {
                        if (voicedPulseTrain[sampleIndex] != 0)  // Define the pitch only at pulse spikes
                        {
                            double pitch = samplingFrequency / voicedPulseSpacingList[sampleIndex];
                            //    pitchList.Add(pitch);
                            double pitchPeriod = voicedPulseSpacingList[sampleIndex] * sampleTime;
                            timePitchPeriodList.Add(new List <double>()
                            {
                                time, pitchPeriod
                            });
                        }

                        /*       else { pitchList.Add(-1); }
                         * }
                         * else
                         * {
                         *     pitchList.Add(-1); // < 0 => pitch not defined  */
                    }
                }

                double deltaTime         = formantSpecification.DeltaTimeList[sampleIndex];
                double relativeAmplitude = relativeAmplitudeList[sampleIndex]; //  formantSettings.GetRelativeAmplitude(deltaTime);
                double sample            = 0;
                for (int iSinusoid = 0; iSinusoid < sinusoidList.Count; iSinusoid++)
                {
                    sample += sinusoidList[iSinusoid].Next(x);
                }
                sample *= relativeAmplitude * volume;
                unscaledSampleList.Add(sample);
                sampleIndex++;
            }

            // Next generate the scaled samples
            List <Int16> sampleList = new List <Int16>();

            for (int ii = 0; ii < numberOfSamples; ii++)
            {
                if (unscaledSampleList[ii] > 1)
                {
                    unscaledSampleList[ii] = 1;
                }
                else if (unscaledSampleList[ii] < -1)
                {
                    unscaledSampleList[ii] = -1;
                }
                Int16 sample = (Int16)Math.Round(32767 * unscaledSampleList[ii]);   // Some ugly hard-coding here...
                sampleList.Add(sample);
            }
            List <List <Int16> > twoChannelSampleList = new List <List <Int16> >();

            twoChannelSampleList.Add(sampleList);
            WAVSound wavSound = new WAVSound("Test", samplingFrequency, 1, 16);  // Some ugly hard-coding here...

            wavSound.GenerateFromSamples(twoChannelSampleList);
            return(wavSound);
        }