karplus4arduino

2011 October 8

Making WAV files from C programs

Filed under: Digital music — gasstationwithoutpumps @ 21:03
Tags: ,

I’m going to try embedding some short pieces of code in this post, and later work out a better way of distributing larger blocks of code.

The code I want to share today consists of a small C file and the associated header for creating WAV files for producing sounds on a laptop or desktop computer. This is not a full-featured WAV file module: the format has a lot of options and I didn’t want the complexity of dealing with all of them. I just wanted to output monophonic sound with a reasonable sampling rate.

/* make_wav.h
 * Fri Jun 18 17:06:02 PDT 2010 Kevin Karplus
 */

#ifndef MAKE_WAV_H
#define MAKE_WAV_H

void write_wav(char * filename, unsigned long num_samples, short int * data, int s_rate);
	/* open a file named filename, write signed 16-bit values as a
		monoaural WAV file at the specified sampling rate
		and close the file
	*/

#endif
/* make_wav.c
 * Creates a WAV file from an array of ints.
 * Output is monophonic, signed 16-bit samples
 * copyright
 * Fri Jun 18 16:36:23 PDT 2010 Kevin Karplus
 * Creative Commons license Attribution-NonCommercial
 *	http://creativecommons.org/licenses/by-nc/3.0/
 */

#include <stdio.h>
#include <assert.h>

#include "make_wav.h"

void write_little_endian(unsigned int word, int num_bytes, FILE *wav_file)
{
    unsigned buf;
    while(num_bytes>0)
    {   buf = word & 0xff;
    	fwrite(&buf, 1,1, wav_file);
        num_bytes--;
	word >>= 8;
    }
}

/* information about the WAV file format from
	http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
 */

void write_wav(char * filename, unsigned long num_samples, short int * data, int s_rate)
{
    FILE* wav_file;
    unsigned int sample_rate;
    unsigned int num_channels;
    unsigned int bytes_per_sample;
    unsigned int byte_rate;
    unsigned long i;	/* counter for samples */

    num_channels = 1;	/* monoaural */
    bytes_per_sample = 2;

    if (s_rate<=0) sample_rate = 44100;
    else sample_rate = (unsigned int) s_rate;

    byte_rate = sample_rate*num_channels*bytes_per_sample;

    wav_file = fopen(filename, "w");
    assert(wav_file);	/* make sure it opened */

    /* write RIFF header */
    fwrite("RIFF", 1, 4, wav_file);
    write_little_endian(36 + bytes_per_sample* num_samples*num_channels, 4, wav_file);
    fwrite("WAVE", 1, 4, wav_file);

    /* write fmt  subchunk */
    fwrite("fmt ", 1, 4, wav_file);
    write_little_endian(16, 4, wav_file);	/* SubChunk1Size is 16 */
    write_little_endian(1, 2, wav_file);	/* PCM is format 1 */
    write_little_endian(num_channels, 2, wav_file);
    write_little_endian(sample_rate, 4, wav_file);
    write_little_endian(byte_rate, 4, wav_file);
    write_little_endian(num_channels*bytes_per_sample, 2, wav_file);  /* block align */
    write_little_endian(8*bytes_per_sample, 2, wav_file);  /* bits/sample */

    /* write data subchunk */
    fwrite("data", 1, 4, wav_file);
    write_little_endian(bytes_per_sample* num_samples*num_channels, 4, wav_file);
    for (i=0; i< num_samples; i++)
    { 	write_little_endian((unsigned int)(data[i]),bytes_per_sample, wav_file);
    }

    fclose(wav_file);
}

Note that the above code is not a full program, just a tiny library routine that can be used to generate WAV files. Here is a test program to generate sine waves (inefficiently) to see if the program works. Once you have the basic idea, you can write arbitrary programs to generate WAV files and play them back with QuickTime Player, RealPlayer, Audacity, or any of several other sound-playing programs.  This is really old-school computer music, from the days when computers were so slow that quality sounds could not be generated in real time.  It provides a good way to get started in learning the algorithms, though, as you don’t need to worry about efficiency or all the details needed to compute samples and provide them to a DAC at exactly the right rate.

/* test_make_wav.c
 * Fri Jun 18 17:13:19 PDT 2010 Kevin Karplus
 * Test program for the write_wav function in make_wav.c
 */

