Question: How to use RecorderEx and PlayerEx in console application or Windows Service?
Answer: In order to use RecorderEx and PlayerEx in a console application or Windows Service need to create instances of these classes by using a parameterized constructor: new RecorderEx(true) and new PlayerEx(true), respectively. The same is true for multi-threaded recorder and player.
Below is an example of full duplex Recorder and Player.
using System;
using Alvas.Audio;
namespace AudioConsCs
{
class Program
{
static void Main(string[] args)
{
rex.Data += new RecorderEx.DataEventHandler(rex_Data);
rex.Open += new EventHandler(rex_Open);
rex.Close += new EventHandler(rex_Close);
rex.Format = pcmFormat;
rex.StartRecord();
Console.WriteLine("Please press enter to exit!");
Console.ReadLine();
rex.StopRecord();
}
static RecorderEx rex = new RecorderEx(true);
static PlayerEx play = new PlayerEx(true);
static IntPtr pcmFormat = AudioCompressionManager.GetPcmFormat(1, 16, 44100);
static void rex_Open(object sender, EventArgs e)
{
play.OpenPlayer(pcmFormat);
play.StartPlay();
}
static void rex_Close(object sender, EventArgs e)
{
play.ClosePlayer();
}
static void rex_Data(object sender, DataEventArgs e)
{
byte[] data = e.Data;
play.AddData(data);
}
}
}
Below is an example of multithreaded full duplex Recorder and Player in Windows Service.
/*
* rem run cmd as administrator
* rem install service
* \WindowsMicrosoft.NETFrameworkv2.0.50727installutil AudioWindowsServiceCs.exe
* rem uninstall service
* \WindowsMicrosoft.NETFrameworkv2.0.50727installutil AudioWindowsServiceCs.exe /u
*/
using System;
using System.ServiceProcess;
using System.Threading;
using Alvas.Audio;
namespace AudioWindowsServiceCs
{
public partial class AudioService : ServiceBase
{
public AudioService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Thread t = new Thread(Start);
t.Start();
}
protected override void OnStop()
{
rex.StopRecord();
}
static void Start()
{
rex.Data += new RecorderEx.DataEventHandler(rex_Data);
rex.Open += new EventHandler(rex_Open);
rex.Close += new EventHandler(rex_Close);
rex.Format = pcmFormat;
rex.StartRecord();
}
static RecorderEx rex = new RecorderEx(true);
static PlayerEx play = new PlayerEx(true);
static IntPtr pcmFormat = AudioCompressionManager.GetPcmFormat(1, 16, 44100);
static void rex_Open(object sender, EventArgs e)
{
play.OpenPlayer(pcmFormat);
play.StartPlay();
}
static void rex_Close(object sender, EventArgs e)
{
play.ClosePlayer();
}
static void rex_Data(object sender, DataEventArgs e)
{
byte[] data = e.Data;
play.AddData(data);
}
}
}
Question: I found that your vox converter doesn't handle vox files from the Orator phone dictation system right.
Answer: There are so many questions about Vox format. What is the Vox format? Vox audio file format is a headerless audio format data, which generally contains Dialogic_ADPCM, but may also include A-law, Mu-law, PCM, and any other type of audio data. It means that vox file doesn't have the header and extention ".vox" doesn't say anything at all. Therefore it is very important to know in which format you have the audio data in the vox file!
See code below for the different types of Vox files.
//Dialogic_ADPCM
private static void Vox2Wav(string voxFile, int samplesPerSec)
{
BinaryReader br = new BinaryReader(File.OpenRead(voxFile));
IntPtr format = AudioCompressionManager.GetPcmFormat(1, 16, samplesPerSec);
WaveWriter ww = new WaveWriter(File.Create(voxFile + ".wav"),
AudioCompressionManager.FormatBytes(format));
Vox.Vox2Wav(br, ww);
br.Close();
ww.Close();
}
private void VoxConv2(string voxFile)
{
//CCITT u-Law, 8000 Hz, 1 channels
WaveFormat wf = new WaveFormat();
wf.wFormatTag = AudioCompressionManager.MuLawFormatTag;
wf.nChannels = 1;
wf.nSamplesPerSec = 8000;
FormatDetails[] formatList = AudioCompressionManager.GetFormatList(wf);
IntPtr format = formatList[0].FormatHandle;
RawReader rr = new RawReader(File.OpenRead(voxFile), format);
byte[] data = rr.ReadData();
rr.Close();
WaveWriter ww = new WaveWriter(File.Create(voxFile + ".wav"),
AudioCompressionManager.FormatBytes(format));
ww.WriteData(data);
ww.Close();
}
static void Raw2Wav(string fileName)
{
//A-Law mono at 8000 HZ
WaveFormat wf = new WaveFormat();
wf.wFormatTag = AudioCompressionManager.ALawFormatTag;
wf.nChannels = 1;
wf.nSamplesPerSec = 8000;
FormatDetails[] formatList = AudioCompressionManager.GetFormatList(wf);
IntPtr format = formatList[0].FormatHandle;
RawReader rr = new RawReader(File.OpenRead(fileName), format);
byte[] data = rr.ReadData();
rr.Close();
WaveWriter ww = new WaveWriter(File.Create(fileName + ".wav"),
AudioCompressionManager.FormatBytes(format));
ww.WriteData(data);
ww.Close();
}
DC offset is an offsetting of a signal from zero. It occurs when hardware, such as a sound card, adds DC offset to the recorded audio signal. DC offset is shift of the red line comparatively to the black one on figure below.
To remove the DC offset is used AudioCompressionManager.RemoveDcOffset method. See code below
static void RemoveDcOffset(string fileName)
{
DsReader dr = new DsReader(fileName);
IntPtr format = dr.ReadFormat();
byte[] data = dr.ReadData();
// Removes DC offset.
byte[] dataNew = AudioCompressionManager.RemoveDcOffset(format, data);
dr.Close();
WaveWriter ww = new WaveWriter(File.Create(fileName + ".wav"),
AudioCompressionManager.FormatBytes(format));
ww.WriteData(dataNew);
ww.Close();
}
Question: Have you got any issues with "Joint-Stereo" MP3's? I have a customer who sends me the attached MP3 and when I convert it to a mono format, it effectively cancels out the audio.
Answer: The problem is that in Mp3 Joint Stereo sound file mirror is opposite. For example the left channel = -1, and the right channel = +1. When converting a file into single channels are merged: -1 + 1 = 0. So you hear the silence. You can solve this problem in that way. Instead of combining the channels keep only one channel (left or right).
The code below solves this problem.
private void Mp3ToUlaw()
{
string FileName = @"d:Work10SBL 6 Year 0511.mp3";
Mp3Reader mr = new Mp3Reader(File.OpenRead(FileName));
IntPtr formatMp3 = mr.ReadFormat();
IntPtr formatPcm = AudioCompressionManager.GetCompatibleFormat(formatMp3, AudioCompressionManager.PcmFormatTag);
byte[] dataMp3 = mr.ReadData();
byte[] dataPcm = AudioCompressionManager.Convert(formatMp3, formatPcm, dataMp3, false);
mr.Close();
IntPtr format8000 = AudioCompressionManager.GetPcmFormat(2, 16, 8000);
byte[] data8000 = AudioCompressionManager.Resample(formatPcm, dataPcm, format8000);
IntPtr formatMono = IntPtr.Zero;
byte[] dataLeft = null;
byte[] dataRight = null;
AudioCompressionManager.SplitStereo(format8000, data8000, ref formatMono, ref dataLeft, ref dataRight);
//mono 8bit 8Khz u-law format
IntPtr format = AudioCompressionManager.GetCompatibleFormat(formatMono, AudioCompressionManager.MuLawFormatTag);
byte[] data = AudioCompressionManager.Convert(formatMono, format, dataLeft, false);
WaveWriter ww = new WaveWriter(File.Create(FileName + ".wav"),
AudioCompressionManager.FormatBytes(format));
ww.WriteData(data);
ww.Close();
}
Very often we have to change the sampling rate, number of channels and the size of a sample when converting from one format to another. You can use AudioCompression.Convert method for these purposes. Unfortunately, for the uncompressed PCM -> PCM this leads to a significant reduction in sound quality and appearance of extraneous noise. We can verify this by example.
private void Convert()
{
string fileName = @"Original.WAV";
IntPtr formatNew = AudioCompressionManager.GetPcmFormat(2, 16, 44100);
string fileNameNew = fileName + ".Convert.Wav";
for (int i = 0; i < 100; i++)
{
WaveReader wr = new WaveReader(File.OpenRead(fileName));
IntPtr format = wr.ReadFormat();
byte[] data = wr.ReadData();
wr.Close();
byte[] dataNew = AudioCompressionManager.Convert(format, formatNew, data, false);
WaveWriter ww = new WaveWriter(File.Create(fileNameNew),
AudioCompressionManager.FormatBytes(formatNew));
ww.WriteData(dataNew);
ww.Close();
fileName = fileNameNew;
//Previous format
formatNew = format;
}
}
Fortunately, Alvas.Audio library has AudioCompression.Resample method. It is a wrapper over Resampler DMO object. If we rewrite the code above using AudioCompression.Resample method, we get much better sound quality.
private void Resample()
{
string fileName = @"Original.WAV";
IntPtr formatNew = AudioCompressionManager.GetPcmFormat(2, 16, 44100);
string fileNameNew = fileName + ".Resample.Wav";
for (int i = 0; i < 100; i++)
{
WaveReader wr = new WaveReader(File.OpenRead(fileName));
IntPtr format = wr.ReadFormat();
byte[] data = wr.ReadData();
wr.Close();
byte[] dataNew = AudioCompressionManager.Resample(format, data, formatNew);
WaveWriter ww = new WaveWriter(File.Create(fileNameNew),
AudioCompressionManager.FormatBytes(formatNew));
ww.WriteData(dataNew);
ww.Close();
fileName = fileNameNew;
//Previous format
formatNew = format;
}
}
You can compare the sound of these 3 files.
As a bonus AudioCompression.Resample method can encode and decode the PCM 24 and 32 bit, more than 2 channels, and also 32-bit IEEE Float audio format.
Summary: For encode and decode a compressed format to PCM AudioCompression.Convert fits perfectly, but for resampling use AudioCompression.Resample method.
Three methods of IAudioReader interface most commonly used.
string fileName = "Your audio file.wav";
IAudioReader ir = new WaveReader(File.OpenRead(fileName));
IntPtr format = ir.ReadFormat();// Reads an audio format.
byte[] data = ir.ReadData();// Reads all audio data from the stream.
ir.Close();// Closes the current reader and the underlying stream.
To read the audio data portions of 1 second, you can use the following code.
string fileName = "Your audio file.mp3";
IAudioReader ir = new Mp3Reader(File.OpenRead(fileName));
IntPtr format = ir.ReadFormat();// Reads an audio format.
int skipSeconds = 0;
const int readSeconds = 1;
while (true)
{
// Reads audio data starts at skipSeconds position and readSeconds duration.
byte[] data = ir.ReadData(skipSeconds, readSeconds);
if (data.Length == 0)
{
break;
}
skipSeconds += readSeconds;
}
ir.Close();// Closes the current reader and the underlying stream.
To read the audio data by portions that are smaller than 1 second, you can use the following code.
string fileName = "Your audio file.pcm";
//Specifies the format of audio data
IntPtr format = AudioCompressionManager.GetPcmFormat(2, 16, 44100);
IAudioReader ir = new RawReader(File.OpenRead(fileName), format);
const int miliseconds = 500;
int skipBytes = 0;
// Converts from milliseconds to bytes for current stream.
int readBytes = ir.Milliseconds2Bytes(miliseconds);
while (true)
{
// Reads audio data starts at skipBytes position and readBytes length.
byte[] data = ir.ReadDataInBytes(skipBytes, readBytes);
if (data.Length == 0)
{
break;
}
skipBytes += readBytes;
}
ir.Close();// Closes the current reader and the underlying stream.
The following code allows to determine the audio stream duration in milliseconds and the audio data size in bytes.
string fileName = "Your audio file.snd";
IAudioReader ir = new AuReader(File.OpenRead(fileName));
// Gets audio stream duration in milliseconds.
int durationInMS = ir.GetDurationInMS();
// Gets audio stream length in bytes.
int lengthInBytes = ir.GetLengthInBytes();
ir.Close();// Closes the current reader and the underlying stream.
Avi files are video files. They may not contain audio data. To determine this AviReader class has HasAudio property. DsReader HasAudio equal false means that the file contains no audio data or audio file format is unknown, ie no suitable DirectShow filter.
using (AviReader ar = new AviReader(File.OpenRead("your.avi")))
{
if (ar.HasAudio)
{
// ...
}
}
using (DsReader dr = new DsReader("Your audio file"))
{
if (dr.HasAudio)
{
// ...
}
}
Audio file formats can be with a header, without header and with header in each packet of audio data.
You can see online demo and example code and Alvas.Audio.
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
"1" Grid.Column="0" >
"pgMain"/>
"wfh" Grid.Row="1" Grid.Column="1" >
private void InitWaveformVisualizer()
{
wfv = new WaveformVisualizer();
wfh.Child = wfv;
}
private WaveformVisualizer wfv;