C# : concatenate 2 MP3 files - c#

I tried to concatenate 2 MP3 files using the code below. I got a new file which I can play the first half of (complete first file), but the second half is silent. The length of the new file was correct. What do I do wrong?
List<Byte[]> files = new List<byte[]>();
var tempfile = File.ReadAllBytes(Path.Combine(path, "1.mp3"));
files.Add(tempfile);
tempfile = File.ReadAllBytes(Path.Combine(path, "2.mp3"));
files.Add(tempfile);
Byte[] a=new Byte[files[0].Length+files[1].Length];
Array.Copy(files[0], a, files[0].Length);
Array.Copy(files[1], a, files[1].Length);
File.WriteAllBytes(Path.Combine(path, "3.mp3") , a);

I am willing to bet you are only hearing the second song. (and that either both files are the same length or the first is shorter)
You are copying the second song data over the first. And MP3 data is streaming so you can just append the files to each other without worrying about bitrates (while they may glitch) the bitrate should automaticly adjust.
Detail on MP3 Frame headers
... try this...
Array.Copy(files[0], 0, a, 0, files[0].Length);
Array.Copy(files[1], 0, a, files[0].Length, files[1].Length);
... or better still...
using (var fs = File.OpenWrite(Path.Combine(path, "3.mp3")))
{
var buffer = File.ReadAllBytes(Path.Combine(path, "1.mp3"));
fs.Write(buffer, 0, buffer.Length);
buffer = File.ReadAllBytes(Path.Combine(path, "2.mp3"));
fs.Write(buffer, 0, buffer.Length);
fs.Flush();
}

Simple:
public static void Combine(string[] mp3Files, string mp3OuputFile)
{
using (var w = new BinaryWriter(File.Create(mp3OuputFile)))
{
new List<string>(mp3Files).ForEach(f => w.Write(File.ReadAllBytes(f)));
}
}

Here's how you can concatenate MP3 files using NAudio:
public static void Combine(string[] inputFiles, Stream output)
{
foreach (string file in inputFiles)
{
Mp3FileReader reader = new Mp3FileReader(file);
if ((output.Position == 0) && (reader.Id3v2Tag != null))
{
output.Write(reader.Id3v2Tag.RawData,
0,
reader.Id3v2Tag.RawData.Length);
}
Mp3Frame frame;
while ((frame = reader.ReadNextFrame()) != null)
{
output.Write(frame.RawData, 0, frame.RawData.Length);
}
}
}
See here for more information.

This question has been asked before here and here. Also see reading MP3 Headers in C#, but instead of reading the header you just want to strip it off, concatenate the rest, then generate a new header for the concatenated file.
Edit: After further reading apparently it doesn't make any difference if you just concatenate the files without stripping the ID3 tags. But it still seems like a good idea to strip them out first.

Each MP3 file has a header at the beginning of the file containing the metadata of the song. At the very least you will have to remove that on the second file.
Here is a previous Stack Overflow question, How do I merge/join MP3 files with C#?.
Here's another link to Read MP3 Tag Information (ID3v1 and ID3v2), it might help you remove it.

As all knows, the Mp3 files are just some frames and you can concatenate the streams together:
public static void Concatenate(params string[] mp3filenames)
{
Stream w = File.OpenWrite("D:\\out.mp3");
foreach (string filename in mp3filenames)
File.OpenRead(filename).CopyTo(w);
w.Flush();
w.Close();
}
and hears the usage:
Concatenate("D:\\1.mp3", "D:\\2.mp3");

Related

