How to get musicbrainz track information from audio file - c#

Can anyone tell me how to get track information from the MusicBrainz database from an audio file (mp3, wav, wma, ogg, etc...) using audio fingerprinting. I'm using MusicBrainz Sharp library, but any other library is ok.
I've seen that you must use the libofa library, that you can't use MusicBrainz Sharp to get puid from the audio file, but I can't figure out how to use libofa with C#.
Please show some examples and code snippets to help me, because I can't find them anywhere.
Thanks in advance!

The thing is that you can probably use libofa to get a fingerprint of the audio file, but if the file has no PUID available, you will be stuck and will have to use something like genpuid to submit the audio fingerprint to AmpliFIND and wait about a day to get a PUID.
That being said, I tried something similar about two years ago, but kinda lost interest in the project when I failed to write the IDv3 tags, if I remember correctly. However, the source code is available on Bitbucket.
I basically wrapped libofa using a DllImport and also wrapped genpuid (ie. read the output XML) to be able to read the fingerprint and submit the file for fingerprinting if I did not get one from libofa. I also wrote some code that reads information from MusicBrainz using MusicBrainz Sharp.
Well, at least that was what I planned back then, I think. :) I hope this helps you to solve your problem and I'd love to see an update on this.
Edit: I just noticed that I created a bug report for myself, which basically says that I still needed an implementation for my decoder which is probably why I created this question in SO. So I guess I did not implement the genpuid fingerprinter just to be able to do the fingerprint/get the guid, because I did not get the libofa fingerprinter to work correctly.

