Пример #1
0
        /// <summary>
        /// Converts a WAV file's bits/sample and number of channels to a separate WAV file.
        /// </summary>
        /// <param name="pSrcFilename">The name of the file to convert</param>
        /// <param name="pDestFilename">The destination file name</param>
        /// <param name="pBitsPerSample">The destination's number of bits/sample</param>
        /// <param name="pStereo">Whether or not the destination should be stereo</param>
        /// <param name="pVolumeMultiplier">A multiplier that can be used to adjust the volume of the output audio file</param>
        public static void CopyAndConvert(String pSrcFilename, String pDestFilename, short pBitsPerSample, bool pStereo, double pVolumeMultiplier)
        {
            WAVFile srcFile = new WAVFile();
            String retval = srcFile.Open(pSrcFilename, WAVFileMode.READ);
            if (retval != "")
                throw new WAVFileException(retval, "WAVFile.Convert_Copy()");

            WAVFile destFile = new WAVFile();
            destFile.Create(pDestFilename, pStereo, srcFile.SampleRateHz, pBitsPerSample);
            if ((srcFile.BitsPerSample == 8) && (pBitsPerSample == 8))
            {
                byte sample = 0;
                if (srcFile.IsStereo && !pStereo)
                {
                    // 8-bit to 8-bit, stereo to mono: Average each 2 samples
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = (byte)((short)((short)srcFile.GetNextSample_8bit() + (short)srcFile.GetNextSample_8bit()) / 2);
                        if (pVolumeMultiplier != 1.0)
                            sample = (byte)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_8bit(sample);
                    }
                }
                else if ((srcFile.IsStereo && pStereo) || (!srcFile.IsStereo && !pStereo))
                {
                    // 8-bit to 8-bit, stereo to stereo or mono to mono
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = srcFile.GetNextSample_8bit();
                        if (pVolumeMultiplier != 1.0)
                            sample = (byte)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_8bit(sample);
                    }
                }
                else if (!srcFile.IsStereo && pStereo)
                {
                    // 8-bit to 8-bit, mono to stereo: Write each sample twice
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = srcFile.GetNextSample_8bit();
                        if (pVolumeMultiplier != 1.0)
                            sample = (byte)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_8bit(sample);
                        destFile.AddSample_8bit(sample);
                    }
                }
            }
            else if ((srcFile.BitsPerSample == 8) && (pBitsPerSample == 16))
            {
                short sample = 0;
                if (srcFile.IsStereo && !pStereo)
                {
                    // 8-bit to 16 bit, stereo to mono: Average each 2 samples
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = (short)((int)((int)srcFile.GetNextSampleAs16Bit() + (int)srcFile.GetNextSampleAs16Bit()) / 2);
                        if (pVolumeMultiplier != 1.0)
                            sample = (short)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_16bit(sample);
                    }
                }
                else if ((srcFile.IsStereo && pStereo) || (!srcFile.IsStereo && !pStereo))
                {
                    // 8-bit to 16 bit, stereo to stereo or mono to mono
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = srcFile.GetNextSampleAs16Bit();
                        if (pVolumeMultiplier != 1.0)
                            sample = (short)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_16bit(sample);
                    }
                }
                else if (!srcFile.IsStereo && pStereo)
                {
                    // 8-bit to 16 bit, mono to stereo: Write each sample twice
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = srcFile.GetNextSampleAs16Bit();
                        if (pVolumeMultiplier != 1.0)
                            sample = (short)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_16bit(sample);
                        destFile.AddSample_16bit(sample);
                    }
                }
            }
            else if ((srcFile.BitsPerSample == 16) && (pBitsPerSample == 8))
            {
                byte sample = 0;
                if (srcFile.IsStereo && !pStereo)
                {
                    // 16-bit to 8-bit, stereo to mono: Average each 2 samples
                    short sample_16bit = 0;
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample_16bit = (short)((int)srcFile.GetNextSample_16bit() + (int)srcFile.GetNextSample_16bit() / 2);
                        if (pVolumeMultiplier != 1.0)
                            sample_16bit = (short)((double)sample_16bit * pVolumeMultiplier);
                        sample = ScaleShortToByte(sample_16bit);
                        destFile.AddSample_8bit(sample);
                    }
                }
                else if ((srcFile.IsStereo && pStereo) || (!srcFile.IsStereo && !pStereo))
                {
                    // 16-bit to 8-bit, stereo to stereo or mono to mono
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = ScaleShortToByte(srcFile.GetNextSample_16bit());
                        if (pVolumeMultiplier != 1.0)
                            sample = (byte)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_8bit(sample);
                    }
                }
                else if (!srcFile.IsStereo && pStereo)
                {
                    // 16-bit to 8-bit, mono to stereo: Write each sample twice
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = ScaleShortToByte(srcFile.GetNextSample_16bit());
                        if (pVolumeMultiplier != 1.0)
                            sample = (byte)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_8bit(sample);
                        destFile.AddSample_8bit(sample);
                    }
                }
            }
            else if ((srcFile.BitsPerSample == 16) && (pBitsPerSample == 16))
            {
                short sample = 0;
                if (srcFile.IsStereo && !pStereo)
                {
                    // 16-bit to 16-bit, stereo to mono: Average each 2 samples
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = (short)((int)((int)srcFile.GetNextSample_16bit() + (int)srcFile.GetNextSample_16bit()) / 2);
                        if (pVolumeMultiplier != 1.0)
                            sample = (short)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_16bit(sample);
                    }
                }
                else if ((srcFile.IsStereo && pStereo) || (!srcFile.IsStereo && !pStereo))
                {
                    // 16-bit to 16-bit, stereo to stereo or mono to mono
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = srcFile.GetNextSample_16bit();
                        if (pVolumeMultiplier != 1.0)
                            sample = (short)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_16bit(sample);
                    }
                }
                else if (!srcFile.IsStereo && pStereo)
                {
                    // 16-bit to 16-bit, mono to stereo: Write each sample twice
                    while (srcFile.NumSamplesRemaining > 0)
                    {
                        sample = srcFile.GetNextSample_16bit();
                        if (pVolumeMultiplier != 1.0)
                            sample = (short)((double)sample * pVolumeMultiplier);
                        destFile.AddSample_16bit(sample);
                        destFile.AddSample_16bit(sample);
                    }
                }
            }

            destFile.Close();
            srcFile.Close();
        }