Why can't I append a new byte array to a file using FileStream? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I am trying to write a byte array to a file, multiple times. The FileMode is set to Append, to create the file if it doesn't exist, otherwise open the file and seek to the end of the file as described. The problem is that when writing to the existing file, it gets overwritten rather than have the new byte array appended to it. That's all there is to it.
void WriteToFile()
{
byte[] buffer = new byte[16 * 1024];
int num;
using (FileStream dest_stream = new FileStream(filename, FileMode.Append, FileAccess.Write))
{
while (num = ReadFromAnotherStream(my_other_stream, ref buffer) > 0)
dest_stream.Write(buffer, 0, num);
}
}
This function will be called occasionally. If the file already exists, seek to the end of the file and continue writing from there, otherwise create a new file and write data.
When it should append, it overwrites... It does not append.
It should append to the file instead of overwrite it.
There is no error thrown.
Using Seek for the FileStream does nothing.
When it overwrites, the data is correct, however, it needs to be appended at the end of the previous data, and not overwrite.
UPDATE: Well, I had no choice but to divide each call into multiple "temp" files then at the end merge them into the main file. Such worked flawlessly, no Seeking was required leading to non-corrupted files.
A downside would be extra processing for multiple temp files (especially large ones) being merged into one.
Pseudo-code:
string filename;
List<string> tmp_files = new List<string>();
int __i = 0;
do
{
filename = $"my_file.tmp{__i++}";
tmp_files.Add(filename);
}
while (File.Exists(filename));
// Every time WriteToFile() gets called... This should be on top.
// Instead of writing directly to the file, add more of them until the input stream has been read fully.
using (FileStream out_fs = new FileStream("my_file.bin", FileMode.Create))
{
foreach (string tmp_file in tmp_files)
{
using (FileStream in_fs = new FileStream(tmp_file, FileMode.Open))
{
in_fs.CopyTo(out_fs);
}
File.Delete(tmp_file);
}
}
First of all, thank you everyone who took part in this thread. Part of the code could not be shared and that is beyond me. I understand that there is no magic ball out there to read minds from pseudo-codes, but I was in desperate to solve this unethical mistake from the API, so I wanted to get as many possibilities.
I still don't know what the issue is with Append, but the input stream has absolutely nothing to do with it and that's out of the way for now.
We can't tell what is wrong with your code because you didn't include the code for ReadFromAnotherStream. But here is some code that does what you want, and it works:
/// <summary>
/// Appends the contents of the file at inputFilePath to the file at pathToAppendTo
/// </summary>
void Append(string inputFilePath,string pathToAppendTo)
{
var buffer = new byte[16];
using FileStream source = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read);
using FileStream destinationStream = new FileStream(pathToAppendTo, FileMode.Append, FileAccess.Write);
while (TryRead(source, buffer, out int bytes))
{
destinationStream.Write(buffer, 0, bytes);
}
}
private bool TryRead(FileStream source, byte[] buffer, out int bytesRead)
{
bytesRead = source.Read(buffer, 0, buffer.Length);
return bytesRead > 0;
}
Here is a unit test to verify that it works:
[TestMethod]
public void TestWriteFile()
{
var inputFileName = "C:/fileYouWantToCopy";
var outputFileName = "C:/fileYouWantToAppendTo";
var originalFileBytes = GetFileLength(outputFileName);
var additionalBytes = GetFileLength(inputFileName);
Append(inputFileName,outputFileName);
Assert.AreEqual(GetFileLength(outputFileName), originalFileBytes + additionalBytes);
}
private long GetFileLength(string path) => new FileInfo(path).Length;

How to implement a listener to check i have read a text file and ignore it

