Recently I got a new MP3 Player, a Sony Walkman NWZ-385. Most people have effectively replaced the MP3 Player with a Smartphone, since smartphones also have MP3 playback capabilities. I don’t have a smartphone (perhaps I will get one in the future. I was replacing my older NWZ-S616F MP3 Player, which has started to have problems, for example the volume rocker switch broke off, the volume up microswitch within snapped off (meaning I have to manually short the connection to raise the volume), the entire case has split and now has a piece of adhesive tape holding the front to the back, etc. The device works pretty much fine, though it does have problems where it actually crashes which requires a hard reset and is not exactly convenient when using the Player. At any rate, I replaced it, and while I was at it I got one four times the size, as well.
As my old one had given me trouble before, I am leery of adding or removing music from it. So it had been a while since I had transferred music; additionally, I had a much larger music library and it would be interesting that I would have a much larger device and thus less decisions to be made.
Unfortunately I found that the device does not support the .flac file format; and almost all of my music is in the .flac format. I could use Audacity or other audio tools to convert the format, but I wanted to convert a huge number of files- In the interest of disclosure I had all of Weird Al’s Albums in .flac format- quite a number of files. And I also had a few other artists with a large number of files that I converted to .flac format (or, rather, probably downloaded in .flac format originally). For some of them I could easily just re-download the file in the appropriate format. For example, I simply re-downloaded the recently released History Repeating: Red Album by The Megas in order to get the .MP3 Files to transfer to my MP3 Player. But the rest of them? Even if I could re-download them, that is a huge amount of data. Additionally, the .flac files are lossless, so I could re-encode to MP3 without many problems.
What better way to solve this problem than with a Program? I cannot think of any! And it makes an excellent code sample to discuss here on my Blog- the best of both worlds!
Audio and .NET
Audio and Audio Playback is somewhat lacking from .NET. There is some basic support for .WAV files, but there is no rich support for playback of compressed formats such as .ogg or .mp3. This is not really an oversight but rather one of those things you simply need an add-on library for. The question becomes- which Add-on library.
For me, there is no question about which Library I should use for this purpose- the only one I have any experience with and a good idea that this is possible. BASS.NET. Could I use it to convert FLAC to MP3? I believe so. I’d only used it for .ogg files so far but it is a versatile library.
What is BASS.NET?
BASS.NET is a .NET Library that wraps BASS itself, which is a cross-platform, multi-language Audio Library. It is not freeware, but rather a sort of ‘shareware’; it is free for personal use and for free projects, but requires licensing for commercial or corporate implementations.
Setting it up is fairly easy, though it requires a few things. First, we need of course BASS.NET itself. BASS.NET requires BASS itself in order to run. Since we will be encoding, we’ll need bassenc.dll, which is available on the un4seen page. Since we want to decode FLAC files (in this instance) we need to bassflac.dll file from there as well. And since we want to encode to MP3 files, we’ll need to find and download an applicable LAME binary (or, of course, build it ourselves). I downloaded a LAME Binary from here myself.
Setting up the project was a case of adding these libraries to the project and setting them to Copy to the output folder. I also changed the build configuration of the project to build an x86 program, rather than Any CPU. On my x64 CPU this would result in an x64 binary which will cause problems when it tries to load the x86 binaries. BASeBlock- my previous game which used BASS.NET for sound- implemented a method that allowed it to load the proper version of BASS and dependent libraries based on what it was running on, but that is not entirely trivial and is out of the scope of what I wanted to do here anyway. Also, I think I might have already written about it, though I’m not sure. (I’ll have to check that, if not, I will definitely make a post on it). With the project setup, And the References added, I could finally get to work writing the program. Thankfully, BASS and the encoding Libraries and in particular BASS.NET make this almost trivial. I thought it was going to be far more difficult than it ended up being.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
using System; using System.IO; using System.Linq; using System.Reflection; using Un4seen.Bass; using Un4seen.Bass.Misc; namespace Recoder { internal class Program { private static readonly String[] DefaultExtensions = {".flac"}; private static void Main(string[] args) { if (args.Length != 2) { Console.WriteLine("Unrecognized Arguments. Syntax:"); Console.WriteLine("Recoder <InputFolder> <OutputFolder>"); return; } bool BassResult = Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero); String CurrentFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); Bass.BASS_PluginLoadDirectory(CurrentFolder); String TestSource = args[0]; String TestTarget = args[1]; if (!Directory.Exists(TestSource)) { Console.WriteLine("Source Folder not Found."); return; } ReencodeResults ConvertResults = EncodeFolder(TestSource, TestTarget, DefaultExtensions); Console.WriteLine("Completed"); Console.WriteLine ("Re-Encoded " + ConvertResults.FileCount + " Files. " + ByteSizeFormatter.FormatSize(ConvertResults.OriginalBytes) + " => " + ByteSizeFormatter.FormatSize(ConvertResults.TargetBytes)); Console.ReadKey(); } private static ReencodeResults EncodeFolder(String SourceFolder, String TargetFolder, String[] ValidExtensions = null) { int Count = 0; long SourceBytes = 0; long TargetBytes = 0; if (ValidExtensions == null) ValidExtensions = DefaultExtensions; Console.WriteLine("Re-encoding Files from " + SourceFolder + ", output going to " + TargetFolder); //loop through each file, and convert each one. if (!Directory.Exists(TargetFolder)) { Directory.CreateDirectory(TargetFolder); } var di = new DirectoryInfo(SourceFolder); foreach (FileInfo fi in di.GetFiles()) { if (ValidExtensions.Any(a => a.Equals(fi.Extension, StringComparison.OrdinalIgnoreCase))) { String sFilePart = Path.GetFileNameWithoutExtension(fi.FullName); String TargetFile = Path.Combine(TargetFolder, sFilePart + ".mp3"); Console.WriteLine("Re-Encoding " + Path.GetFileNameWithoutExtension(fi.FullName)); Console.WriteLine("Target:" + Path.GetFileNameWithoutExtension(TargetFile)); ConvertFile(fi.FullName, TargetFile); Count++; SourceBytes += fi.Length; TargetBytes += new FileInfo(TargetFile).Length; } } //now subfolders. foreach (DirectoryInfo subdir in di.GetDirectories()) { String TargetName = Path.Combine(TargetFolder, subdir.Name); ReencodeResults EncodeResults = EncodeFolder(subdir.FullName, TargetName, ValidExtensions); Count += EncodeResults.FileCount; SourceBytes += EncodeResults.OriginalBytes; TargetBytes += EncodeResults.TargetBytes; } return new ReencodeResults(Count, SourceBytes, TargetBytes); } private static bool ConvertFile(String pSource, String pTarget) { int hStream = Bass.BASS_StreamCreateFile(pSource, 0, 0, BASSFlag.BASS_DEFAULT); //Bass.BASS_ChannelPlay(hStream, false); var l = new EncoderLAME(hStream); l.InputFile = null; //STDIN (recording) l.OutputFile = pTarget; l.LAME_Bitrate = (int) BaseEncoder.BITRATE.kbps_320; l.LAME_Mode = EncoderLAME.LAMEMode.Mono; l.LAME_TargetSampleRate = (int) BaseEncoder.SAMPLERATE.Hz_44100; l.LAME_Quality = EncoderLAME.LAMEQuality.Quality; BaseEncoder.EncodeFile(pSource, pTarget, l, null, true, false); return true; } } public class ReencodeResults { public ReencodeResults(int pFileCount, long pOriginal, long pTarget) { FileCount = pFileCount; OriginalBytes = pOriginal; TargetBytes = pTarget; } public int FileCount { get; private set; } public long OriginalBytes { get; private set; } public long TargetBytes { get; private set; } } } |
You can find the full project here. Basically it accepts two folders on the command line. It will convert all .FLAC files in the Source Folder to .MP3 Files in the target folder, than it will recursively call itself on all subfolders that it finds. It then returns a ReencodeResults class instance holding information that is used for the display of information when the program completes. A lot of the code here could be customized and it could certainly be moulded into a Windows Forms Application; however as it is it serves the purpose I need it for. I haven’t made a new program available in some time though so this could be a reasonably simple program to fill that need, while also being quite useful.
Have something to say about this post? Comment!
2 thoughts on “Bulk Music Re-encoding, FLAC to MP3”
Terrible article – no explanation of what is happening. No explanation of the nuget commands or setup of VS to use the information. This should be an example of how NOT to publish a project.
Thanks, This isn’t really a “published project”; it is a project I wrote for myself that I decided to share nearly two years ago, but was not deserving of it’s own repository or the time I would need to spend setting up requisites or build steps. A link is provided for the BASS.NET Wrapper as well as the underlying BASS libraries. I’d argue that the capability to install software, copy files, unzip a file and use the VS references dialog is not a task requiring any certifications.
the BASS-related NuGet packages appear to have only been published to NuGet a week or so prior to the publication of this post. The one used specifically as part of this project is not made available via NuGet at all.
A newer variant- which I alluded to being possible in the last paragraph, was written about more recently here. Being more of a ‘real’ project as opposed to a one-off program, it also got a github repository. The appropriate logic was ported to use the available BASS.NET.Objects NuGet package on NuGet at the time.