/// <summary> /// Returns the highest sample value in a WAV file, as a 16-bit value, regardless of /// whether the file contains 8-bit or 16-bit audio. If the sample is coming from /// an 8-bit audio file, the sample will be scaled up from 8-bit to 16-bit. /// </summary> /// <param name="pFilename">The audio file name</param> /// <returns>The highest sample value from the file, as a 16-bit value</returns> public static short HighestSampleValueAs16Bit(String pFilename) { short highestSample = 0; try { WAVFile audioFile = new WAVFile(); if (audioFile.Open(pFilename, WAVFileMode.READ) == "") { if (audioFile.BitsPerSample == 8) { short sample = 0; for (int i = 0; i < audioFile.NumSamples; ++i) { sample = ScaleByteToShort(audioFile.GetNextSample_8bit()); if (sample > highestSample) highestSample = sample; } } else if (audioFile.BitsPerSample == 16) { short sample = 0; for (int i = 0; i < audioFile.NumSamples; ++i) { sample = audioFile.GetNextSample_16bit(); if (sample > highestSample) highestSample = sample; } } audioFile.Close(); } } catch (Exception) { } return highestSample; }
/// <summary> /// Returns the highest number of channels in a set of audio files. /// </summary> /// <param name="pFilenames">An array containing the audio file names</param> /// <returns>The highest number of channels in the set of audio files</returns> public static byte HighestNumChannels(String[] pFilenames) { byte numChannels = 0; if (pFilenames != null) { WAVFile audioFile = new WAVFile(); String retval = ""; foreach (String filename in pFilenames) { try { retval = audioFile.Open(filename, WAVFileMode.READ); if (retval == "") { if (audioFile.NumChannels > numChannels) numChannels = audioFile.NumChannels; audioFile.Close(); } } catch (Exception) { } } } return numChannels; }
/// <summary> /// Returns the highest sample value in a WAV audio file. /// The return value is a byte array and will contain one /// byte if the file contains 8-bit audio or 2 bytes if the file /// contains 16-bit audio. The return value will be null if /// the file cannot be opened. If it is known that the audio /// file contains 16-bit samples, the byte array can be converted /// to a 16-bit integer using BitConverter.ToInt16(). /// </summary> /// <param name="pFilename">The name of the WAV file</param> /// <param name="pBitsPerSample">This will contain the number of bits per sample, or 0 if the file wasn't loaded.</param> /// <returns>A byte array containing the highest audio sample, or null if the file wasn't loaded.</returns> public static byte[] HighestSampleValue(String pFilename, out short pBitsPerSample) { pBitsPerSample = 0; byte[] highestSampleValue = null; WAVFile audioFile = new WAVFile(); try { if (audioFile.Open(pFilename, WAVFileMode.READ) == "") { pBitsPerSample = audioFile.mBitsPerSample; if (audioFile.mBitsPerSample == 8) { byte sample = 0; byte highestSample = 0; for (int i = 0; i < audioFile.NumSamples; ++i) { sample = audioFile.GetNextSample_8bit(); if (sample > highestSample) highestSample = sample; } highestSampleValue = new byte[1]; highestSampleValue[0] = highestSample; } else if (audioFile.mBitsPerSample == 16) { short sample = 0; short highestSample = 0; for (int i = 0; i < audioFile.NumSamples; ++i) { sample = audioFile.GetNextSample_16bit(); if (sample > highestSample) highestSample = sample; } highestSampleValue = BitConverter.GetBytes(highestSample); if (!BitConverter.IsLittleEndian) Array.Reverse(highestSampleValue); } audioFile.Close(); } } catch (Exception) { } return (highestSampleValue); }
/// <summary> /// Returns a WAVFormat struct containing audio format information /// (# channels, sample rate, and bits per sample) for a WAV file. /// </summary> /// <param name="pFilename">The name of the file about which to retrieve format information</param> /// <returns>A WAVFormat struct object containing the audio format information for the open file</returns> public static WAVFormat GetAudioFormat(String pFilename) { WAVFormat format = new WAVFormat(); WAVFile audioFile = new WAVFile(); if (audioFile.Open(pFilename, WAVFileMode.READ) == "") { format.BitsPerSample = audioFile.mBitsPerSample; format.NumChannels = audioFile.mNumChannels; format.SampleRateHz = audioFile.mSampleRateHz; audioFile.Close(); } return (format); }
/// <summary> /// Returns the highest number of bits per sample in a set of audio files. /// </summary> /// <param name="pFilenames">An array containing the audio file names</param> /// <returns>The highest number of bits per sample in the set of audio files</returns> public static short HighestBitsPerSample(String[] pFilenames) { short bitsPerSample = 0; if (pFilenames != null) { WAVFile audioFile = new WAVFile(); String retval = ""; foreach (String filename in pFilenames) { try { retval = audioFile.Open(filename, WAVFileMode.READ); if (retval == "") { if (audioFile.BitsPerSample > bitsPerSample) bitsPerSample = audioFile.BitsPerSample; audioFile.Close(); } } catch (Exception) { } } } return bitsPerSample; }
/// <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(); }
/// <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()"); }
/// <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()"); }
/// <summary> /// Changes the volume of a WAV file. /// </summary> /// <param name="pFilename">The name of the WAV file to adjust</param> /// <param name="pMultiplier">The volume multiplier</param> public static void AdjustVolumeInPlace(String pFilename, double pMultiplier) { // If pMultiplier is 1, then we don't need to do anything. if (pMultiplier == 1.0) return; // Open the file WAVFile audioFile = new WAVFile(); String retval = audioFile.Open(pFilename, WAVFileMode.READ_WRITE); 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(audioFile.BitsPerSample)) { short bitsPerSample = audioFile.BitsPerSample; audioFile.Close(); throw new WAVFileBitsPerSampleException(pFilename + " has unsupported bits/sample (" + bitsPerSample.ToString() + ")", "WAVFile.AdjustVolumeInPlace()", bitsPerSample); } if (!SupportedSampleRate(audioFile.SampleRateHz)) { int sampleRate = audioFile.SampleRateHz; audioFile.Close(); throw new WAVFileSampleRateException(pFilename + " has unsupported sample rate (" + sampleRate.ToString() + ")", "WAVFile.AdjustVolumeInPlace()", sampleRate); } // Adjust the file volume if (audioFile.BitsPerSample == 8) { byte sample = 0; for (int sampleNum = 0; sampleNum < audioFile.NumSamples; ++sampleNum) { sample = (byte)((double)audioFile.GetNextSample_8bit() * pMultiplier); audioFile.SeekToAudioSample(sampleNum); audioFile.AddSample_8bit(sample); } } else if (audioFile.BitsPerSample == 16) { short sample = 0; for (int sampleNum = 0; sampleNum < audioFile.NumSamples; ++sampleNum) { sample = (short)((double)audioFile.GetNextSample_16bit() * pMultiplier); audioFile.SeekToAudioSample(sampleNum); audioFile.AddSample_16bit(sample); } } audioFile.Close(); } else throw new WAVFileReadException(retval, "WAVFile.AdjustVolumeInPlace()"); }
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; }