Пример #2
0
        /// <summary>
        /// Adjusts the volume level of a WAV file, saving the adjusted file as a separate file.
        /// </summary>
        /// <param name="pSrcFilename">The name of the WAV file to adjust</param>
        /// <param name="pDestFilename">The name to use for the volume-adjusted WAV file</param>
        /// <param name="pMultiplier">The value by which to multiply the audio samples</param>
        public static void AdjustVolume_Copy(String pSrcFilename, String pDestFilename, double pMultiplier)
        {
            // If an empty source or destination file were passed in, then throw an exception.
            if (pSrcFilename == "")
                throw new WAVFileReadException("Blank filename specified.", "WAVFile.AdjustVolume_Copy()");
            if (pDestFilename == "")
                throw new WAVFileWriteException("Blank filename specified.", "WAVFile.AdjustVolume_Copy()");

            // Open the srouce file
            WAVFile srcFile = new WAVFile();
            String retval = srcFile.Open(pSrcFilename, WAVFileMode.READ);
            if (retval == "")
            {
                // Check to make sure the input file has a supported number of bits/sample and sample rate.  If
                // not, then throw an exception.
                if (!SupportedBitsPerSample(srcFile.BitsPerSample))
                {
                    WAVFileBitsPerSampleException exc = new WAVFileBitsPerSampleException(pSrcFilename +
                                                              " has unsupported bits/sample ("
                                                              + srcFile.BitsPerSample.ToString() + ")",
                                                              "WAVFile.AdjustVolume_Copy()", srcFile.BitsPerSample);
                    srcFile.Close();
                    throw exc;
                }

                // Open the destination file and start copying the adjusted audio data to it.
                WAVFile destFile = new WAVFile();
                destFile.Create(pDestFilename, srcFile.IsStereo, srcFile.SampleRateHz, srcFile.BitsPerSample);
                if (srcFile.BitsPerSample == 8)
                {
                    byte sample = 0;
                    for (int i = 0; i < srcFile.NumSamples; ++i)
                    {
                        // Note: Only multiply the sample if pMultiplier is not 1.0 (if the multiplier is
                        // 1.0, then it would be good to avoid any binary roundoff error).
                        sample = srcFile.GetNextSample_8bit();
                        if (pMultiplier != 1.0)
                            sample = (byte)((double)sample * pMultiplier);
                        destFile.AddSample_8bit(sample);
                    }
                }
                else if (srcFile.BitsPerSample == 16)
                {
                    short sample = 0;
                    for (int i = 0; i < srcFile.NumSamples; ++i)
                    {
                        // Note: Only multiply the sample if pMultiplier is not 1.0 (if the multiplier is
                        // 1.0, then it would be good to avoid any binary roundoff error).
                        sample = srcFile.GetNextSample_16bit();
                        if (pMultiplier != 1.0)
                            sample = (short)((double)sample * pMultiplier);
                        destFile.AddSample_16bit(sample);
                    }
                }

                srcFile.Close();
                destFile.Close();
            }
            else
                throw new WAVFileReadException(retval, "WAVFile.AdjustVolume_Copy()");
        }
