loop optimization in c# - c#

I am working on a music player for pc using c# and all seems to go fine but, i have problem in loading all the music files from the music directory because it loads very slowly and this take time when opening the app, it could take 5 min depending on the amount of music files. I think this happens because i created a loop to loop through each music file and get the metadatas and also the picture to load on different picture boxes for each music file.
Please Help, i need it to be faster. Thank you.
the code is below...
public List<MusicDetails> Music_Library()
{
List<MusicDetails> files = new List<MusicDetails>();
string[] musicfolder = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic),"*mp3", SearchOption.AllDirectories);
for (int i = 0; i <musicfolder.Length; i++)
{
try {
files.Add(new MusicDetails
{
title = TagLib.File.Create(musicfolder[i]).Tag.Title,
genre = TagLib.File.Create(musicfolder[i]).Tag.FirstGenre,
artist = TagLib.File.Create(musicfolder[i]).Tag.FirstPerformer,
path = musicfolder[i],
CoverArt = OrganiseAlbums.SingleAlbumImage(musicfolder[i],true)
});
}catch(Exception)
{
// OMIT FILE
}
}
return files;
}

You could try replacing your loop with a parallel foreach loop using background threads - add each item to the UI as it is processed, and let .Net determine the most efficient way to process everything. If you do it right, your UI will remain responsive, and your users will start out with "enough to look at..." Here is a link to get you started:
https://msdn.microsoft.com/en-us/library/dd460720(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
If you have a ton of music files, though, you may not want to load them all into memory at once. I would look into some sort of a list control or layout control that allows virtualization so that you instantiate and display only the visible ones at any given time. Use a tutorial to see how to bind to your collection so that it is virtualized.
If you post a more specific question with examples of what you have tried, you will get more specific answers...

Related

c# embedded images or is there another way

I'm trying to find a good way to load say 100+ small images (98px png's each) into my .net form and additionally have them all interactive in a way (click, and a name of the image/file), but without loading the images from a local predefined folder. What do I mean by that? Let me explain and demonstrate an example. Please bear with me, I'm kinda confused on the topic of building the solution and compiling the executeable's folder structure.
Example:
Imagine when creating a profile on say, here for example. Usually, you set a name and add a profile picture. Sometimes the profile picture has to be uploaded, but in my case I want to have a preset of 100 available profile pictures that the user can choose from. It may sound like a lot, but considering the purpose of the application it really is nesseary.
The problem/question:
The easy and probably not so good way is simply adding pictureboxes and assigning a click to them onto a flowlayoutpanel. This is my current temporary solution, but the big flaw is that it uses a locally stored path. This app is supposed to installed on several machines and I need this to work on whichever computer it's installed on, no matter what drive letter that is used ect.
string[] files = Directory.GetFiles("C:\\path\\folder_a", "*", SearchOption.TopDirectoryOnly);
foreach (var filename in files)
{
Bitmap bmp = null;
try
{
bmp = new Bitmap(filename);
}catch{
}
var card = new PictureBox();
card.BackgroundImage = bmp;
card.Tag = (filename.Split('\\').Last());
card.Padding = new Padding(0);
card.BackgroundImageLayout = ImageLayout.Stretch;
card.Size = normal;
card.Click += delegate
{
//something
};
flowProfileIcons.Controls.Add(card);
}
These are the options to my knownledge, but I don't really know:
Embed (in 'build action') all the images directly from a folder within solution explorer and use AssemblyDirectory to find and use the images in a similar way as displayed above. However, will this allow the users to actually access and potentionally modify/delete the files because they are exposed in the installation folder? And is this even a good way to do this?
Put all images in resources.resx and somehow use the images from there, but as far as I've seen that isn't very easy to do. I will definitely look into it though if this is the prefered way. I did try some things out and this is my progress so far, though just testing - not working.
var images = Properties.resProfileIconsDark.ResourceManager
.GetResourceSet(CultureInfo.CurrentCulture, true, true)
.Cast<DictionaryEntry>()
.Where(x => x.Value.GetType() == typeof(Bitmap))
.Select(x => new { Name = x.Key.ToString(), Image = x.Value })
.ToList();
This is where I'm at. Hopefully someone can push me in the right direction.

Playlist Create and PLay in C#

So I am trying to add a music player to my web browser so users can play music in it from a specific folder in it's app data folder ("\AppData\Roaming\MantouWeb360\MWP360")
I was looking around a came accross a solution here
The user can select files (multiple ones too) which is then added to a playlist and then played.
However I am not so experienced at C# and VS doesn't recognise the wmp.playlistCollection.newPlaylist("myplaylist"); part of the code (and the following code using the wmp. part). This must mean there was code outside the example defining wmp. but I do not know what it is. Any way to get this code to work so that users can play music from their selected files on my web browser or is there possibly a better way which copies the music to the folder I said above and plays them in order (loop at the end of the last file) so that the user does not need to keep selecting files?
WMPLib.IWMPPlaylist playlist = wmp.playlistCollection.newPlaylist("myplaylist");
WMPLib.IWMPMedia media;
if (ofdSong.ShowDialog() == DialogResult.OK)
{
foreach (string file in ofdSong.FileNames)
{
media = wmp.newMedia(file);
playlist.appendItem(media);
}
}
wmp.currentPlaylist = playlist;
wmp.Ctlcontrols.play();