I did the wrapped genpuid approach suggested above.
private string GetPUID(string fileName)
{
Process p;
ProcessStartInfo si;
string outRow;
string puidReturned;
string gendPuidPath = #"C:\Program Files\genpuid\genpuid.exe";
string gendPuidKey = "your key here";
System.Text.RegularExpressions.Regex puidRex = new System.Text.RegularExpressions.Regex( #"puid: (\S+)" ); // sample: puid: 3c62e009-ec93-1c0f-e078-8829e885df67
System.Text.RegularExpressions.Match m;
if (File.Exists(gendPuidPath))
{
try
{
si = new ProcessStartInfo(gendPuidPath, gendPuidKey + " \"" + fileName + "\"");
si.RedirectStandardOutput = true;
si.UseShellExecute = false;
p = new Process();
p.StartInfo = si;
p.Start();
puidReturned = "";
while ((outRow = p.StandardOutput.ReadLine()) != null)
{
m = puidRex.Match(outRow);
if (m.Success)
puidReturned = m.Groups[1].Value;
Debug.WriteLine(outRow);
}
p.WaitForExit();
p.Close();
return puidReturned;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine(ex.StackTrace);
throw new Exception("Unexpexted Error obtaining PUID for file: " + fileName, ex);
}
}
else
{
Debug.WriteLine("genpuid.exe not found");
return "";
}
}

Related

Compare subfolders name

I'm not sure what the correct question for my case would be but I'll try to describe as good as I can. I have to mention that I don't have much knowledge of this language, I'm using it strictly for the executable of my appplication, mainly I mess around with Java. So I have an app that only starts up if it finds java in my PC. I'm using something like this:
ProcessStartInfo startJava = new ProcessStartInfo("java", JavaProcessArguments());
startJava.CreateNoWindow = !client.ShowConsole;
startJava.UseShellExecute = false;
But, let's say I want to use openJDK, then I would have to change "java" to something like this:
ProcessStartInfo startJava = new ProcessStartInfo
(#"C:\Program Files (x86)\Java\openJDK_1.7\bin\java.exe", JavaProcessArguments());
Moving on, I wanted to start openJDK FIRST, even if java is present, so I wrote a condition that does that:
private void StartTheProcess()
{
string pathJDK = #"C:\Program Files (x86)\Java\openJDK_1.7\bin\";
bool isDirJDK7 = Directory.Exists(pathJDK);
if (isDirJDK7)
{
ProcessStartInfo startJava = new ProcessStartInfo(#"C:\Program Files (x86)\Java\openJDK_1.7\bin\java.exe", JavaProcessArguments());
startJava.CreateNoWindow = !client.ShowConsole;
startJava.UseShellExecute = false;
try
{
using (Process p = Process.Start(startJava))
{
p.WaitForExit();
}
}
catch (Win32Exception ex)
{
some error...
}
catch (Exception e)
{
some error...
}
}
else
{
ProcessStartInfo startJava = new ProcessStartInfo("java", JavaProcessArguments());
startJava.CreateNoWindow = !client.ShowConsole;
startJava.UseShellExecute = false;
try
{
using (Process p = Process.Start(startJava))
{
p.WaitForExit();
}
}
catch (Win32Exception ex)
{
some error...
}
catch (Exception e)
{
some error...
}
}
}
Now let's suppose I have more openJDK versions in the "C:\Program Files (x86)\Java\" folder: openJDK_1.7, openJDK_1.7_u1, openJDK_1.8, so on, and I want to start the latest one. How should I accomplish this? I think one method would be to compare the subfolders names found there but I don't really know how to. The content of all the subfolders is identical and the names of the subfolders have the same construction (openJDK_1.X / openJDK_1.X_uYZ). Could you help me, based on this poorly (most likely) code? :D
There are a few things you could try,
Split the directory name string by
var version = string.split('_'), and then the version would be version[1] = "1.7", you can convert all of these into doubles/decimals/floats,etc and just sort the data out, get the latest version (the one with the highest number and get its directory back
The second thing you can try is checking the Directory.GetLastWriteTime(String), which you can compare, and find the last one, please not that this is not reliable at all since the folder can be changed by anything.

Capturing output from powershell script

I have been working on converting a GUI script from another language to C# in VS2017 for a customer. With help from the folks here I am 95% of the way there, but have run into a couple of snags; just not sure I am doing things in the best way. I'm including just the relevant portions of code below, please let me know if I am not providing enough:
The majority of the code is centered on the wpf form, which collects data for low level technicians to batch deploy a number of Virtual Machines into the VMware environment. This number could easily range into the dozens or even a hundred VMs at once. The information for each VM is specified in the form, then collected in a listview. Once the listview is fully populated it is exported to a csv. Up to this point everything works just fine.
I've next been working on actually launching the powershell/powerCLI script (also working) and capturing output. The log file is opened with a specific reader application the customer uses, which updates in real time, and the captured output is fed to the log. It is important for the technicians to see the output from the code line by line so they can react if there is an issue.
I started with something like this as a test:
string sPSScript = "C:\\Users\\Admin\\Desktop\\TestC#.ps1";
string logFile = "C:\\Users\\Admin\\Desktop\\My.log";
string logReader = "C:\\Users\\Admin\\Documents\\CMTrace.exe";
string standard_output;
System.Diagnostics.Process PSScript = new System.Diagnostics.Process();
PSScript.StartInfo.FileName =
Environment.GetFolderPath(Environment.SpecialFolder.SystemX86) +
"\\WindowsPowerShell\\v1.0\\powershell.exe";
PSScript.StartInfo.Arguments = "-command . '" + sPSScript + "' " +
vCenter.Text;
PSScript.StartInfo.RedirectStandardOutput = true;
PSScript.StartInfo.UseShellExecute = false;
PSScript.Start();
System.Diagnostics.Process LogFile = new System.Diagnostics.Process();
LogFile.StartInfo.FileName = logReader;
LogFile.StartInfo.Arguments = logFile;
LogFile.Start(); while ((standard_output =
PSScript.StandardOutput.ReadLine()) != null)
{
if (standard_output != "")
{
using (StreamWriter file = new StreamWriter(logFile, append: true))
{
file.WriteLine(standard_output);
}
}
}
While this writes to the log file in real time as expected, it creates 100 instances of the logReader application. I understand why, since I am declaring a new StreamWriter object through every pass, but am unsure how better to go about this.
I tried creating the file outside the loop, like this:
StreamWriter file = new StreamWriter(logFile, append: true) { };
System.Diagnostics.Process LogFile = new System.Diagnostics.Process();
LogFile.StartInfo.FileName = logReader;
LogFile.StartInfo.Arguments = logFile;
System.Diagnostics.Process PSScript = new System.Diagnostics.Process();
PSScript.StartInfo.FileName = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86) + "\\WindowsPowerShell\\v1.0\\powershell.exe";
PSScript.StartInfo.Arguments = "-command . '" + sPSScript + "' " + vCenter.Text;
PSScript.StartInfo.RedirectStandardOutput = true;
PSScript.StartInfo.UseShellExecute = false;
LogFile.Start();
PSScript.Start();
System.Threading.Thread.Sleep(1500);
while ((standard_output = PSScript.StandardOutput.ReadLine()) != null)
{
if (standard_output != "")
{
file.WriteLine(standard_output);
}
}
It doesn't create multiple instances, but it also does not update the log file in real time as the previous code does. It only updates once the script runs, and then only partially. The script produces ~1000 lines of output, and I consistently see only about 840 written to the log file.
I thought about doing something like this:
FileStream logFS;
logFS = new FileStream(logFile, FileMode.Append);
but it appears the only options available to me to write to the file are expecting a byte array.
I am sure that I am missing something stupid simple in this, but would appreciate any suggestions on the easiest way to create the log file, open it in the reader, and then update it with the standard output from the powershell script.
why did the previous code writes in real time?
because you are wrapping it with using. And at the end of using block its gonna call dispose which calls .Flush to write to disk
Your second code block calls WriteLine but never called Flush so it writes to the disk whenever the buffer is full. Just add a .Flush call after WriteLine and you will have real time logging

C# Problem Reading Console Output to string

i want to launch ffmpeg from my app and retrive all console output that ffmpeg produces. Thing seems obvious, i followed many forum threads/articles like this one but i have problem, though i follow all information included there I seem to end up in dead end.
String that should contain output from ffmpeg is always empty. I've tried to see where is the problem so i made simple c# console application that only lists all execution parameters that are passed to ffmpeg, just to check if problem is caused by ffmpeg itself. In that case everything work as expected.
I also did preview console window of my app. When i launch ffmpeg i see all the output in console but the function that should recieve that output for further processing reports that string was empty. When my param-listing app is launched the only thing I see is the expected report from function that gets output.
So my question is what to do to get ffmpeg output as i intended at first place.
Thanks in advance
MTH
This is a long shot, but have you tried redirecting StandardError too?
Here is a part of my ffmpeg wrapper class, in particular showing how to collect the output and errors from ffmpeg.
I have put the Process in the GetVideoDuration() function just so you can see everything in the one place.
Setup:
My ffmpeg is on the desktop, ffPath is used to point to it.
namespace ChildTools.Tools
{
public class FFMpegWrapper
{
//path to ffmpeg (I HATE!!! MS special folders)
string ffPath = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\ffmpeg.exe";
//outputLines receives each line of output, only if they are not zero length
List<string> outputLines = new List<string>();
//In GetVideoDuration I only want the one line of output and in text form.
//To get the whole output just remove the filter I use (my search for 'Duration') and either return the List<>
//Or joint the strings from List<> (you could have used StringBuilder, but I find a List<> handier.
public string GetVideoDuration(FileInfo fi)
{
outputLines.Clear();
//I only use the information flag in this function
string strCommand = string.Concat(" -i \"", fi.FullName, "\"");
//Point ffPath to my ffmpeg
string ffPath = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\ffmpeg.exe";
Process processFfmpeg = new Process();
processFfmpeg.StartInfo.Arguments = strCommand;
processFfmpeg.StartInfo.FileName = ffPath;
//I have to say that I struggled for a while with the order that I setup the process.
//But this order below I know to work
processFfmpeg.StartInfo.UseShellExecute = false;
processFfmpeg.StartInfo.RedirectStandardOutput = true;
processFfmpeg.StartInfo.RedirectStandardError = true;
processFfmpeg.StartInfo.CreateNoWindow = true;
processFfmpeg.ErrorDataReceived += processFfmpeg_OutData;
processFfmpeg.OutputDataReceived += processFfmpeg_OutData;
processFfmpeg.EnableRaisingEvents = true;
processFfmpeg.Start();
processFfmpeg.BeginOutputReadLine();
processFfmpeg.BeginErrorReadLine();
processFfmpeg.WaitForExit();
//I filter the lines because I only want 'Duration' this time
string oStr = "";
foreach (string str in outputLines)
{
if (str.Contains("Duration"))
{
oStr = str;
}
}
//return a single string with the duration line
return oStr;
}
private void processFfmpeg_OutData(object sender, DataReceivedEventArgs e)
{
//The data we want is in e.Data, you must be careful of null strings
string strMessage = e.Data;
if outputLines != null && strMessage != null && strMessage.Length > 0)
{
outputLines.Add(string.Concat( strMessage,"\n"));
//Try a Console output here to see all of the output. Particularly
//useful when you are examining the packets and working out timeframes
//Console.WriteLine(strMessage);
}
}
}
}

SAXParser equivalent in C#

I have below java code , I need to convert these in C#, Kindly help me ..
public class Configuration {
private ConfigContentHandler confHandler;
public Configuration() {
}
public boolean parseConfigFile() throws Exception {
boolean bReturn = true;
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
System.out.println("*** Start parsing");
try {
confHandler = new ConfigContentHandler(100);
// Configuration file must be located in main jar file folder
// Set the full Prosper file name
String sConfigFile = "configuration.xml";
// Get abstract (system independent) filename
File fFile = new File(sConfigFile);
if (!fFile.exists()) {
System.out.println("Could not find configuration file " + sConfigFile + ", trying input parameters.");
bReturn = false;
} else if (!fFile.canRead()) {
System.out.println("Could not read configuration file " + sConfigFile + ", trying input parameters.");
bReturn = false;
} else {
parser.parse(fFile, confHandler);
}
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Input error.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("*** End parsing");
return bReturn;
}
Thanks
C# native XML parser XmlReader doesn't support SAX and is forward-only. You may take a look at this article presenting some specific points about it. You could simulate a SAX parser using XmlReader. If it doesn't suit your needs you could also use XDocument which is a different API for working with XML files in .NET. So to conclude there's no push XML parser built into .NET framework so you might need to use a third party library or COM Interop to MSXML to achieve this if you really need an event driven parser.
I used SAX for .NET in two projects successfully in the past.
http://saxdotnet.sourceforge.net/

How can I save first frame of a video as image?

I want to extract first frame of uploaded video and save it as image file.
Possible video formats are mpeg, avi and wmv.
One more thing to consider is that we are creating an ASP.NET website.
You could use FFMPEG as a separate process (simplest way) and let it decode first IDR for you. Here you have a class FFMPEG that has GetThumbnail() method, to it you pass address of video file, address of the JPEG image to be made, and resolution that you want the image to be:
using System.Diagnostics;
using System.Threading;
public class FFMPEG
{
Process ffmpeg;
public void exec(string input, string output, string parametri)
{
ffmpeg = new Process();
ffmpeg.StartInfo.Arguments = " -i " + input+ (parametri != null? " "+parametri:"")+" "+output;
ffmpeg.StartInfo.FileName = "utils/ffmpeg.exe";
ffmpeg.StartInfo.UseShellExecute = false;
ffmpeg.StartInfo.RedirectStandardOutput = true;
ffmpeg.StartInfo.RedirectStandardError = true;
ffmpeg.StartInfo.CreateNoWindow = true;
ffmpeg.Start();
ffmpeg.WaitForExit();
ffmpeg.Close();
}
public void GetThumbnail(string video, string jpg, string velicina)
{
if (velicina == null) velicina = "640x480";
exec(video, jpg, "-s "+velicina);
}
}
Use like this:
FFMPEG f = new FFMPEG();
f.GetThumbnail("videos/myvid.wmv", "images/thumb.jpg", "1200x223");
For this to work, you must have ffmpeg.exe in folder /utils, or change the code to locate ffmpeg.exe.
There are other ways to use FFMPEG in .NET, like .NET wrappers, you could google for them. They basically do the same thing here, only better. So if FFMPEG gets your job done, I'd recomend to use .NET wrapper.
Try to make argument string format like:
ffmpeg.StartInfo.Arguments =" -i c:\MyPath\MyVideo -vframes 1 c:\MyOutputPath\MyImage%d.jpg"
Instead of
ffmpeg.StartInfo.Arguments = " -i " + input+ (parametri != null? " "+parametri:"")+" "+output;
in the answer code provided above.
I don't know what was the reason, but second mentioned argument line is not working on my machine whereas when I changed argument like the first command it works fine.
Probably the best tool for working with videos programatically is FFMpeg. It has support for many formats, even wmv. I suspect there's even a .net wrapper for it.

Categories