Пример #3
0
        /// <summary>
        /// For 8-bit WAV files: Adjusts the volume level and converts it to a 16-bit audio file.
        /// The converted data is saved to a separate file.
        /// </summary>
        /// <param name="pSrcFilename">The name of the WAV file to convert</param>
        /// <param name="pDestFilename">The name to use for the converted WAV file</param>
        /// <param name="pMultiplier">The volume multiplier</param>
        public static void AdjustVolume_Copy_8BitTo16Bit(String pSrcFilename, String pDestFilename, double pMultiplier)
        {
            // If an empty source or destination file were passed in, then throw an exception.
            if (pSrcFilename == "")
                throw new WAVFileReadException("Blank filename specified.", "WAVFile.AdjustVolume_Copy_8BitTo16Bit()");
            if (pDestFilename == "")
                throw new WAVFileWriteException("Blank filename specified.", "WAVFile.AdjustVolume_Copy_8BitTo16Bit()");

            // Open the srouce file
            WAVFile srcFile = new WAVFile();
            String retval = srcFile.Open(pSrcFilename, WAVFileMode.READ);
            if (retval == "")
            {
                // Check to make sure the input file has 8 bits per sample.  If not, then throw an exception.
                if (srcFile.BitsPerSample != 8)
                {
                    WAVFileBitsPerSampleException exc = new WAVFileBitsPerSampleException(pSrcFilename +
                                                              ": 8 bits per sample required, and the file has " +
                                                              srcFile.BitsPerSample.ToString() + " bits per sample.",
                                                              "WAVFile.AdjustVolume_Copy_8BitTo16Bit()",
                                                              srcFile.BitsPerSample);
                    srcFile.Close();
                    throw exc;
                }

                // Open the destination file
                WAVFile destFile = new WAVFile();
                destFile.Create(pDestFilename, srcFile.IsStereo, srcFile.SampleRateHz, 16, true);

                // Copy the data
                short sample_16bit = 0;
                while (srcFile.NumSamplesRemaining > 0)
                {
                    // Scale the sample from 8-bit to 16 bits
                    sample_16bit = ScaleByteToShort(srcFile.GetNextSample_8bit());

                    // Now, apply pMultiplier if it is not 1.0
                    if (pMultiplier != 1.0)
                        sample_16bit = (short)((double)sample_16bit * pMultiplier);

                    // Save the sample to the destination file
                    destFile.AddSample_16bit(sample_16bit);
                }

                srcFile.Close();
                destFile.Close();
            }
            else
                throw new WAVFileReadException(retval, "WAVFile.AdjustVolume_Copy_8BitTo16Bit()");
        }