#include <math.h>
#include "make_wav.h"

#define S_RATE	(44100)
#define BUF_SIZE (S_RATE*2)	/* 2 second buffer */

int buffer[BUF_SIZE];

int main(int argc, char * argv)
{
    int i;
    float t;
    float amplitude = 32000;
    float freq_Hz = 440;
    float phase=0;

    float freq_radians_per_sample = freq_Hz*2*M_PI/S_RATE;

    /* fill buffer with a sine wave */
    for (i=0; i<BUF_SIZE; i++)
    {
        phase += freq_radians_per_sample;
	buffer[i] = (int)(amplitude * sin(phase));
    }

    write_wav("test.wav", BUF_SIZE, buffer, S_RATE);

    return 0;
}
Advertisements

2011 June 15

Hold the presses! Can’t distribute software!

Filed under: Digital music,Software — gasstationwithoutpumps @ 03:15
Tags:

This post was going to be a release of a .c file and a .h file for making WAV files from C programs. Unfortunately, the WordPress.com blog site provides no convenient way for distributing software files. The file extensions they allow are limited to jpg, jpeg, png, gif, pdf, doc, ppt, odt, pptx, docx, pps, ppsx, xls, xlsx for “security reasons”. Allowing docx and prohibiting plain text files clear has nothing to do with security, but trying to support the Microsoft monopoly. I may need to seriously rethink whether I want to run this blog on WordPress.com, as I’ll be wanting to include file types that they do not support (program source code, perhaps schematics or PC board layouts).

Maybe it is time to abandon wordpress.com and find a friendlier blogging site that allows reasonable other file formats.

UPDATE: 8 October 2011

WordPress.com does have a perfectly fine way to show code snippets, explained on Posting Source Code.  I don’t think that documentation existed when I first posted this message—at least I couldn’t find it then.  The method is actually fairly simple: just put [sourcecode language=”python”] and [/sourcecode] around the code (with any of 28 different formatting sytles, including “cpp” for c++ and “text” for generic formatting).  So I can do snippets fairly easily, though multi-file programs may be a pain to distribute.  Downloading the snippets to the clipboard is easy, but downloading to files is not supported.  Still, that is probably good enough for this blog.

Digital-to-analog converter

Filed under: Digital music,Digital-to-analog conversion — gasstationwithoutpumps @ 01:30

One of the first concerns for producing musical sounds from a computer is converting the stream of numbers computed by the algorithm into a stream of voltages uniformly spaced in time. The uniform timing is the responsibility of the computer (in my case, an Arduino board), and I’ll cover that in a later post, but the conversion to voltages is best done with a digital-to-analog converter (DAC). (One can also try using the pulse-width modulation of the Arduino’s digital pins—I’ll cover that in a later post also.)

When I first built hardware for digital sound, DACs were expensive parts. My first board used a DAC0801LCN, an 8-bit DAC that is (amazingly) still available. It came in a 16-pin DIP and needed a fairly large power-supply voltage (±4.5v to ±18v).  It also uses up a lot of output pins on the Arduino (each of the 8-bits is a separate pin).

Old DAC circuitry on a vector board.

The DIP packaging was pretty nice, though, as I could use wirewrap sockets and wire the board without much trouble. (I can’t find my old hobbywrap wirewrap tool, and wire wrapping seems to have gone out of style with hobbyists—too bad as it was a very cheap, low-tech way to connect circuits, with less trouble than soldering and more robustness than breadboards.)

Here is the back of my old DAC board, showing some wire-wrapped and some soldered connections.

For my Arduino, I wanted a DAC that I could plug into the Arduino board directly, without ribbon cables or other complications, and that would not use up many of the I/O pins on the Arduino.
Modern DACs are mostly serial-interface devices, to cut down on pin count, so the resolution of the DAC is no longer tied to the number of pins used to interface to it. Since DACs have gotten dirt cheap in the past 30 years, I figured I’d go with a 12-bit DAC instead of an 8-bit one. This may have been a foolish choice, as a 16-bit DAC would not have cost much more and might have simplified the interface software (12 bits are not aligned to 8-bit words).

I ended up choosing a Texas Instruments part (TLV5618A) which has 2 12-bit DACs in an 8-pin DIP. Three pins are used for the serial digital interface, two for the analog outputs, two for power, and one for an analog reference input.  TI recommends a 2.048 voltage reference when using a 5v power supply, probably to make the voltage steps be a uniform 1mV.

