I want to create a huge dummy file say 1~2 GBs in matter of seconds.
here is what I've written in C#:
file.writeallbytes("filename",new byte[a huge number]);
and another way with indicating the status, was like following:
long FSS = din.TotalFreeSpace;
long segments = FSS / 10000;
long last_seg = FSS % 10000;
BinaryWriter br = new BinaryWriter(fs);
for (long i = 0; i < segments; i++)
{
br.Write(new byte[10000]);
this.label2.Text = "segments write :" + i.ToString() + "\r\n" + "segments remain :" + ((segments-i)+1).ToString();
Application.DoEvents();
}
br.Write(new byte[last_seg]);
this.label2.Text += "\r\nDone!";
br.Close();
where din is Disk Information object
well with these two approach it takes something like 2 or more minutes to write such a big but dummy file. Is there any other faster way for doing so?
Simply create the file, seek to a suitably large offset, and write a single byte:
FileStream fs = new FileStream(#"c:\tmp\huge_dummy_file", FileMode.CreateNew);
fs.Seek(2048L * 1024 * 1024, SeekOrigin.Begin);
fs.WriteByte(0);
fs.Close();
This will yield a 2GB file with basically unpredictable contents, which should be fine for your purposes.
If you don't care about the contents, then by far the fastest way I know of is this - it is practically instant:
private void CreateDummyFile(string fileName, long length)
{
using (var fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
fileStream.SetLength(length);
}
}
If you just need a FileStream, you could use FileStream.SetLength. That will get you a stream which is 2 GB long. Then you can write the final byte at an arbitrary position of your choice. But the contents will be undefined.
If you're trying to actually create a file on the disk, yes, you'll need to actually write its contents. And yes, hard disks are going to be slow; something like a 1 GB/min write speed isn't totally ridiculous. Sorry -- that's physics!
Why did you not use the BackgroundWorker class to achieve this, as you can pass anything into the method ReportProgress to indicate the status report. See the example below:
private BackgroundWorker bgWorker;
public Form1()
{
InitializeComponent();
bgWorker = new BackgroundWorker();
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged += new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.RunWorkerAsync();
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.label2.Text = "Done";
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
MyStatus myProgressStatus = (MyStatus)e.UserState;
this.label2.Text = string.Format("segments write : {0}" + Environment.Newline + "Segments Remain: {1}", myProgressStatus.iWritten, myProgressStatus.iRemaining);
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
long FSS = din.TotalFreeSpace;
long segments = FSS / 10000;
long last_seg = FSS % 10000;
BinaryWriter br = new BinaryWriter(fs);
for (long i = 0; i < segments; i++)
{
br.Write(new byte[10000]);
bgWorker.ReportProgress(i.ToString(), new MyStatus(i, ((segments-i) + 1)));
}
br.Write(new byte[last_seg]);
br.Close();
}
public class MyStatus{
public int iWritten;
public int iRemaining;
public MyStatus(int iWrit, int iRem){
this.iWritten = iWrit;
this.iRemaining = iRem;
}
}
}
This is a rough draft...
I could be wrong but you will probably find that it's impossible to create a file that large that quickly as there will be a bottleneck in the I/O writing process.
However in your code above the Applciation.DoEvents will be slowing things down. Also any repainting of the screenthis.label2.Text = will cause a slight slow down.
Related
So I'm trying to download two images and just for demonstration I've checked their sizes and summed it up to a variable called totalBytes.
I want to keep track of how much has been downloaded out of those total bytes so I can calculate the percentage by taking downloaded / totalBytes * 100
But I have no idea how to keep track of the mount of bytes that has been downloaded.
public static int totalBytes = 1378954;
static void Main(string[] args)
{
var images = new List<string>
{
"http://4.bp.blogspot.com/-HTvSYzA-pO4/UgQb4Zh_u0I/AAAAAAAAEuI/XwhtogT_1tA/s1600/3+cute2.jpg",
"http://getwallpapers.com/wallpaper/full/7/7/0/74728.jpg"
};
foreach (var image in images)
{
int i = 0;
using (var wc = new WebClient())
{
wc.DownloadProgressChanged += Wc_DownloadProgressChanged;
wc.DownloadFileAsync(new Uri(image), $"image{i}.png");
i++;
Console.ReadKey();
}
}
}
private static void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Console.WriteLine($"Downloaded: {e.BytesReceived} out of {totalBytes}");
}
Assuming that you do not intend to actually start downloading files in parallel (as you dispose the WebClient before starting another download), the problem can be solved with two field
One to store the current total
Another to store the previous ReceivedByte value
private static long _totalBytesReceivedAllFiles = 0;
private static long _previousBytesReceivedForCurrentFile = 0;
private static object _lock = new Object();
private static void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
// There are no guarantees in the documentation that such events are serialized and can't execute in parallel even for a single file,
// thus we will use lock to at least partially serialize it and ensure atomic field access.
// !!!It is not intended to handle actual parallel downloads!!!
lock (_lock)
{
_totalBytesReceivedAllFiles = _totalBytesReceivedAllFiles - _previousBytesReceivedForCurrentFile + e.BytesReceived;
_previousBytesReceivedForCurrentFile = e.BytesReceived;
Console.WriteLine($"Downloaded: {_totalBytesReceivedAllFiles} out of {totalBytes}");
if (e.ProgressPercentage == 100)
{
Console.WriteLine("Current file downloaded");
_previousBytesReceivedForCurrentFile = 0;
}
}
}
If on the other hand you want to start downloads truly in parallel, then you will have to
Not to dispose (in the loop!) the client, for obvious reasons.
Use a per-file Wc_DownloadProgressChanged with closure to have an actual per-file _previousBytesReceivedForCurrentFile
This question already has answers here:
How to use WinForms progress bar?
(4 answers)
Closed 4 years ago.
I'm writing a simple checksum generator app using C# for large files. It's working quite fine, but users would like to see some sort of progress bar since the app freezes for a a few dozen seconds.
Here is a a sample of code I use (BufferedStream increased a lot the app performance):
private static string GetSHA5(string file)
{
using (var stream = new BufferedStream(File.OpenRead(file), 1200000))
{
var sha5 = new SHA512Managed();
byte[] checksum_sha5 = sha5.ComputeHash(stream);
return BitConverter.ToString(checksum_sha5).Replace("-", String.Empty);
}
}
My question is, is it possible to get the buffer "progress" ? Because I guess internally it operates some sort of division and looping.
I tried implementing jdweng solution but I had trouble accessing threads to update my progress bar with the position variable. In the end I rewrote my code using background_worker and a custom buffer. Here is a sample of a it.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
dynamic data = e.Argument;
string fPath = data["file"];
byte[] buffer;
int bytesRead;
long size;
long totalBytesRead = 0;
using (Stream file = File.OpenRead(fPath))
{
size = file.Length;
progressBar1.Visible = true;
HashAlgorithm hasher = MD5.Create();
do
{
buffer = new byte[4096];
bytesRead = file.Read(buffer, 0, buffer.Length);
totalBytesRead += bytesRead;
hasher.TransformBlock(buffer, 0, bytesRead, null, 0);
backgroundWorker1.ReportProgress((int)((double)totalBytesRead / size * 100));
}
while ( bytesRead != 0) ;
hasher.TransformFinalBlock(buffer, 0, 0);
e.Result = MakeHashString(hasher.Hash);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void md5HashBtn_Click(object sender, EventArgs e)
{
if (MD5TextBox.Text.Length > 0)
{
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("algo", "MD5");
param.Add("file", MD5TextBox.Text);
backgroundWorker1.RunWorkerAsync(param);
}
}
I am writing a program which copies a file. I have the file copying correctly, the progress bar updates, but I get an error which states that the e.ProgressPercentage is at 101. The code for the bgWorker_ProgressChanged event handler is:
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// We will increase the progress bar when work progress is reported.
pbCopyProgress.Maximum = 100;
pbCopyProgress.Value = e.ProgressPercentage;
}
Here is the code for the bgWorker_DoWork event handler:
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Gets the size of the file in bytes.
Int64 iSize = strInputFile.Length;
// Keeps track of the total bytes downloaded so we can update the progress bar.
Int64 iRunningByteTotal = 0;
// Open the input file for reading.
using (FileStream InputFile = new FileStream(strInputFile, FileMode.Open, FileAccess.Read, FileShare.None))
{
// Using the FileStream object, we can write the output file.
using (FileStream OutputFile = new FileStream(strOutputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
// Loop the stream and get the file into the byte buffer.
int iByteSize = 0;
byte[] byteBuffer = new byte[iSize];
while ((iByteSize = InputFile.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
{
// Calculate the progress out of a base "100."
double dIndex;
double dTotal;
double dProgressPercentage;
// Write the bytes to the file system at the file path specified.
dIndex = (double)(iRunningByteTotal);
dTotal = (double)byteBuffer.Length;
dProgressPercentage = (dIndex / dTotal);
OutputFile.Write(byteBuffer, 0, iByteSize);
iRunningByteTotal += iByteSize;
intProgress = Convert.ToUInt16(dProgressPercentage);
// Update the progress bar.
bgWorker.ReportProgress(intProgress);
}
// Close the output file.
OutputFile.Close();
}
// Close the input file.
InputFile.Close();
}
}
As I said, the progress bar is updating, but I get an error because it seems to continue copying the file after it has reached 100 percent. If I put in a MessageBox.Show(Convert.ToString(intProgress)) immediately after the bgWorker.ReportProgress(intProgress) line, the dialog will pop up with 101 for the text. Any help will be greatly appreciated.
You're dividing your running total by the length of the block buffer, not the whole stream, which means the result is basically unbounded. You're failing to multiply by 100 too, but that problem is masked by the fact that the ratio is growing larger than one.
But you're making it all look very difficult - the code you want is simply:
bgWorker.ReportProgress((int)(100 * runningByteTotal / fileLength))
You should set up fileLength before the start of the loop (and it needs to be the length of the file, not the filename, as #azyberezovsky points out in his answer).
You can allow this calculation to happen with simple integer arithmetic rather than needing floating point types, as long as the multiply by 100 happens before the divide.
As a stylistic point, you don't need all the 'i's and 'd's in front of variable names - that's not considered to be good C# style. Nor are variables normally started with a capital letter - if nothing else, that confuses the SO code syntax highlighter...
That is not size of file - it is simply length of file name string:
Int64 iSize = strInputFile.Length;
And this is also not file size, this is a size of buffer you use to write data to output file:
dTotal = (double)byteBuffer.Length;
What you need is
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
using (FileStream inputFile = new FileStream(strInputFile, FileMode.Open, FileAccess.Read, FileShare.None))
using (FileStream outputFile = new FileStream(strOutputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
long totalBytesToWrite = inputFile.Length;
long totalBytesWritten = 0;
byte[] buffer = new byte[512]; // provide any buffer size here
int bytesToWrite;
ushort percentage;
while ((bytesToWrite = inputFile.Read(buffer, 0, buffer.Length)) > 0)
{
outputFile.Write(buffer, 0, bytesToWrite);
totalBytesWritten += bytesToWrite;
percentage = (ushort)((100 * totalBytesWritten)/totalBytesToWrite);
bgWorker.ReportProgress(percentage);
}
}
}
Keep in mind - you don't need to close stream manually if you are using using block - stream will be disposed (i.e. closed) at the end of this block.
Declare maximum value of:
pbCopyProgress.Maximum = InputFile.Read(byteBuffer, 0, byteBuffer.Length)
Remove line:
percentage = (ushort)((100 * totalBytesWritten)/totalBytesToWrite);
from bgWorker_DoWork, and declare counter before while cycle in bgWorker_DoWork. For example:
int counter = 1;
before the closing brace of while increment the counter (counter++);
in bgWorker_ProgressChanged update the percentage:
pbCopyProgress.Value = e.ProgressPercentage;
If you want to fill any label with text showing the percentage the following line calculates the percentage:
int percent = 100 / (byteBuffer.Length / e.ProgressPercentage);
Label1.Text = String.Format("Passed: {0} %, percent.ToString());
Those lines should be also in bgWorker_ProgressChanged.
I'm building an application which records audio from the microphone to a file (.mp3). I want my application to write data to the file only when a high enough amplitude is detected. I'm having trouble trying to save the selected data. All I get is a fast distortion, not even close from what was recorded.
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using NAudio.Wave;
namespace NAudio_Testing
{
public partial class Form1 : Form
{
int counter = 0;
int passed = 0;
private BufferedWaveProvider bufferedWaveProvider;
WaveIn waveIn;
WaveFileWriter writer;
string output = "C:/Users/klein/AppData/Roaming/Microsoft/Windows/gravacao.mp3";
public Form1()
{
InitializeComponent();
waveIn = new WaveIn();
writer = new WaveFileWriter(output, waveIn.WaveFormat);
waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(new_dataAvailable);
bufferedWaveProvider = new BufferedWaveProvider(waveIn.WaveFormat);
bufferedWaveProvider.DiscardOnBufferOverflow = true;
waveIn.StartRecording();
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 1000;
timer1.Start();
}
void new_dataAvailable(object sender, WaveInEventArgs e)
{
for (int i = 0; i < e.BytesRecorded; i+=2)
{
short sample = (short)((e.Buffer[i + 1] << 8) | e.Buffer[i + 0]);
float sample32 = sample / 32768f;
if (sample32 > 0.02) //0.02~0.03
{
writer.WriteSample(sample);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
waveIn.StopRecording();
waveIn.Dispose();
waveIn = null;
writer.Close();
writer = null;
}
private void timer1_Tick(object sender, EventArgs e)
{
textBox1.Text = counter++.ToString();
}
}
}
EDIT: I am able to record the audio successfully if I remove the selective if and just add the lines, the problem, of course is that then I'm unable to record or not according to the amplitude of the audio:
writer.WriteByte(e.Buffer[i + 0]);
writer.WriteByte(e.Buffer[i + 1]);
Like so:
void new_dataAvailable(object sender, WaveInEventArgs e)
{
for (int i = 0; i < e.BytesRecorded; i+=2)
{
short sample = (short)((e.Buffer[i + 1] << 8) | e.Buffer[i + 0]);
float sample32 = sample / 32768f;
writer.WriteByte(e.Buffer[i + 0]);
writer.WriteByte(e.Buffer[i + 1]);
}
}
Thank you.
Your approach is essentially throwing away all samples near a zero crossing, irrespective of whether the overall audio at that time is loud or not. It will distort the signal, and speed it up, since audio signals typically have lots of zero crossings every second.
Instead, you need to measure the maximum volume of all the samples in a short time period (how long is up to you, but you could do 1 second at a time). If during that time any sample is louder than the specified minimum value then you record the whole block.
The second problem with your code is that you are not actually creating an MP3 file. The WaveFileWriter makes a WAV file. Change your filename to end in WAV and listen to that to hear the results of your recording. Then you would need to convert to MP3 as a second step after recording. (I have an article on how to convert between formats here).
You should compare absolute value of sample, since samples are signed.
if (Abs(sample32) > 0.02) //0.02~0.03
{
writer.WriteSample(sample);
}
I want to calculate download speed as kbps (kb per second). There's a problem in the code, it doesn't show actual speed. And I'm really tired of this work. Also, when using (TotalDownloadSize / ElapsedTime) formula it shows more realistic results but you know it will get average value and it will be stupidity.
It usually gives 4000 and it's basicly because of chunk 4096 when I set it to 128 that time I get 50/100/125 values.
DateTime dlElapsed;
private delegate void UpdateProgessCallback(Int64 BytesRead, Int64 TotalBytes, Int32 CurrentBytes);
private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes, Int32 CurrentBytes)
{
DateTime Elapsed = DateTime.Now;
var progress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
var elaps = (Elapsed - dlElapsed).TotalSeconds;
long kbps;
if (elaps > 0)
{
kbps = Convert.ToInt64((CurrentBytes / elaps) / 1024);
updateLabelText(String.Format("Downloading ({0} kbps)...", kbps));
}
// Make progress on the progress bar
if (progress < progressBar1.Maximum)
{
progressBar1.Value = progress;
}
else
{
progressBar1.Value = progressBar1.Maximum;
}
dlElapsed = DateTime.Now;
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
// Some stuff here...
int byteSize = 0;
byte[] downBuffer = new byte[4096];
FileStream strLocal= new FileStream(path, FileMode.Create, FileAccess.Write);
dlElapsed = DateTime.Now;
while ((byteSize = stream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
strLocal.Write(downBuffer, 0, byteSize);
this.Invoke(new UpdateProgessCallback(this.UpdateProgress),
new object[] { strLocal.Length, totalbyte, byteSize});
}
updateLabelText("Download complete!");
strLocal.Close();
}
}
So where's the problem?
So where's the problem?
You're coarsely sampling something that varies a lot.
Consider buffering measurements and averaging the last 5 or so. Look for implementations of a "running average".
Well, my first comment would be that your code is not thread safe, so when you set dlElapsed = DateTime.Now;, it's not the same dlElapsed value that UpdateProgress is going to be checking.