Пример #4
0
        public static void MixAudioFiles(WAVInputFile[] pInputFiles, string pOutputFilename, string pTempDir)
        {
            amountDone = 0;
            OnUpdated();

            string[] pFileList = new string[pInputFiles.Length];
            for (int i = 0; i < pInputFiles.Length; i++)
            {
                pFileList[i] = pInputFiles[i].ToString();
            }

            // If pFileList is null or empty, then just return.
            if (pFileList == null)
                return;
            if (pFileList.GetLength(0) == 0)
                return;

            // Make sure all the audio files have the sample rate (we can merge 8-bit and 16-bit audio and
            // convert mono to stereo).  If the sample rates don't match, then throw an exception.
            if (!SampleRatesEqual(pFileList))
                throw new WAVFileAudioMergeException("The sample rates of the audio files differ.", "WAVFile.MergeAudioFiles()");

            amountDone = 1;
            OnUpdated();

            // Check the audio format.  If the number of bits/sample or sample rate is not
            // supported, then throw an exception.
            WAVFormat firstFileAudioFormat = GetAudioFormat(pFileList[0]);
            if (!SupportedBitsPerSample(firstFileAudioFormat.BitsPerSample))
                throw new WAVFileBitsPerSampleException("Unsupported number of bits per sample: " + firstFileAudioFormat.BitsPerSample.ToString(), "WAVFile.MergeAudioFiles()", firstFileAudioFormat.BitsPerSample);
            if (!SupportedSampleRate(firstFileAudioFormat.SampleRateHz))
                throw new WAVFileSampleRateException("Unsupported sample rate: " + firstFileAudioFormat.SampleRateHz.ToString(), "WAVFile.MergeAudioFiles()", firstFileAudioFormat.SampleRateHz);

            amountDone = 2;
            OnUpdated();

            // 2. Create the temporary directory if it doesn't exist already.  This checks for the
            // existence of the temp directory and stores the result in tempDirExisted so that
            // later, if the temp directory did not exist, we can delete it.
            bool tempDirExisted = Directory.Exists(pTempDir);
            if (!tempDirExisted)
            {
                try
                {
                    Directory.CreateDirectory(pTempDir);
                    if (!Directory.Exists(pTempDir))
                        throw new WAVFileAudioMergeException("Unable to create temporary work directory (" + pTempDir + ")", "WAVFile.MergeAudioFiles()");
                }
                catch (System.Exception exc)
                {
                    throw new WAVFileAudioMergeException("Unable to create temporary work directory (" + pTempDir + "): "
                                                         + exc.Message, "WAVFile.MergeAudioFiles()");
                }
            }

            amountDone = 3;
            OnUpdated();

            // 4. Find the highest sample value of all files, and calculate the sound
            //    multiplier based on this (all files will be scaled down by this amount).
            int numTracks = pFileList.GetLength(0);
            double multiplier = 0.0; // The multiplier for scaling down the audio files
            short highestSampleValue = 0; // Will store the highest sample value (8-bit will be cast to short)
            // Determine the highest # of bits per sample in all the audio files.
            short highestBitsPerSample = HighestBitsPerSample(pFileList);
            bool outputStereo = (HighestNumChannels(pFileList) > 1);
            if (highestBitsPerSample == 8)
            {
                // Get the highest sample value of all of the WAV files
                byte highestSample = HighestSampleValue_8bit(pFileList);
                highestSampleValue = (short)highestSample;

                byte difference = (byte)(highestSample - (byte.MaxValue / (byte)numTracks));
                multiplier = 1.0 - ((double)difference / (double)highestSample);
            }
            else if (highestBitsPerSample == 16)
            {
                // Get the highest sample value of all of the WAV files
                highestSampleValue = HighestSampleValueAs16Bit(pFileList);

                short difference = (short)(highestSampleValue - (short.MaxValue / (short)numTracks));
                multiplier = 1.0 - ((double)difference / (double)highestSampleValue);
            }
            if (double.IsInfinity(multiplier) || (multiplier == 0.0))
            {
                // If the temp dir did not exist, then remove it.
                if (!tempDirExisted)
                    DeleteDir(pTempDir);
                // Throw the exception
                throw new WAVFileAudioMergeException("Could not calculate first volume multiplier.", "WAVFile.MergeAudioFiles()");
            }

            amountDone = 4;
            OnUpdated();

            if (multiplier < 0.0)
                multiplier = -multiplier;

            // 5. Scale down the audio levels of the source files, and save the output
            //    in the temp directory.  Also change the path to the audio files in
            //    inputFilenames so that they point to the temp directory (we'll be
            //    combining the scaled audio files).
            // This array (scaledAudioFiles) will contain WAVFile objects for the scaled audio files.
            WAVFile[] scaledAudioFiles = new WAVFile[pFileList.GetLength(0)];
            String filename = "";               // For the scaled-down WAV filename
            WAVFile inputFile = new WAVFile();
            WAVFile outputFile = new WAVFile();
            for (int i = 0; i < pFileList.GetLength(0); ++i)
            {
                // pFileList[i] contains the fully-pathed filename.  Using just the
                // filename, construct the fully-pathed filename for the scaled-down
                // version of the file in the temporary directory.
                filename = pTempDir + "\\" + Path.GetFileName(pFileList[i]);

                // Copy the file to the temp directory, adjusting its bits/sample and number of
                // channels if necessary.  And also ajust its volume using multiplier.
                //CopyAndConvert(pFileList[i], filename, highestBitsPerSample, outputStereo, multiplier);
                CopyAndConvert(pFileList[i], filename, highestBitsPerSample, outputStereo, 1.0f);

                // Create the WAVFile object in the scaledAudioFiles array, and open the scaled
                // audio file with it.
                scaledAudioFiles[i] = new WAVFile();
                scaledAudioFiles[i].Open(filename, WAVFileMode.READ);
            }

            amountDone = 5;
            OnUpdated();

            // 7. Now, create the final audio mix file.
            outputFile.Create(pOutputFilename, outputStereo, firstFileAudioFormat.SampleRateHz,
                              highestBitsPerSample);

            amountDone = 6;
            OnUpdated();

            // 8. Do the merging..
            // The basic algorithm for doing the merging is as follows:
            // while there is at least 1 sample remaining in any of the source files
            //    sample = 0
            //    for each source file
            //       if the source file has any samples remaining
            //          sample = sample + next available sample from the source file
            //    sample = sample / # of source files
            //    write the sample to the output file
            if (highestBitsPerSample == 8)
            {
                byte sample = 0;
                while (SamplesRemain(scaledAudioFiles))
                {
                    sample = 0;
                    for (int i = 0; i < scaledAudioFiles.GetLength(0); ++i)
                    {
                        if (scaledAudioFiles[i].NumSamplesRemaining > 0)
                            sample += scaledAudioFiles[i].GetNextSample_8bit();
                    }
                    sample /= (byte)(scaledAudioFiles.GetLength(0));
                    outputFile.AddSample_8bit(sample);
                }
            }
            else if (highestBitsPerSample == 16)
            {
                for (int i = 0; i < scaledAudioFiles.Length - 1; i++)
                {
                    int currentAudioFile = i;
                    double totalFileTime = GetWavFileDuration(pFileList[currentAudioFile]).TotalSeconds;
                    double timePerSample = totalFileTime / scaledAudioFiles[currentAudioFile].NumSamples;  // in seconds
                    double mixTime = totalFileTime - pInputFiles[currentAudioFile].FadeTime;

                    double timePast = (scaledAudioFiles[currentAudioFile].NumSamples - scaledAudioFiles[currentAudioFile].NumSamplesRemaining) * timePerSample;

                    while (scaledAudioFiles[currentAudioFile].NumSamplesRemaining > 0)
                    {
                        // Get the next sample from the current audio file
                        short sample = scaledAudioFiles[currentAudioFile].GetNextSampleAs16Bit();

                        // Find the mix time to mix the sample of the next audio file into there
                        if (timePast >= mixTime)
                        {
                            sample += scaledAudioFiles[currentAudioFile + 1].GetNextSampleAs16Bit();
                        }

                        // Devide the sample by the amount of samples
                        sample /= (short)(scaledAudioFiles.Length);

                        // Write the audio sample to the output file
                        outputFile.AddSample_16bit(sample);

                        // Add the time of the sample ot the total time
                        timePast += timePerSample;
                    }

                    amountDone = 94 / scaledAudioFiles.Length * (i + 1) + 6;
                    OnUpdated();
                }

                // Write the rest
                while (scaledAudioFiles[scaledAudioFiles.Length -1].NumSamplesRemaining > 0)
                {
                    short sample = scaledAudioFiles[scaledAudioFiles.Length - 1].GetNextSampleAs16Bit();

                    sample /= (short)(scaledAudioFiles.Length);

                    outputFile.AddSample_16bit(sample);
                }

                amountDone = 99;
                OnUpdated();
            }

            PBBRenderer.Instance.ActiveStatus = 2;
            OnUpdated();

            outputFile.Close();

            // 9. Remove the input files(to free up disk space.
            foreach (WAVFile audioFile in scaledAudioFiles)
            {
                filename = audioFile.Filename;
                audioFile.Close();
                System.IO.File.Delete(filename);
            }

            // 10. Now, increase the volume level of the output file. (will first have
            //     to see if the inputs are 8-bit or 16-bit.)
            //  Adjust the volume level of the file so that its volume is
            //  the same as the volume of the input files.  This is done by
            //  first finding the highest sample value of all files, then
            //  the highest sample value of the combined audio file, and
            //  scaling the audio of the output file to match the volume
            //  of the input files (such that the highest level of the output
            //  file is the same as the highest level of all files).
            // For 16-bit audio, this works okay, but for 8-bit audio, the
            //  output seems to sound better if we adjust the volume so that
            //  the highest sample value is 3/4 of the maximum sample value
            //  for the # of bits/sample.
            if (highestBitsPerSample == 8)
            {
                byte highestSampleVal = HighestSampleValue_8bit(pOutputFilename);
                byte maxValue = byte.MaxValue / 4 * 3;
                multiplier = (double)maxValue / (double)highestSampleVal;
            }
            else if (highestBitsPerSample == 16)
            {
                short finalMixFileHighestSample = HighestSampleValueAs16Bit(pOutputFilename);
                // Calculate the multiplier for adjusting the audio of the final mix file.
                //short difference = (short)(finalMixFileHighestSample - highestSampleValue);
                //multiplier = 1.0 - ((double)difference / (double)finalMixFileHighestSample);
                // This calculates the multiplier based on the highest sample value in the audio
                // file and the highest possible 16-bit sample value.
                multiplier = (double)short.MaxValue / (double)finalMixFileHighestSample;
            }
            if (multiplier < 0.0)
                multiplier = -multiplier;

            // Adjust the volume of the final mix file.
            //AdjustVolumeInPlace(pOutputFilename, multiplier);

            // If the temporary directory did not exist prior to this method being called, then
            // delete it.
            if (!tempDirExisted)
            {
                String retval = DeleteDir(pTempDir);
                if (retval != "")
                    throw new WAVFileAudioMergeException("Unable to remove temp directory (" + pTempDir + "): " + retval,
                                                         "WAVFile.MergeAudioFiles()");
            }

            amountDone = 100;
        }