loading...

Shuffle Fail: Fixing my car stereo with code!

turnerj profile image James Turner Updated on ・4 min read

Background

I have a 40-ish minute commute to work and love blasting music in my car (sorry if you were in the car next to me). I bought my car used 6 years ago and it still has the same stereo since I bought it.

Alpine CDA-9886
Alpine CDA-9886 - the stereo currently in my car

I have my music on a USB that I plug into my car and I like listening to the music on shuffle (I don't want to anticipate the next song). I had suspicions for a while that my car wasn't actually shuffling the music very well.

Some songs I have heard quite a lot (even back-to-back) and others I have never heard play. Turns out my stereo has an interesting quirk of only allowing the first 100 songs in a folder to play. This is definitely a problem but wasn't my only problem as I did have some of my music spread through dozens of folders which did occasionally play.

If I were to guess, from the few hundred songs I had loaded on my USB, I'd say there was probably 60 songs that I actually heard with shuffle enabled.

I'm a programmer dammit, we bend computers to our will! I'm sure there is something I can do...

!Random

xkcd: Random Number
xkcd #221

You may or may not know this but many random number functions in our code are not as random as you might expect. These are pseudorandom number generators that are actually determined by an initial value, a seed.

The seed for a pseudorandom number generator is often based on the current time which can lead to an interesting problem if you're not careful. If you want a lot of random numbers in a short period of time, you want to use the same instance of the generator otherwise you might end up with the exact same number as it may have the same seed.

Pseudorandom numbers are useful for a lot of things but definitely shouldn't be used for cryptographic purposes which you will normally find noted in documentation for these functions.

With my car stereo, I don't know how the seed is generated or how it comes up with the random number. While it would be a fun job trying to reverse engineer the stereo to control an aspect like that, I was hoping for a simpler solution.

Debugging

Through some more repetitive trips to work and a bit more debugging, I discovered something interesting about playing without shuffle. Without shuffle, I thought the music would play in alphabetical order or maybe by date modified but it is far simpler than that - it uses the order that it was written to the file system.

This might seem like an obvious thing to others but was a bit of a surprise for me and gave me a way to potentially work around my randomness problem. If the songs are loaded up in a different order, I'd get to listen to my music in a different order.

Solution

The solution is straight forward - remove all music from the USB then re-add music to the USB in both a shuffled (to achieve my randomness goal) and "chunked" (to avoid the 100 files in a folder problem) fashion.

Here is what I cobbled together:

class Program
{
    static Random rand = new Random();

    static void Main(string[] args)
    {
        var source = @"C:\Path\To\My\Music\";
        var destination = @"E:\";

        Console.WriteLine($"Source: {source}");
        Console.WriteLine($"Destination: {destination}");

        Console.WriteLine("Press Enter to continue...");
        Console.ReadLine();

        RemoveAllFiles(destination);
        CopyFiles(source, destination);

        Console.WriteLine("Complete! Press Enter to close");
        Console.ReadLine();
    }

    static void RemoveAllFiles(string location)
    {
        foreach (var file in Directory.EnumerateFiles(location))
        {
            Console.WriteLine($"Deleteing {file}...");
            File.Delete(file);
        }
    }

    static void CopyFiles(string source, string destination)
    {
        var sourceFiles = Directory.EnumerateFiles(source).ToArray();

        Shuffle(rand, sourceFiles);

        var foldersOfFiles = ChunkBy(sourceFiles.ToList(), 25);

        for (int i = 0, l = foldersOfFiles.Count; i < l; i++)
        {
            var folderName = $"{i + 1}";
            var folderPath = Path.Combine(destination, folderName);

            Directory.CreateDirectory(folderPath);

            var files = foldersOfFiles[i];

            for (int i2 = 0, l2 = files.Count; i2 < l2; i2++)
            {
                var sourceFile = files[i2];
                var destFile = Path.Combine(folderPath, Path.GetFileName(files[i2]));

                Console.WriteLine($"Copying {sourceFile} to {destFile}...");
                File.Copy(sourceFile, destFile);
            }
        }
    }

    static void Shuffle<T>(Random rng, T[] array)
    {
        int n = array.Length;
        while (n > 1)
        {
            int k = rng.Next(n--);
            T temp = array[n];
            array[n] = array[k];
            array[k] = temp;
        }
    }

    static List<List<T>> ChunkBy<T>(List<T> source, int chunkSize)
    {
        return source
            .Select((x, i) => new { Index = i, Value = x })
            .GroupBy(x => x.Index / chunkSize)
            .Select(x => x.Select(v => v.Value).ToList())
            .ToList();
    }

Credit where credit is due, Stack Overflow was the source for both that shuffle and chunking implementation.

You might notice I'm also using the Random class which is a pseudorandom number generator but that is actually fine in this case. My problem stemmed from the car stereo having a bad pseudorandom generator (or seed) so with my computer randomising the music on upload to the USB, I have worked around that problem.

This doesn't eliminate that shuffle play on my car stereo still only plays about 60 songs but it does mean that I now don't need to have shuffle enabled as my music already is shuffled.

Posted on by:

turnerj profile

James Turner

@turnerj

Director - Innovator of Things - Co-Author of Your First Year In Code

Discussion

pic
Editor guide
 

Nice workaround :)

Did you consider re-ordering the directory entries, rather than bulk copying many GB to the USB? Assuming it's FAT, then it's well documented.. should still be easier than reversing the car radio!

 

Nope, I didn't consider doing that though it would be a lot more efficient! My method does take a several minutes to run currently where really I am only wanting to shuffle my music and sync any new music to it.

I haven't tried to play with a file system at a lower level like that before though now that you've brought it up, the thought does intrigue me and maybe I'll do a followup post if I do it. :)

 

Actually it occurs to me that plain old renaming should do the trick - move (aka rename) to a temporary folder, then move back in desired order - this re-writes the directory, no special knowledge required :)

 

Yeah, the title is a little disingenuous. While it would totally be interesting to work out how to modify the embedded code (and maybe I will at some point just for fun), what I came up with got me close enough to my goal that I didn't try to pursue other options.

Hopefully it was a somewhat interesting article nonetheless 🙂