How to add mp3 files to a MediaPlayer.Queue, or otherwise play a list of mp3 files sequentially on Windows Phone?

Background
I have a folder containing .mp3 files on my device, stored in /Resources/raw. I need to play some of these files sequentially under certain circumstances.
I'm trying to do the following:
foreach (var track in _tracks)
{
var name = track.Item1;
var uri = new Uri(string.Format("{0}/{1}", "/Resources/raw", name), UriKind.Relative);
var song = Song.FromUri(name, uri);
FrameworkDispatcher.Update();
// only the last track plays because the foreach loop is too fast
MediaPlayer.Play(song);
}
Unfortunately, this isn't working because MediaPlayer.Play() plays the song in a separate thread and the foreach loop just fires each one as fast as my processor can let it. As such, it only ever plays the last track.
I've tried adding a delay, e.g. Thread.Sleep(1000); before each call to Play() but that has had no effect.
I can see that MediaPlayer has a Queue property and MoveNext() method, which plays the next song in the queue. However, I can't figure out how to add the list of files to the Queue.
Question
How can I get songs into that Queue so that I can play them sequentially? Is this the best way to achieve the result I need? If not, what should I do to play the mp3 files sequentially?
Unfortunately you can't.
For some unknown reason Microsoft does not allow the developper to create a custom playlist on Windows phone.
Implementing playlists with songs from phone storage [windows phone]
What eventually worked (though not ideally) was to increase the Thread.Sleep() time to something longer than the track. I initially wasn't using a long enough value:
foreach (var track in _tracks)
{
var name = track.Item1;
var uri = new Uri(string.Format("{0}/{1}", "/Resources/raw", name), UriKind.Relative);
var song = Song.FromUri(name, uri);
Thread.Sleep(2000); // this value has to be longer than the track
FrameworkDispatcher.Update();
MediaPlayer.Play(song);
}

How to Get a Specific Song From the Phone's MediaLibrary? And optimising the code

In my application I have the following code, which get's the phone's MediaLibrary and filters out the correct chosen Song from the MediaLibrary.Songs and plays it:
using (MediaLibrary library = new MediaLibrary())
{
foreach (var item in library.Songs)
{
if (item.Name == songName)
{
FrameworkDispatcher.Update();
MediaPlayer.Play(item);
}
}
library.Dispose();
}
However this takes quite a while and results in a pause. Is there a faster/more-efficient way to access a specific Song from the phone's MediaLibrary.Songs?
Thanks for your help.
Maybe you have to store the values in a XML document after you read the directory, this can save you a lot of time if the library doesn't get changed (Check the size of the whole library). If it does then you can check the library again and update your XML.
Well this is not an efficient way at all.
To find any song in MediaLibrary use LINQ.
Example
MediaPlayer.Play(library.Songs.First(x=>x.Name == songName));

Parallel.ForEach and ListView Control

I am having an issue getting the Parallel.ForEach to work properly. I have a listview control that loads a files contents or mutliple files contents (each file represents a log file) into a richtextbox. Had it all working with a Foreach loop but decided that the parallel operation would cut down on the loadtime when loading 100+ files.
Here is the code block I was using for the foreach loop. How do I get a Parallel.Foreach working?
//Set cursor to WaitCursor Style
this.Cursor = Cursors.WaitCursor;
//Clear Messages RichTexBox
this.rtbMessages.Clear();
//Loop through each selected file
foreach (ListViewItem Item in lvMessageFiles.Items)
{
//Check if item is selected in the listview
if (Item.Selected && rtbMessages.TextLength < rtbMessages.MaxLength)
{
//Get Path to message file
filename = String.Format("{0}\\Data\\Log\\{1}.log", Global.AppPath, Item.SubItems[0].Text);
//Set Timeline Events calendar to selected items created date
cvTimeline.ShowDate(Convert.ToDateTime(lvMessageFiles.SelectedItems[0].SubItems[2].Text));
//Check if file exists
if (File.Exists(filename))
{
//streamreader to read the file
reader = new StreamReader(filename);
//to copy the read content in to richtextbox
string MessageContents = String.Format("{0}\n{1}\n", ("file:///" + filename.Replace(" ", "%20").Replace("\\", "/")), reader.ReadToEnd());
rtbMessages.Text += MessageContents;
// closing streamreader
reader.Close();
}
}
}
//Set cursor to WaitCursor Style
this.Cursor = Cursors.Default;
2 problems with what you are doing.
1) with this specific code, you are trying to modify UI from a background thread which is not allowed
2) this scenario isn't a good candidate for parallelization anyhow because you're performace is "I/O bound" to a single hard drive. if you want to speed up load time, split the files onto multiple hard drives and then parallelization may be worthwhile
see Is Parallel File.Read Faster than Sequential Read?
You're not allowed to update UI from a non-UI thread, which threads in a Parallel.ForEach obviously will be. Instead use the Invoke method to set the Text of rtbMessages and call cvTimeline.ShowDate
I am experiencing dejavu with this question... Anyway, take a look at this blog article. It's very simple to my taste but does exactly what you are trying to achieve - update UI while working with concurrent collections using new .NET 4 parallel mechanisms. Though not ForEach but Task.
http://blogs.msdn.com/b/csharpfaq/archive/2010/06/18/parallel-programming-task-schedulers-and-synchronization-context.aspx

Categories