Loading Multiple Buffers


One very common way for us to use the MiniBees is by triggering a randomly selected sample from a collection of related sounds. In order to quickly load a whole bunch of sounds into buffers that we can later access in functions and patterns, we wrote the BufArray class. It departs from an idea found in Eli Fieldsteel's excellent YouTube tutorial: read all the soundfiles of a folder into an array of Buffers. The BufArray class goes one step further, adding the possibility to recursively add all files in all the sub-folders into an array.

The class looks like this:

BufArray {

    var <>pathName;
    var <>buf;

    *new { arg pathName;
        ^super.newCopyArgs(pathName).init;
    }

    init {
        this.loadBufs(pathName, [".wav", ".aiff"]);
    }

    loadBufs { arg path, ext;
        var results = [];
        var extSet = ext !? { ext.collectAs(_.asSymbol, IdentitySet) };
        var search = { arg p;
            if (p.isFile.) {
                if (extSet.notNil) {
                    extSet.findMatch(("." ++ p.extension).asSymbol) !? {
                        results = results.add(p.fullPath);
                    };
                } { results = results.add(p.fullPath); };
            } {
                // continue in subfolders
                p.entries.do { arg e; search.(e) };
            };
        };
        search.(PathName(path));
        results.do{ arg file;
            file.postln;
            buf = buf.add(Buffer.readChannel(Server.default, file, channels: 0));
        }; 
    }

    bufAdd { arg path;
        buf = buf.add(Buffer.readChannel(Server.default, path.fullPath, channels: 0));
        // path.postln;
    }

    at { arg i;
        ^buf[i];
    }

    size {
        ^buf.size;
    }

    choose {
        ^buf.choose;
    }

    clump { arg i;
        ^buf.clump(i);
    }

    free {
        buf.do(_.free);
        buf = [];
    }
}

The heavy lifting is done by the loadBufs and bufAdd methods. Starting from the bufAdd, we add buffers to an array, in this case represented by the buf variable.

bufAdd { arg path;
    buf = buf.add(Buffer.readChannel(Server.default, path.fullPath, channels: 0));
    // path.postln;
}

The bufAdd method is called by the loadBufs method. This one is a bit more complicated and heavily indebted to Christof Ressi.

loadBufs { arg path, ext;
    var results = [];
    var extSet = ext !? { ext.collectAs(_.asSymbol, IdentitySet) };
    var search = { arg p;
        p.isFile.if {
            extSet.notNil.if {
                extSet.findMatch(("." ++ p.extension).asSymbol) !? {
                    results = results.add(p.fullPath);
                };
            } { results = results.add(p.fullPath); };
        } {
            // continue in subfolders
            p.entries.do { arg e; search.(e) };
        };
    };
    search.(PathName(path));
    results.do{ arg file;
        file.postln;
        buf = buf.add(Buffer.readChannel(Server.default, file, channels: 0));
    }; 
}

In Christof's own words and pseudo-code:

if (path is a file) then
   if (user provided extension list) then
       if (file extension matches extensions list) then
            add to results
       endif
   else
       add to results unconditionally
    endif
else
    recursively call search on each entry
endif

The at, size, choose and clump methods are mirroring the same methods from SuperCollider's native Array class. It would be very easy to add methods in this way: just create a method foo:

foo {| i |
    ^buf.foo[i]
}

Back in real life coding, the BufArray method is called like this:

~ba = BufArray("path/to/your/sound/files/folder");

If successful, you will see all your files listed in the post window. The BufArray can then be accessed just like you would access any other array.