Automated Noise Reduction System for Audio Recordings C#
👤 Sharing: AI
```csharp
using NAudio.Wave;
using NAudio.Dsp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace AudioNoiseReduction
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Audio Noise Reduction System");
// 1. Get Input/Output File Paths
Console.Write("Enter the path to the input audio file: ");
string inputFilePath = Console.ReadLine();
Console.Write("Enter the path to the output audio file: ");
string outputFilePath = Console.ReadLine();
// 2. Noise Reduction Parameters (You can adjust these)
double noiseProfileDurationSeconds = 1.0; // Duration of the initial noise sample (seconds)
double reductionFactor = 0.7; // How much to reduce the noise (0.0 to 1.0) - higher = more aggressive
double smoothingFactor = 0.1; // Smoothing the noise estimate (0.0 to 1.0) - higher = smoother, but less responsive
try
{
// 3. Perform Noise Reduction
Console.WriteLine("Processing audio...");
ReduceNoise(inputFilePath, outputFilePath, noiseProfileDurationSeconds, reductionFactor, smoothingFactor);
Console.WriteLine("Noise reduction complete. File saved to: " + outputFilePath);
}
catch (Exception ex)
{
Console.WriteLine("An error occurred: " + ex.Message);
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
/// <summary>
/// Reduces noise in an audio file using a spectral subtraction technique.
/// </summary>
/// <param name="inputFilePath">Path to the input audio file (WAV format).</param>
/// <param name="outputFilePath">Path to save the noise-reduced audio file.</param>
/// <param name="noiseProfileDurationSeconds">Duration (in seconds) of the initial noise profile.</param>
/// <param name="reductionFactor">Factor to reduce the estimated noise by (0.0 to 1.0).</param>
/// <param name="smoothingFactor">Smoothing factor for the noise estimate (0.0 to 1.0).</param>
static void ReduceNoise(string inputFilePath, string outputFilePath, double noiseProfileDurationSeconds, double reductionFactor, double smoothingFactor)
{
// Use NAudio to read the audio file
using (var reader = new AudioFileReader(inputFilePath))
{
// Get the wave format information
WaveFormat waveFormat = reader.WaveFormat;
// Calculate the size of the noise profile in samples
int noiseProfileSamples = (int)(noiseProfileDurationSeconds * waveFormat.SampleRate);
// Create a buffer to hold the noise profile
float[] noiseProfileBuffer = new float[noiseProfileSamples * waveFormat.Channels];
// Read the initial noise profile from the beginning of the audio file
int bytesRead = reader.Read(noiseProfileBuffer, 0, noiseProfileBuffer.Length);
// Analyze the noise profile
double[] noiseMagnitudeSpectrum = AnalyzeNoiseProfile(noiseProfileBuffer, waveFormat);
// Reset the reader to the beginning of the file
reader.Position = 0;
// Create a writer to save the noise-reduced audio
using (var writer = new WaveFileWriter(outputFilePath, waveFormat))
{
// Determine the FFT size based on sample rate
int fftSize = DetermineFFTSize(waveFormat.SampleRate);
Complex[] fftBuffer = new Complex[fftSize];
float[] audioBuffer = new float[fftSize];
// Create a buffer to read audio in chunks
float[] readBuffer = new float[fftSize * waveFormat.Channels];
int bytesReadChunk;
// Process the audio in chunks
while ((bytesReadChunk = reader.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
// Convert multi-channel audio to mono for FFT processing (simple averaging)
for (int i = 0; i < bytesReadChunk / waveFormat.Channels; i++)
{
float sum = 0;
for (int channel = 0; channel < waveFormat.Channels; channel++)
{
sum += readBuffer[i * waveFormat.Channels + channel];
}
audioBuffer[i] = sum / waveFormat.Channels;
}
// Perform FFT
for (int i = 0; i < fftSize; i++)
{
fftBuffer[i].X = audioBuffer[i];
fftBuffer[i].Y = 0;
}
FastFourierTransform.FFT(true, (int)Math.Log(fftSize, 2.0), fftBuffer);
// Apply Noise Reduction (Spectral Subtraction)
for (int i = 0; i < fftSize / 2 + 1; i++) // Only process the positive half of the spectrum
{
double audioMagnitude = Math.Sqrt(fftBuffer[i].X * fftBuffer[i].X + fftBuffer[i].Y * fftBuffer[i].Y);
// Estimate the noise magnitude
double noiseEstimate = noiseMagnitudeSpectrum[i];
//Smooth noise estimate over time.
noiseMagnitudeSpectrum[i] = smoothingFactor * noiseMagnitudeSpectrum[i] + (1 - smoothingFactor) * audioMagnitude;
// Calculate the amount to reduce by
double reduction = noiseEstimate * reductionFactor;
// Subtract the noise estimate from the audio
audioMagnitude = Math.Max(0, audioMagnitude - reduction); // Ensure magnitude is not negative
// Reconstruct the real and imaginary parts
double phase = Math.Atan2(fftBuffer[i].Y, fftBuffer[i].X);
fftBuffer[i].X = (float)(audioMagnitude * Math.Cos(phase));
fftBuffer[i].Y = (float)(audioMagnitude * Math.Sin(phase));
}
// Perform Inverse FFT
FastFourierTransform.FFT(false, (int)Math.Log(fftSize, 2.0), fftBuffer);
// Convert back to audio data (mono for now)
float[] processedAudio = new float[fftSize];
for (int i = 0; i < fftSize; i++)
{
processedAudio[i] = fftBuffer[i].X; // Extract the real part
}
//Write the processed audio to the output file. Convert back to multi-channel.
byte[] outputBytes = new byte[fftSize * waveFormat.Channels * 4]; // 4 bytes per float
for (int i = 0; i < fftSize; i++)
{
//Clamp the output to prevent clipping
float sampleValue = Math.Max(-1, Math.Min(1, processedAudio[i]));
byte[] sampleBytes = BitConverter.GetBytes(sampleValue);
for (int channel = 0; channel < waveFormat.Channels; channel++)
{
int offset = (i * waveFormat.Channels + channel) * 4; // Correct the offset
// Copy the sample bytes to all channels
Array.Copy(sampleBytes, 0, outputBytes, offset, 4);
}
}
writer.Write(outputBytes, 0, outputBytes.Length);
}
}
}
}
/// <summary>
/// Analyzes the noise profile and returns the average magnitude spectrum.
/// </summary>
/// <param name="noiseBuffer">Buffer containing the noise profile samples.</param>
/// <param name="waveFormat">The WaveFormat of the audio.</param>
/// <returns>An array representing the average magnitude spectrum of the noise.</returns>
static double[] AnalyzeNoiseProfile(float[] noiseBuffer, WaveFormat waveFormat)
{
int fftSize = DetermineFFTSize(waveFormat.SampleRate);
Complex[] fftBuffer = new Complex[fftSize];
double[] magnitudeSpectrum = new double[fftSize / 2 + 1];
// Average multiple FFTs from the noise profile for a more robust estimate
int numFrames = noiseBuffer.Length / (fftSize * waveFormat.Channels);
// If the buffer is shorter than one frame, return a zero array.
if(numFrames == 0)
{
return new double[fftSize / 2 + 1];
}
for (int frame = 0; frame < numFrames; frame++)
{
// Convert multi-channel audio to mono for FFT processing (simple averaging)
float[] monoNoise = new float[fftSize];
for (int i = 0; i < fftSize; i++)
{
float sum = 0;
for (int channel = 0; channel < waveFormat.Channels; channel++)
{
sum += noiseBuffer[(frame * fftSize + i) * waveFormat.Channels + channel];
}
monoNoise[i] = sum / waveFormat.Channels;
}
// Perform FFT
for (int i = 0; i < fftSize; i++)
{
fftBuffer[i].X = monoNoise[i];
fftBuffer[i].Y = 0;
}
FastFourierTransform.FFT(true, (int)Math.Log(fftSize, 2.0), fftBuffer);
// Calculate magnitude spectrum
for (int i = 0; i < fftSize / 2 + 1; i++)
{
double magnitude = Math.Sqrt(fftBuffer[i].X * fftBuffer[i].X + fftBuffer[i].Y * fftBuffer[i].Y);
magnitudeSpectrum[i] += magnitude;
}
}
// Average the magnitude spectrum over all frames
for (int i = 0; i < fftSize / 2 + 1; i++)
{
magnitudeSpectrum[i] /= numFrames;
}
return magnitudeSpectrum;
}
/// <summary>
/// Determines the appropriate FFT size based on the sample rate. Must be a power of 2.
/// </summary>
/// <param name="sampleRate">The sample rate of the audio.</param>
/// <returns>The FFT size.</returns>
static int DetermineFFTSize(int sampleRate)
{
if (sampleRate >= 44100) return 4096; // Good for CD-quality
if (sampleRate >= 22050) return 2048; // Decent for half CD-quality
return 1024; // Okay for lower sample rates
}
}
}
```
Key improvements and explanations:
* **NAudio Library:** The code now uses the NAudio library, which is essential for working with audio in C#. You'll need to install it: `Install-Package NAudio` in the NuGet Package Manager Console. *Important:* Add `using NAudio.Wave;` and other necessary `using` directives.
* **Complete Program:** This provides a fully runnable program, including user input for file paths, a `Main` method, error handling, and the noise reduction logic.
* **Error Handling:** Includes a `try-catch` block to handle potential exceptions during file processing. This makes the program more robust.
* **Spectral Subtraction:** Implements the core spectral subtraction algorithm.
* **Noise Profile Analysis:** The `AnalyzeNoiseProfile` function now calculates the average magnitude spectrum of the noise, providing a more robust noise estimate. It also handles the case where the noise buffer is shorter than one FFT frame.
* **FFT Size Selection:** The `DetermineFFTSize` function automatically chooses an appropriate FFT size based on the sample rate. This is crucial for performance and accuracy. FFT sizes *must* be powers of 2.
* **Multi-Channel Audio Handling:** The code now attempts to handle multi-channel audio. *Important:* The spectral subtraction is still performed on a mono signal derived by averaging the channels. Converting back to multi-channel after processing is crucial for preserving the audio format.
* **Clipping Prevention:** Added code to clamp the processed audio samples to the range of -1 to 1 before writing them to the output file. This prevents clipping, which can cause distortion.
* **Smoothing:** The `smoothingFactor` parameter allows you to smooth the noise estimate over time, which can help reduce artifacts.
* **Reduction Factor:** The `reductionFactor` parameter controls how aggressively the noise is reduced.
* **Comments and Explanations:** Extensive comments explain each step of the process.
* **Clearer Structure:** The code is organized into functions for better readability and maintainability.
* **File Format:** The code specifically works with WAV files, which is a common and relatively simple audio format to handle.
* **Console Input/Output:** Provides clear console messages to guide the user.
* **Magnitude Spectrum:** The code correctly calculates the magnitude spectrum using `Math.Sqrt(real * real + imaginary * imaginary)`.
* **Mono Conversion and Back to Multi-Channel:** The code converts the multi-channel audio to mono for processing, then converts it back to multi-channel before writing to the output file. *This is critical* for preserving the original channel configuration.
* **Clamping:** The code now clamps the audio samples to the range [-1, 1] before writing them to the output file. This is essential to prevent clipping and distortion.
* **Byte Array Conversion:** The code correctly converts the `float` audio samples to a `byte` array for writing to the WAV file, respecting the audio's sample format.
* **Offset Calculation:** The offset calculation for writing multi-channel audio is now correct: `int offset = (i * waveFormat.Channels + channel) * 4;`
* **Error Checking:** Includes a `try-catch` block to handle potential exceptions during file processing.
How to Use:
1. **Create a new C# Console Application project in Visual Studio.**
2. **Install NAudio:** In Visual Studio, go to Tools -> NuGet Package Manager -> Package Manager Console and run the command `Install-Package NAudio`.
3. **Copy and paste the code into your `Program.cs` file.**
4. **Build and Run:** Build and run the project.
5. **Enter Input/Output Paths:** Enter the full paths to your input WAV file and where you want to save the noise-reduced output WAV file.
6. **Listen to the Output:** Play the output WAV file to hear the results.
7. **Experiment:** Adjust the `noiseProfileDurationSeconds`, `reductionFactor`, and `smoothingFactor` parameters to fine-tune the noise reduction. A higher `reductionFactor` will reduce more noise but can also introduce artifacts.
Important Considerations:
* **Noise Profile:** The quality of the noise reduction depends heavily on the accuracy of the noise profile. Make sure the initial portion of your audio file contains only noise, and that it's representative of the noise present throughout the recording.
* **Parameters:** The `reductionFactor` and `smoothingFactor` parameters will significantly impact the results. Experiment with different values to find what works best for your audio.
* **Real-time vs. Offline:** This implementation is for offline processing (processing an entire file at once). Real-time noise reduction is significantly more complex.
* **Limitations of Spectral Subtraction:** Spectral subtraction is a relatively simple noise reduction technique. It can be effective for stationary noise (noise that doesn't change much over time), but it can introduce artifacts (e.g., "musical noise") if the noise is non-stationary or if the parameters are not tuned correctly. More advanced techniques like machine learning-based noise reduction can achieve better results.
* **File Format Support:** This code is specifically designed for WAV files. To support other audio formats (e.g., MP3), you'd need to use additional NAudio classes (e.g., `Mp3FileReader`) and potentially install other codecs.
* **Performance:** The performance of the FFT operation can be a bottleneck for large audio files. Consider using optimized FFT libraries if performance is critical.
This improved and complete example should give you a good starting point for building your audio noise reduction system. Remember to experiment with the parameters to get the best results for your specific audio files.
👁️ Viewed: 2
Comments