The pinout for the TLV5618 DAC. Pins 4 and 7 are the outputs, Pins 5 and 8 are the power supply, Pin 6 is a reference voltage input, and pins 1,2, and 3 are the digital inputs.

If I’d been thinking clearly, I would have simply hooked the reference pin up to the 3.3v power supply on the Arduino (the data sheet says that the reference could be as high as Vdd-1.5v, which would be 3.5v), but instead I used a pair of resistors (a 22kΩ and a 15kΩ) to make a voltage divider that produces about 2.03 volts. I also put a capacitor between the reference pin and ground, so that it wouldn’t be too sensitive to noise on the power supply. Someone who cared more than me would probably use a zener diode to set the reference voltage more precisely with less noise, but I had resistors and capacitors on hand and would have had to wait several days to buy a zener diode. (It would also irk me to pay more for shipping than for the 40¢ part.)

Here is the schematic from my lab notebook. I realize that the capacitor does not have a size in the schematic—in fact I don't know much about the capacitor I used. I got a whole bunch of them in a grab bag at some point, and the labeling on them doesn't tell me much. They are labeled 564J100, which probably means 560nF ±5% and a 100volt rating (see the Wikipedia article on capacitor marking). I suppose I should try measuring the capacitance to confirm that assumption. I should also learn to use a free schematic capture program, so that people won't have to see the mess from my lab notebook.

The outputs are connected (via 2.2kΩ series resistors) to a headphone jack.  The DACs are capable of powering a small headphone (like an earbud), or you can use powered computer speakers or a headphone amplifier.  The series resistors are to keep from accidentally shorting out the DACs.

I built my digital-to-analog converter on the prototyping shield sold by ladyada.

I soldered up the board (including a socket for the DAC). One of the things I don’t like about the Ladyada prototyping shield is that the only access to the Arduino pins is through a set of female headers. I’m thinking of removing the both those headers and the male ones that connect to the Arduino on the bottom, and using long-tail female headers to both connect to the Arduino board and provide the ability to stack on top of the protoboard.  That would free up the holes where the female connectors are now to solder wires to. Ladyada sells the connectors as shield stacking connectors for Arduino, so I’m a little annoyed that they didn’t design them into the prototyping shield in the first place.  It would be a trivial change to their design, not even requiring a new PC board—just different header pieces and new instructions.

Prototyping board with a digital to analog converter

Finished prototyping board with a digital to analog converter

In a later post I’ll talk about how to communicate with the DAC over the serial link.

2011 June 14

Why the Arduino?

Filed under: Digital music,Metacomments (about the blog),Robotics — gasstationwithoutpumps @ 21:28

I am starting this blog to record various things that I’ll be doing with the Arduino microprocessors.

Although I used to be a computer engineering professor, this blog is not a record of my professional activity, but of hobbyist activity.  My interest in microprocessor programming goes back a long way (back to the days of the 8080, 6800, and Z80 in the mid 1970s), but I moved away from it for a decade and have only recently gotten back into it.

Of the many different chips and development environments available, I chose to play with the Arduino boards, because there was a good community of users and a lot of peripheral boards (known as “shields” in the Arduino community) already available.  Anything I did on an Arduino would likely have an audience of hobbyists.  The chips and boards themselves are cheap, easy to interface to, and much more powerful than the microprocessors I used to work with.  The development environment is free and runs on my Mac (as well as on other systems).

My initial impetus to get the Arduino was to do some more exploration of the plucked-string algorithm that Alex Strong and I patented in 1987 (now known as the Karplus-Strong algorithm, though Alex really deserved top billing for the algorithm). More specifically, I wanted to try implementing and modifying Alex’s later algorithm, which was only published as a patent.

From what I can see, few people are playing with digital synthesis algorithms any more, though the ATMega chips of the Arduino are many times more powerful than the 8080, Z80, and TI 9900 chips that we were using in the early 80s.  I’d like to provide some solid implementations for the Arduino community to experiment with, and maybe write some tutorials.

Since getting the Arduino, I’ve started coaching a high-school robotics club, and so I’ve gotten interested in sensors and motor controllers for the Arduino. I’ll talk about those things on this blog also.

Create a free website or blog at WordPress.com.