How to Code a Sampler in VB.net


Writing high-level code for working with audio is kind of a black art. You'd think that it would be covered by some of the million pages on the internet devoted to multimedia programming, but most of those pages are really just game programming guides in disguise. The truth is, in relation to graphics programming, sound programming is undersupported by platform developers and ignored by mainstream programmers.

I plan to address that issue with a series of 'How To' articles that I am writing for this site. This is the first, and it will walk you through the basics of sound programming in a windows environment by helping you build a basic sampler/sequencer.

The Plan

So here's what we will need:
1. A custom control for setting sample triggers
2. A class for playing back sounds
3. A basic knowledge of threading

The Custom Control

We need a control that will allow us to toggle an arbitrary number of triggers to one of two states. Since none of the built-in controls are really suited for this, we will have to create our own.

For this project I created a simple class called NoteRow that stores an array of booleans and displays the status of each boolean in a horizontal row of rounded rectangles. There is some relatively advanced GUI drawing code in this class, but just focus on the public properties: NumTriggers and Triggers().

Simple Sequencer GUI Class

Here's the code for the custom control

Playing Samples

To play samples back, we will use DirectX, so before you get started, make sure that you have the DirectX SDK.

For years, DirectX has barely supported audio. That has all changed with managed DirectX and although it may at first look intimidating, DirectX actually makes playing audio very easy.

In this project, the sound playback class is called soundBank. It is basically just an array of wav files contained in instances of a subclass called sound.

The sound class relies on two main directx classes, SecondaryBuffer and BufferDescription. An instance of BufferDescription holds information about the sound, and an instance of SecondaryBuffer holds the actual sound data. Luckily, DirectX takes care of all of the messy file parsing for us, so all we have to worry about is starting and stopping the sounds at the right time.

Here's the code for the soundBank class

Threading

For this project, we don't need the kind of robust timing that a professional sequencer needs, so I implemented a very basic thread for playback. This thread consists of a single subroutine that checks to see if any of the sounds should be triggered, then just waits until the next tick.

The routine that controls playback

Putting it All Together

Putting everything together is relatively easy. I added a few buttons for loading samples, a start/stop button and a control to change the tempo. That's pretty much all you need.

The final GUI

Get the full source code in a zip file here

Improvements

This code is very basic, and there are a million improvements you could make, if you want to. You could add code to render the output to a wav file, allow users to change the number of ticks in a loop, allow an infinite number of channels or add controls for changing the volume/pan/pitch. If you tackle any of these upgrades, send your results to me at evan at thisisnotalabel dot com.

I will address some of these more advanced changes in a new article in a few weeks.


2007-06-07 20:53:20
nulldude at 2007-11-07 20:01:17
great stuff! i actually used this project as a foundation for an interface experiment for a vb class i was taking. full props given. a video demo is here http://uk.youtube.com/watch?v=K6Fn8sLPbeE i added some interface elements and mapped the circle of fifths to chord playing buttons. other enhancements underway so you can't hit a bum note in the lead or bass if the note exists in the chord space. cheers!
asdf at 2008-04-19 08:08:54vb.net
fsadfsadfasdfsadfasasdf
InsaneGenius at 2008-05-29 17:23:14Exellent!
Great job! This site has just become my favorite!
Speak
Handle:
Title:
Comment:
Enter this text:
username: password: