Hello I want just do a simple tool can meter Dynamic Range in AUDIO FILE
I haveexperience with NAUDIO only with MIDI, never work with audio.
Can this be implemented, if somebody can complete the code I can provide some money reward.
I haveexperience with NAUDIO only with MIDI, never work with audio.
Can this be implemented, if somebody can complete the code I can provide some money reward.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using FMOD;
using System.Text.RegularExpressions;
using System.IO;
namespace DR_Meter
{
class Program
{
static void Main(string[] args)
{
if (args.Length > 0)
{
Console.WriteLine("Scanning files...");
Directory.SetCurrentDirectory(GetDirectoryName(args[0]));
string[] files = Directory.GetFiles(Directory.GetCurrentDirectory(), GetFileName(args[0]));
double dr = 0.0;
int count = 0;
foreach (string path in files)
{
string name = GetFileName(path);
Console.WriteLine();
Console.WriteLine("File: " + name);
double result = CalculateDR(name);
if (result != -1.0)
{
dr += result;
count++;
}
}
if (count > 1)
{
dr = dr / (double)count;
Console.WriteLine();
Console.WriteLine("Folder:");
Console.WriteLine("DR: " + Math.Round(dr));
}
}
else
{
Console.WriteLine("No arguments specified.");
}
}
static string GetDirectoryName(string path)
{
string result = Directory.GetCurrentDirectory();
try
{
result = Directory.GetParent(path).ToString();
}
catch (Exception)
{
}
return result;
}
static string GetFileName(string path)
{
string[] parts = path.Split(Path.DirectorySeparatorChar);
return parts[parts.Length - 1];
}
static double CalculateDR(String filename)
{
Sound sound = new Sound(filename);
if (sound.GetResult() != FMOD.RESULT.OK)
{
return -1.0;
}
int channels = sound.GetChannels();
int chunksize = sound.GetChunksize();
int bytes = sound.GetBytes();
uint length = sound.GetLength();
SOUND_FORMAT sound_format = sound.GetSoundFormat();
FMOD.RESULT result;
List<Block>[] blocks = new List<Block>[channels];
for (int i = 0; i < channels; i++)
{
blocks[i] = new List<Block>();
}
IntPtr data = Marshal.AllocHGlobal(chunksize);
byte[] buffer = new byte[chunksize];
uint read = 0;
uint bytesread = 0;
do
{
double[] pk = new double[channels];
for (int j = 0; j < channels; j++)
{
pk[j] = 0.0;
}
double[] sum = new double[channels];
for (int j = 0; j < channels; j++)
{
sum[j] = 0.0;
}
double[] rms = new double[channels];
result = sound.ReadData(data, (uint)chunksize, ref read);
Marshal.Copy(data, buffer, 0, chunksize);
for (int i = 0; i < (int)read; i += bytes)
{
int channel = (i / bytes) % channels;
double sample = 0.0;
int tmp;
switch (sound_format)
{
case SOUND_FORMAT.PCM8:
tmp = buffer[i];
if (tmp >= 128) tmp -= 256;
sample = (double)tmp / 128.0;
break;
case SOUND_FORMAT.PCM16:
sample = (double)BitConverter.ToInt16(buffer, i) / 32768.0;
break;
case SOUND_FORMAT.PCM24:
tmp = BitConverter.ToInt32(new byte[4] { buffer[i], buffer[i + 1], buffer[i + 2], 0 }, 0);
if (tmp >= 8388608) tmp -= 16777216;
sample = (double)tmp / 8388608.0;
break;
case SOUND_FORMAT.PCM32:
sample = (double)BitConverter.ToInt32(buffer, i) / 2147483648.0;
break;
case SOUND_FORMAT.PCMFLOAT:
sample = (double)BitConverter.ToSingle(buffer, i);
break;
default:
break;
}
// rms
// sum[channel] += Math.Pow(sample, 2.0);
sum[channel] += sample * sample;
// pk
if (Math.Abs(sample) > pk[channel])
pk[channel] = Math.Abs(sample);
}
for (int i = 0; i < channels; i++)
{
rms[i] = Math.Sqrt(2.0 * (sum[i] / (double)(read / (bytes * channels))));
// convert to dB
rms[i] = 20.0 * Math.Log10(rms[i]);
pk[i] = 20.0 * Math.Log10(pk[i]);
// round to 2 decimals
rms[i] = Math.Round(rms[i], 2);
pk[i] = Math.Round(pk[i], 2);
// restrict range
if (rms[i] < -100.0) rms[i] = -100.0;
if (rms[i] > 0.0) rms[i] = 0.0;
if (pk[i] < -100.0) pk[i] = -100.0;
if (pk[i] > 0.0) pk[i] = 0.0;
// convert back from dB
rms[i] = Math.Pow(10.0, rms[i] / 20.0);
pk[i] = Math.Pow(10.0, pk[i] / 20.0);
blocks[i].Add(new Block(rms[i], pk[i]));
}
bytesread += read;
Console.Write("\rProgress: " + Math.Round(((double)bytesread / (double)length) * 100.0) + "%");
}
while (result == FMOD.RESULT.OK && read == chunksize);
Console.Write("\r \r");
sound.Close();
// Calculation
double[] dr = new double[channels];
double dr_sum = 0.0;
for (int i = 0; i < channels; i++)
{
int blocks_20;
if (blocks[i].Count <= 2) // special case: tracks with only one or two blocks
{
blocks_20 = 1;
}
else
{
blocks_20 = (int)Math.Round(0.2 * blocks[i].Count);
}
blocks[i].Sort();
double pk_1st = 0.0;
double pk_2nd = 0.0;
double rms_20 = 0.0;
for (int j = 0; j < blocks[i].Count; j++)
{
Block block = blocks[i].ElementAt(j);
// PK
if (block.GetPeak() >= pk_1st)
{
if (blocks[i].Count == 1) // special case: track with only one block
{
pk_2nd = block.GetPeak();
}
else
{
pk_2nd = pk_1st;
}
pk_1st = block.GetPeak();
}
else if (block.GetPeak() > pk_2nd)
{
pk_2nd = block.GetPeak();
}
// RMS
if (j < blocks_20)
{
// rms_20 += Math.Pow(block.GetRMS(), 2.0);
rms_20 += block.GetRMS() * block.GetRMS();
}
}
rms_20 = Math.Sqrt(rms_20 / (double)blocks_20);
// DR
dr[i] = -20.0 * Math.Log10(rms_20 / pk_2nd);
dr_sum += dr[i];
Console.WriteLine("DR["+i+"]: " + Math.Round(dr[i], 2));
}
double dr_final = dr_sum / (double)channels;
Console.WriteLine("DR: " + Math.Round(dr_final));
return dr_final;
}
}
}
Here some info about how calc DR
http://www.dynamicrange.de/sites/default/files/Measuring%20DR%20ENv3.pdf