I have written a small program to monitor a folder. I have not used the folderWatcher in .NET because multiple files can be added to the folder simultaneously and folderWatcher can sometimes miss them. Basically, I am checking the folder for a file that starts and ends with certain characters. The timer is checking the folder every 10 seconds.
My issue: my program will read the same file multiple times. I need to set a condition or have a listener check if the file has already been read to ignore it.
The problem is I'm not sure how to go about this, can anyone shed some light on the best way to maybe implement a listener that will check if I have read this file and ignore it and move onto the next file, please?
I couldn't find another thread on here that helped me with the answer I am looking for.
Basicly, you sould keep somewhere (maybe in a text file) the last scan date and can check the creation time of files.
void ReadFiles()
{
try
{
DateTime lastScanDate = GetLastScanDate();
SetLastScanDate();
foreach (string filePath in System.IO.Directory.GetFiles(#"C:\Users\cozogul\Desktop\test"))
{
System.IO.FileInfo fi = new System.IO.FileInfo(filePath);
if (fi.CreationTime >= lastScanDate)
{
//read file
}
else
{
//Don't read file.
}
}
}
catch (Exception ex)
{
//Log your error
}
}
DateTime GetLastScanDate()
{
if (System.IO.File.Exists(#"C:\Users\cozogul\Desktop\test\datefile.txt"))
{
using (System.IO.FileStream fs = new System.IO.FileStream(#"C:\Users\cozogul\Desktop\test\datefile.txt", System.IO.FileMode.Open))
{
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
string dateString = System.Text.Encoding.UTF8.GetString(buffer);
DateTime date = Convert.ToDateTime(dateString);
return date;
}
}
//Will read all files.
else
return DateTime.MinValue;
}
public void SetLastScanDate()
{
using (System.IO.FileStream fs = new System.IO.FileStream(#"C:\Users\cozogul\Desktop\test\datefile.txt", System.IO.FileMode.Create))
{
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(DateTime.Now.ToString());
fs.Write(buffer, 0, buffer.Length);
}
}
Or you can keep filenames in a file or a database table to check a file is before read.
Thanks Coskun
That is a good option and it does work.
For simplicity i went a different route, I add the file names to a list then check the list pre-execution, if the list does not contain that file name it executes. I go on to count the file names in the list and output the total on each pass to a text field that tell me how many files i ignored.

Mix audio read from two source files

I want to mix audio read from two files (1 mp3 and 1 wav(recorded voice) file) into one file (mp3 or wav).
I have read many relevant answers but non helped me to get what I wanted.
Like this code below generate a steam as output. I do not know how to call this function properly and how to get the Output stream as an mp3/wav file at the end.
public static void Combine(string[] inputFiles, Stream output)
{
foreach (string file in inputFiles)
{
Mp3FileReader reader = new Mp3FileReader(file);
if ((output.Position == 0) && (reader.Id3v2Tag != null))
{
output.Write(reader.Id3v2Tag.RawData, 0, reader.Id3v2Tag.RawData.Length);
}
Mp3Frame frame;
while ((frame = reader.ReadNextFrame()) != null)
{
output.Write(frame.RawData, 0, frame.RawData.Length);
}
}
}
It looks like you took the code from http://mark-dot-net.blogspot.com/2010/11/merging-mp3-files-with-naudio-in-c-and.html. Check out http://markheath.net/post/mixing-and-looping-with-naudio as well. This talks about looping as well as mixing. Since you're not looping, you don't need that part.
Note that the example there sends the audio to the speaker. However, you can always replace that output with a (e.g.) WaveFileWriter.
Note also that the MixingWaveProvider32 requires that all inputs be in the same WaveFormat. If that's not the case, then you'll need to use a resampler. Luckily, there's http://mark-dot-net.blogspot.com/2014/05/how-to-resample-audio-with-naudio.html to help you out there as well.

How to find the no. of bytes of a text file without reading it?

I have c# code reading a text file and printing it out which looks like this:
StreamReader sr = new StreamReader(File.OpenRead(ofd.FileName));
byte[] buffer = new byte[100]; //is there a way to simply specify the length of this to be the number of bytes in the file?
sr.BaseStream.Read(buffer, 0, buffer.Length);
foreach (byte b in buffer)
{
label1.Text += b.ToString("x") + " ";
}
Is there anyway I can know how many bytes my file has?
I want to know the length of the byte[] buffer in advance so that in the Read function, I can simply pass in buffer.length as the third argument.
System.IO.FileInfo fi = new System.IO.FileInfo("myfile.exe");
long size = fi.Length;
In order to find the file size, the system has to read from the disk. So, the above example performs data read from disk but does not read file content.
It's not clear why you're using StreamReader at all if you're going to read binary data. Just use FileStream instead. You can use the Length property to find the length of the file.
Note, however, that that still doesn't mean you should just call Read and *assume` that a single call will read all the data. You should loop until you've read everything:
byte[] data;
using (var stream = File.OpenRead(...))
{
data = new byte[(int) stream.Length];
int offset = 0;
while (offset < data.Length)
{
int chunk = stream.Read(data, offset, data.Length - offset);
if (chunk == 0)
{
// Or handle this some other way
throw new IOException("File has shrunk while reading");
}
offset += chunk;
}
}
Note that this is assuming you do want to read the data. If you don't want to even open the stream, use FileInfo.Length as other answers have shown. Note that both FileStream.Length and FileInfo.Length have a type of long, whereas arrays are limited to 32-bit lengths. What do you want to happen with a file which is bigger than 2 gigs?
You can use the FileInfo.Length method.
Take a look at the example given in the link.
I would imagine something in here should help.
I doubt you can preemptively guess the size of a file without reading it...
How do I use File.ReadAllBytes In chunks
If it is a large file; then reading in chunks should might help

C#: Appending *contents* of one text file to another text file

There is probably no other way to do this, but is there a way to append the contents of one text file into another text file, while clearing the first after the move?
The only way I know is to just use a reader and writer, which seems inefficient for large files...
Thanks!
No, I don't think there's anything which does this.
If the two files use the same encoding and you don't need to verify that they're valid, you can treat them as binary files, e.g.
using (Stream input = File.OpenRead("file1.txt"))
using (Stream output = new FileStream("file2.txt", FileMode.Append,
FileAccess.Write, FileShare.None))
{
input.CopyTo(output); // Using .NET 4
}
File.Delete("file1.txt");
Note that if file1.txt contains a byte order mark, you should skip past this first to avoid having it in the middle of file2.txt.
If you're not using .NET 4 you can write your own equivalent of Stream.CopyTo... even with an extension method to make the hand-over seamless:
public static class StreamExtensions
{
public static void CopyTo(this Stream input, Stream output)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (output == null)
{
throw new ArgumentNullException("output");
}
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
}
}
}
Ignoring error handling, encodings, and efficiency for the moment, something like this would probably work (but I haven't tested it)
File.AppendAllText("path/to/destination/file", File.ReadAllText("path/to/source/file"));
Then you just have to delete or clear out the first file once this step is complete.
The cmd.exe version of this is
type fileone.txt >>filetwo.txt
del fileone.txt
You could create a system shell to do this. It should be pretty effecient.
I'm not sure what you mean by "inefficient". Jon's answer is probably enough for most cases.
However, if you are concerned about extremely large source files, Memory-Mapped Files could be your friend. See this link for more info.

Categories