Skip to content

Instantly share code, notes, and snippets.

@DomDomHaas
Created May 9, 2014 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save DomDomHaas/f0a619b7fc0e6fc805a9 to your computer and use it in GitHub Desktop.
Save DomDomHaas/f0a619b7fc0e6fc805a9 to your computer and use it in GitHub Desktop.
Spectrum Analyzer converted from c++ to c#
using System;
/*
* main.cpp - spectrum_analyzer
* https://bitbucket.org/andor44/spectrum_analyzer
*
* Copyright (c) 2011 TÖRÖK Attila <torokati44@gmail.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
/*
* forked by andor44, same license applies
*/
public static class GlobalMembersConstants
{
// this file is public domain
public static readonly uint[] pot = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152};
public const uint num_threads = 2;
// ---- CONFIGURATION ----
public static uint window_size = 2048; // should be a power of two
// ^ Erm, not the rendering window that displays the spectrum,
// but the window of samples to be analyzed at once.
public static uint band_width = 16; // should be an integer divisor of window_size/2
public static float hamming_alpha = 0.54f;
public static float height_coeff = 0.005f;
public static float fading_coeff = 0.5f;
internal static void remove_whitespace(string str)
{
for (int i = 0;i < str.Length;i++)
{
if (str[i] == ' ')
{
str = str.Remove(i,1);
i--;
}
}
}
//C++ TO C# CONVERTER TODO TASK: The original C++ template specifier was replaced with a C# generic specifier, which may not produce the same behavior:
//ORIGINAL LINE: template <class T, uint N>
public static uint size<T, uint N>(T[] x)
{
return N;
}
internal static bool is_pot(uint val)
{
uint len = GlobalMembersConstants.size(pot);
for (uint i = 0; i < len; ++i)
{
if (pot[i] == val)
{
return true;
}
}
return false;
}
internal static string int_to_str(int val)
{
std.stringstream s = new std.stringstream();
s << val;
return s.str();
}
internal static string conv_str = new string(new char[200]);
internal static string construct_text(sf.Sound snd, uint si)
{
conv_str = string.Format("Offset: {0,4:f2} \nVolume: {1:f0} \nStarting[]: {2:D}", snd.GetPlayingOffset(), snd.GetVolume(), si);
return (string)conv_str;
}
}
public static class GlobalMembersMain
{
internal static void load_cfg(string loc)
{
std.ifstream reader = new std.ifstream();
reader.open(loc);
while (!reader.eof())
{
string line;
//reader >> line;
getline(reader,line);
//std::cout << line << std::endl;
string key;
string value;
if (line[0] == '#') // the whole line is a comment
continue;
else if (!(line.IndexOf("#") == string.npos)) // line contains a comment, ignore everything after the comment
{
line = line.Substring(0,line.IndexOf("#"));
}
// we still assume it's not good
if (!(line.IndexOf("=") == string.npos))
{
key = line.Substring(0,line.IndexOf("=")); // the part before the equation sign is the key
GlobalMembersConstants.remove_whitespace(key);
// we oughta have our key by now
value = line.Substring(line.IndexOf("=") + 1, line.Length);
GlobalMembersConstants.remove_whitespace(value);
if (key.CompareTo("window_size") == 0)
{
uint v = Convert.ToInt32(value);
if (GlobalMembersConstants.is_pot(v))
{
GlobalMembersConstants.window_size = v;
}
Console.Write("setting window_size to: ");
Console.Write(v);
Console.Write("\n");
}
else if (key.CompareTo("band_width") == 0)
{
uint v = Convert.ToInt32(value);
if (GlobalMembersConstants.is_pot(v))
{
GlobalMembersConstants.band_width = v;
}
Console.Write("setting band_width to: ");
Console.Write(v);
Console.Write("\n");
}
else if (key.CompareTo("hamming_alpha") == 0)
{
float v = Convert.ToDouble(value);
if (v > 0.0f && v < 1.0f)
{
GlobalMembersConstants.hamming_alpha = v;
}
Console.Write("setting hamming_alpha to: ");
Console.Write(v);
Console.Write("\n");
}
else if (key.CompareTo("height_coeff") == 0)
{
float v = Convert.ToDouble(value);
if (v > 0.0f) // we allow semi-sane values, negative values wouldn't make sense
{
GlobalMembersConstants.height_coeff = v;
}
Console.Write("setting height_coeff to: ");
Console.Write(v);
Console.Write("\n");
}
else if (key.CompareTo("fading_coeff") == 0)
{
float v = Convert.ToDouble(value);
if (v > 0.0f && v <= 1.0f) // 1f means it's never fading, meh
{
GlobalMembersConstants.fading_coeff = v;
}
Console.Write("setting fading_coeff to: ");
Console.Write(v);
Console.Write("\n");
}
else
{
Console.Write("Unknown key '" + key + "'. Ignoring.\n");
}
// these will do for now
}
}
reader.close();
}
static int Main(int argc, string[] args)
{
// ---- LOADING SOUND ----
// obsolete, will redo later
bool display_stats = true;
if (argc != 2)
{
Console.Write("usage: <filename>");
Console.Write("\n");
return 4;
}
string filename = args[1];
Console.Write("Reading config file...");
Console.Write("\n");
GlobalMembersMain.load_cfg("config.cfg");
// it must be defined here, otherwise it'll be strrev("kcuf")(tm)'d
uint num_bands = GlobalMembersConstants.window_size / GlobalMembersConstants.band_width / 2;
Console.Write("Loading sound file...");
std.cout.flush();
sf.SoundBuffer sound_buffer = new sf.SoundBuffer();
if (!sound_buffer.LoadFromFile(filename))
{
Console.Write("\nFailed to load the sound file! Exiting.\n");
Console.Write("Bye!\n");
return 1;
}
if ((sound_buffer.GetChannelsCount() != 2) && (sound_buffer.GetChannelsCount() != 1))
{
Console.Write("\nToo many channels! Exiting.\n");
Console.Write("Bye!\n");
return 2;
}
Console.Write("OK!\n");
Console.Write(sound_buffer.GetSamplesCount());
Console.Write(" samples\n");
Console.Write(sound_buffer.GetChannelsCount());
Console.Write(" channels\n");
Console.Write(sound_buffer.GetSampleRate());
Console.Write(" Hz sampling rate\n");
Console.Write(sound_buffer.GetDuration());
Console.Write(" seconds long\n\n");
// ---- INITIALIZING ANALYZER ----
Console.Write("Allocating buffers...");
std.cout.flush();
float[] float_samples = new float[sound_buffer.GetSamplesCount() / sound_buffer.GetChannelsCount()];
if (float_samples == 0)
{
Console.Write("\nNot enough memory! Exiting.\n");
return 3;
}
float[] hamming_window = new float[GlobalMembersConstants.window_size];
if (hamming_window == 0)
{
Console.Write("\nNot enough memory! Exiting.\n");
float_samples = null;
Console.Write("Bye!\n");
return 3;
}
float[] bands = new float[num_bands];
if (bands == 0)
{
Console.Write("\nNot enough memory! Exiting.\n");
float_samples = null;
hamming_window = null;
Console.Write("Bye!\n");
return 3;
}
// spawn threads
if (!fftw_init_threads())
{
Console.Write("Unable to spawn threads. Quitting.");
Console.Write("\n");
return 1;
}
fftw_plan_with_nthreads(GlobalMembersConstants.num_threads);
fftw_complex in_buffer;
fftw_complex out_buffer;
in_buffer = (fftw_complex) fftw_malloc(sizeof(fftw_complex) * GlobalMembersConstants.window_size);
if (in_buffer == 0)
{
Console.Write("\nNot enough memory! Exiting.\n");
float_samples = null;
hamming_window = null;
bands = null;
Console.Write("Bye!\n");
fftw_cleanup_threads();
return 3;
}
out_buffer = (fftw_complex) fftw_malloc(sizeof(fftw_complex) * GlobalMembersConstants.window_size);
if (out_buffer == 0)
{
Console.Write("\nNot enough memory! Exiting.\n");
float_samples = null;
hamming_window = null;
bands = null;
fftw_free(in_buffer);
Console.Write("Bye!\n");
fftw_cleanup_threads();
return 3;
}
Console.Write("OK!\n\n");
Console.Write("Initializing buffers...");
std.cout.flush();
for (uint i = 0; i < GlobalMembersConstants.window_size; ++i)
{
hamming_window[i] = GlobalMembersConstants.hamming_alpha - (1.0f - GlobalMembersConstants.hamming_alpha) * Math.Cos((2.0f * Math.PI * i) / (GlobalMembersConstants.window_size - 1.0f));
}
if (sound_buffer.GetChannelsCount() == 1)
{
for (uint i = 0; i < sound_buffer.GetSamplesCount(); ++i)
{
float_samples[i] = sound_buffer.GetSamples()[i] / ((sound_buffer.GetSamples()[i] > 0) ? 32767.0f : 32768.0f);
}
}
else
{
for (uint i = 0; i < sound_buffer.GetSamplesCount() / 2; ++i)
{
float_samples[i] = (sound_buffer.GetSamples()[2 * i] + sound_buffer.GetSamples()[2 * i + 1]);
float_samples[i] /= ((float_samples[i] > 0) ? 65534.0f : 65536.0f);
}
}
Console.Write("OK!\n\n");
Console.Write("Planning DFT...");
std.cout.flush();
fftw_plan plan = fftw_plan_dft_1d(GlobalMembersConstants.window_size, in_buffer, out_buffer, FFTW_FORWARD, FFTW_PATIENT);
Console.Write("OK!\n\n");
sf.RenderWindow win = new sf.RenderWindow(sf.VideoMode(1024,768), "Spectrum analyzer by torokati44, forked by andor44", sf.Style.Titlebar | sf.Style.Close);
win.UseVerticalSync(true);
win.PreserveOpenGLStates(true);
win.Clear();
sf.PostFX post_fx = new sf.PostFX();
if (sf.PostFX.CanUsePostFX())
{
// nemtom mi köze van az inceptionhoz de biztos :D vagy arra a githubos példaprogramra gondolsz?
// Inception, huh?! :D
post_fx.LoadFromMemory("texture frame\n" + "float coeff\n" + "\n" + "effect\n" + "{\n" + " _out = frame(_in) * coeff;\n" + " _out.a = 1.0;" + "}\n");
post_fx.SetTexture("frame", null);
post_fx.SetParameter("coeff", GlobalMembersConstants.fading_coeff);
}
Console.Write("Starting the main loop.\n\n");
// ---- MAIN LOOP ----
sf.Sound sound = new sf.Sound(sound_buffer);
sound.SetVolume(85);
sound.Play();
sf.String text = new sf.String(); // TODO: REMOVE , duration;
sf.Font f = new sf.Font();
f.LoadFromFile("/usr/share/fonts/TTF/TerminusMedium.ttf", 64);
text.SetFont(f);
text.SetSize(32);
text.SetPosition(12,4);
sf.Shape dur = new sf.Shape();
sf.Shape offs = new sf.Shape();
dur = sf.Shape.Rectangle(sf.Vector2f(20,720), sf.Vector2f(1001,740), sf.Color(250,10,10,200), 3.0f, sf.Color(250,10,10,100));
dur.EnableFill(false);
sf.Event event = new sf.Event();
while ((sound.GetStatus() == sf.Sound.Playing || sound.GetStatus() == sf.Sound.Paused) && win.IsOpened())
{
while (win.GetEvent(event))
{
switch (event.Type)
{
case sf.Event.Closed:
{
win.Close();
}
break;
case sf.Event.KeyPressed:
{
switch (event.Key.Code)
{
case sf.Key.Escape:
{
win.Close();
}
break;
case sf.Key.Space:
sound.GetStatus() == sf.Sound.Paused ? sound.Play() : sound.Pause();
break;
case sf.Key.F1:
display_stats = !display_stats;
break;
case sf.Key.Right:
{
float pos = sound.GetPlayingOffset() + 5.0f;
Console.Write("Seeking to ");
Console.Write(pos);
Console.Write(" (+5)");
sound.SetPlayingOffset(pos);
}
break;
case sf.Key.Left:
{
float pos = sound.GetPlayingOffset() - 5.0f;
Console.Write("Seeking to ");
Console.Write(pos);
Console.Write(" (-5)");
sound.SetPlayingOffset(pos);
}
break;
default:
{
}
break;
}
}
break;
case sf.Event.MouseButtonPressed:
{
float pos = sound_buffer.GetDuration() * ((float)event.MouseButton.X / (float)win.GetWidth());
Console.Write("Seeking to ");
Console.Write(pos);
Console.Write(" seconds.\n");
sound.SetPlayingOffset(pos);
}
break;
case sf.Event.MouseWheelMoved:
{
if ((event.MouseWheel.Delta < 0 && sound.GetVolume() <= 1) || (event.MouseWheel.Delta > 0 && sound.GetVolume() >= 98))
{
break;
}
else
{
sound.SetVolume(sound.GetVolume() + event.MouseWheel.Delta * 2);
break;
}
}
break;
default:
{
}
break;
}
}
if (!win.IsOpened())
{
break;
}
uint starting_index = (uint)(sound.GetPlayingOffset() * sound_buffer.GetSampleRate());
if (starting_index > sound_buffer.GetSamplesCount() - GlobalMembersConstants.window_size)
{
starting_index = sound_buffer.GetSamplesCount() - GlobalMembersConstants.window_size;
}
for (uint i = 0; i < GlobalMembersConstants.window_size; ++i)
{
in_buffer[i][0] = float_samples[i + starting_index] * hamming_window[i];
in_buffer[i][1] = 0;
}
fftw_execute(plan);
for (uint i = 0; i < num_bands; ++i)
{
bands[i] = 0.0f;
for (uint j = 0; j < GlobalMembersConstants.band_width; ++j)
{
bands[i] += Math.Sqrt(Math.Pow(out_buffer[i * GlobalMembersConstants.band_width + j][0], 2) + Math.Pow(out_buffer[i * GlobalMembersConstants.band_width + j][1], 2));
}
}
if (sf.PostFX.CanUsePostFX())
{
win.Draw(post_fx); // just fades
}
else
{
win.Clear(); // clears totally
}
glBegin(GL_LINE_STRIP);
for (uint i = 0; i < num_bands; ++i)
{
float x = (float)i / (float)(num_bands - 1); // 0..1
float y = bands[i] * GlobalMembersConstants.height_coeff; // 0..1
float red = (y > 0.5f) ? 1.0f : ((y * 2.0f));
float green = (y > 0.5f) ? (2.0f - (2.0f * y)) : 1.0f;
glColor3f(red, green, 0.0f);
glVertex2f(x * 2.0f - 1.0f, y * 2.0f - 1.0f); // -1..1
}
glEnd();
if (display_stats)
{
text.SetText(GlobalMembersConstants.construct_text(sound, starting_index));
offs = sf.Shape.Rectangle(sf.Vector2f(22,722), sf.Vector2f(22 + 978 * (sound.GetPlayingOffset() / sound_buffer.GetDuration()), 738), sf.Color(250,10,10,120));
offs.EnableFill(true);
win.Draw(offs);
win.Draw(dur);
win.Draw(text);
}
win.Display();
}
// ---- UNINITIALIZING ----
Console.Write("\nPeacefully freeing everything...\n");
fftw_destroy_plan(plan);
fftw_free(in_buffer);
fftw_free(out_buffer);
fftw_cleanup();
fftw_cleanup_threads();
float_samples = null;
hamming_window = null;
bands = null;
win.Close();
Console.Write("Bye!\n");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment