Shallow Thoughts : tags : music

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Sat, 23 Mar 2024

Command-Line Metronome

I mentioned before that I'm taking beginner guitar lessons. Justin recommends using a metronome for some of the practicing, and that makes sense: I notice that sometimes when I practice I try to go too fast, which might or might not be good for learning the chord changes but it also leads to more mistakes and worse chord quality.

There are probably lots of phone metronome apps, but I'm usually practicing near my computer (where I watch the lessons and where I keep all my notes on chords and rhythms for particular songs), so I thought it would be nice to have a metronome on Linux.

Read more ...

Tags: , , ,
[ 18:37 Mar 23, 2024    More linux | permalink to this entry | ]

Tue, 27 Feb 2024

Transposing a Song in Audacity

I've been learning guitar. I've tried several times in the past without much success, concluding that although I love music, making it is just not where my talents lie. But this time I'm following a course (Justin Guitar), doing things more or less in the recommended order rather than jumping around randomly. It's fun, and I'm practicing regularly and feel like I'm making progress.

So far I know about five chords, and surprisingly, there are a lot of good songs you can play with only three to five chords. But if you want to play along with a record, some songs need a capo (one of those little dinguses you can clamp around the guitar's neck) to map the chords to ones that are easy for beginners to play.

I don't have a capo. Justin Guitar has a phone app that supposedly can transpose songs, but I wouldn't know about that, because it doesn't work on my phone, just spins forever any time I try to load a song. But I do have a computer; can't I just take a recording and transpose it?

Yes! It's easy in Audacity.

Read more ...

Tags: , ,
[ 14:06 Feb 27, 2024    More misc | permalink to this entry | ]

Wed, 05 Oct 2016

Play notes, chords and arbitrary waveforms from Python

Reading Stephen Wolfram's latest discussion of teaching computational thinking (which, though I mostly agree with it, is more an extended ad for Wolfram Programming Lab than a discussion of what computational thinking is and why we should teach it) I found myself musing over ideas for future computer classes for Los Alamos Makers. Students, and especially kids, like to see something other than words on a screen. Graphics and games good, or robotics when possible ... but another fun project a novice programmer can appreciate is music.

I found myself curious what you could do with Python, since I hadn't played much with Python sound generation libraries. I did discover a while ago that Python is rather bad at playing audio files, though I did eventually manage to write a music player script that works quite well. What about generating tones and chords?

A web search revealed that this is another thing Python is bad at. I found lots of people asking about chord generation, and a handful of half-baked ideas that relied on long obsolete packages or external program. But none of it actually worked, at least without requiring Windows or relying on larger packages like fluidsynth (which looked worth exploring some day when I have more time).

Play an arbitrary waveform with Pygame and NumPy

But I did find one example based on a long-obsolete Python package called Numeric which, when rewritten to use NumPy, actually played a sound. You can take a NumPy array and play it using a pygame.sndarray object this way:

import pygame, pygame.sndarray

def play_for(sample_wave, ms):
    """Play the given NumPy array, as a sound, for ms milliseconds."""
    sound = pygame.sndarray.make_sound(sample_wave)
    sound.play(-1)
    pygame.time.delay(ms)
    sound.stop()

Then you just need to calculate the waveform you want to play. NumPy can generate sine waves on its own, while scipy.signal can generate square and sawtooth waves. Like this:

import numpy
import scipy.signal

sample_rate = 44100

def sine_wave(hz, peak, n_samples=sample_rate):
    """Compute N samples of a sine wave with given frequency and peak amplitude.
       Defaults to one second.
    """
    length = sample_rate / float(hz)
    omega = numpy.pi * 2 / length
    xvalues = numpy.arange(int(length)) * omega
    onecycle = peak * numpy.sin(xvalues)
    return numpy.resize(onecycle, (n_samples,)).astype(numpy.int16)

def square_wave(hz, peak, duty_cycle=.5, n_samples=sample_rate):
    """Compute N samples of a sine wave with given frequency and peak amplitude.
       Defaults to one second.
    """
    t = numpy.linspace(0, 1, 500 * 440/hz, endpoint=False)
    wave = scipy.signal.square(2 * numpy.pi * 5 * t, duty=duty_cycle)
    wave = numpy.resize(wave, (n_samples,))
    return (peak / 2 * wave.astype(numpy.int16))

# Play A (440Hz) for 1 second as a sine wave:
play_for(sine_wave(440, 4096), 1000)

# Play A-440 for 1 second as a square wave:
play_for(square_wave(440, 4096), 1000)

Playing chords

That's all very well, but it's still a single tone, not a chord.

To generate a chord of two notes, you can add the waveforms for the two notes. For instance, 440Hz is concert A, and the A one octave above it is double the frequence, or 880 Hz. If you wanted to play a chord consisting of those two As, you could do it like this:

play_for(sum([sine_wave(440, 4096), sine_wave(880, 4096)]), 1000)

Simple octaves aren't very interesting to listen to. What you want is chords like major and minor triads and so forth. If you google for chord ratios Google helpfully gives you a few of them right off, then links to a page with a table of ratios for some common chords.

For instance, the major triad ratios are listed as 4:5:6. What does that mean? It means that for a C-E-G triad (the first C chord you learn in piano), the E's frequency is 5/4 of the C's frequency, and the G is 6/4 of the C.

You can pass that list, [4, 5, 5] to a function that will calculate the right ratios to produce the set of waveforms you need to add to get your chord:

def make_chord(hz, ratios):
    """Make a chord based on a list of frequency ratios."""
    sampling = 4096
    chord = waveform(hz, sampling)
    for r in ratios[1:]:
        chord = sum([chord, sine_wave(hz * r / ratios[0], sampling)])
    return chord

def major_triad(hz):
    return make_chord(hz, [4, 5, 6])

play_for(major_triad(440), length)

Even better, you can pass in the waveform you want to use when you're adding instruments together:

def make_chord(hz, ratios, waveform=None):
    """Make a chord based on a list of frequency ratios
       using a given waveform (defaults to a sine wave).
    """
    sampling = 4096
    if not waveform:
        waveform = sine_wave
    chord = waveform(hz, sampling)
    for r in ratios[1:]:
        chord = sum([chord, waveform(hz * r / ratios[0], sampling)])
    return chord

def major_triad(hz, waveform=None):
    return make_chord(hz, [4, 5, 6], waveform)

play_for(major_triad(440, square_wave), length)

There are still some problems. For instance, sawtooth_wave() works fine individually or for pairs of notes, but triads of sawtooths don't play correctly. I'm guessing something about the sampling rate is making their overtones cancel out part of the sawtooth wave. Triangle waves (in scipy.signal, that's a sawtooth wave with rising ramp width of 0.5) don't seem to work right even for single tones. I'm sure these are solvable, perhaps by fiddling with the sampling rate. I'll probably need to add graphics so I can look at the waveform for debugging purposes.

In any case, it was a fun morning hack. Most chords work pretty well, and it's nice to know how to to play any waveform I can generate.

The full script is here: play_chord.py on GitHub.

Tags: , ,
[ 11:29 Oct 05, 2016    More programming | permalink to this entry | ]

Sun, 17 Feb 2013

Playing music or sound samples on Arduino

I've done a few experiments with playing music on an Arduino over the years -- the Arduino library has a tone() call that gives you a nice tinny monophonic "chiptunes" style noise. But for playing anything more complex, you need more processing power.

I had a silly little project in mind. I like some pink noise when I'm sleeping (in summer, I usually have a fan running). You can buy electronic devices called "sleep soothers" that have tracks of the ocean, rain, trains etc. but they often have annoying misfeatures (like foghorns or train whistles in the middle of a track). Wouldn't it be more fun to make my own, with sound samples of my choice?

Pink noise samples

Of course, I needed some sound samples, and I found a great resource: Laptop.org's list of Free sound samples. I downloaded a few sample collections that looked like they might have some nice ambient pink-noise sounds -- rain, ocean and so forth.

Some of the samples were usable right away. But others are only available at 44.1k, and the Adafruit Wave Shield, the hardware I was targeting first, will only play WAV audio at 16k. So they needed to be converted. A simple shell loop did that:

for fil in *.wav ; do
  avconv -i $fil -ar 16000 ../samples16/$fil
  echo $fil
done

Arduino hardware

There are several Arduino shields for playing sound files. The simplest (and probably cheapest) is the Adafruit Wave Shield, and that's what I started with. It comes as a kit that you solder together (an easy soldering project) and it has its own volume control and SD card reader. On the down side, it can only play .WAV files, not other formats like .MP3 or .OGG. But for my sleep soother project that was okay.

Getting basic sounds playing was easy, following Adafruit's tutorial and sample code. But for my project, I also needed some external buttons, to allow for jumping from one track to the next. I was a little confused about which pins are used by the shield, and I ended up wiring my button to one of the pins that the shield uses for talking to the SD card reader. Things didn't work at all. And then while I was fumbling with plugging/unplugging things, at some point I installed the shield onto the Arduino wrong, with the pins off by one. I'm not sure whether it was the miswired button or the off-by-one shield, but something got fried in the wave shield and it was never able to read an SD card again after that (yes, even after plugging it in properly).

I thought about ordering another Wave Shield. But I was leery -- if it's so delicate that a stray 5v signal in the wrong place can fry it permanently, how long did I expect a second one to last? Besides, I was tired of soldering things, and I happened to be putting in an Amazon order for some other things. So I ran a search and found that there was an MP3 player shield available through them, made by Seeed Studio. It even had buttons built in, so I wouldn't need any extra hardware. It was a little more expensive than the Wave shield, but it claimed to play MP3 and OGG as well as WAV, and it comes pre-assembled, no soldering needed.

The hardware arrived and looked nice. Two simple buttons plus a "multifunction" button you can press or rock left or right. I grabbed a micro SD card, put some MP3s on it, and went to Seeed's page on the Music Shield.

Hacking the Seeed library

I was a little worried when I discovered that they have three different demos there -- each of which comes with a different library, and you have to remove one set of libraries before you can try a different demo. Not a good sign.

And indeed, it wasn't. None of the demos worked at all. When I added some debug Serial.printlns, I found that it wasn't opening the SD card.

Much web searching found a couple of people saying they'd discovered that the Seeed shield only works with 2G or smaller microSD cards. Good luck finding one of those! The next day, I drove all over town looking for one, without success, and was on the verge of giving up when Dave remembered a little cheapie camera he'd bought a few years ago for taking airplane movies. It came with a microSD card. Success! It was a 2G card.

Back to trying the various demos and their incompatible libraries again. And this time, one of the demos, the first one (the one that comes with the Music v1 14.zip library), worked. I could play tracks, sequentially as they were loaded on the SD card.

Unfortunately, that wasn't what I wanted to do -- I wanted to play the same track over and over, jumping to the next track when the user presses a button. I tried the other demos. None of them worked at all.

Long story short, after struggling for the better part of a week and reverse-engineering a fair amount of the Music v1 14 library, I finally got my sketch working.

Sharing the changes

I come from the open-source world. I keep my Arduino sketches on GitHub (at least once they work well enough to be useful to anybody). So of course I wanted to share the work I'd put into fixing this library.

I had it all laid out and ready to commit, and was working on some documentation on how to use it, when I noticed the readme.txt in the library directory. It begins:

Copyright (c) 2010 Seedstudio.  All right reserved.

Pffft! So after finally getting things working, I can't share my working version of the library! What are they thinking? What on earth is the point of distributing a library for your hardware, one that you know doesn't work very well (or you wouldn't be distributing four different incompatible versions of it), and then not letting anyone fix it for you?

I posted a query in one of the many threads discussing problems with the Music Shield, asking if Seeed would consider releasing the library under a license that allowed redistribution. It's been a few weeks now, and no answer yet.

Incidentally, even posting the query wasn't easy. Seeed doesn't let you post in their forums unless you register -- and the registration process requires full name, address, and phone number! Fortunately, they have no way of knowing whether the info you give them is fake, so that's what I did.

Since I don't have permission to share the code directly, I've checked in a patch that updates their library so it can play arbitrary tracks, not just sequential ones, and can re-play the same track. It's here: Music Shield library on GitHub, along with my sample app, called play-repeat.

Conclusions

So my app is working now. Well, mostly; sometimes the volume randomly jumps in the middle of the night, which I think is a hardware glitch, but since it only happens after several hours of play, it's hard to debug.

But if you're looking for an Arduino sound project, I can't recommend either the Wave Shield or the Seeed Music Shield. The Wave Shield seems too fragile and its formats are limited, though the tutorials and support are great. And I'll certainly never buy anything from Seeed again.

If I had it to do over again, I'd spend the big bucks and buy the Sparkfun MP3 Player Shield. It's more expensive ($40) and doesn't have nice buttons like the Seeed shield, but it plays all the formats the Seeed shield does, and they offer tons of documentation and examples, including an open-source library and code contributed by users.

Tags: , , ,
[ 12:37 Feb 17, 2013    More hardware | permalink to this entry | ]

Fri, 02 Mar 2012

Music with an Arduino

Working on projects that might be fun for a proposed Arduino high school workshop, I realized I hadn't done much with Arduinos and sound. I'd made clicking noise for my sonar echolocation device, but nothing more than that.

But the Arduino library has a nice function to control a speaker: tone(int pin, int frequency, int length).

tone() works much better than trying to make your own square wave, because it uses interrupts and doesn't glitch when the processor gets busy doing other things. You can leave off the length parameter and the tone will continue until you tell it to stop or change frequency.

Random tones

So you can produce random tones like this (SPEAKER is the pin the speaker is plugged into):

uint8_t SPEAKER = 8;

void setup()
{
    pinMode(SPEAKER, OUTPUT);
    // Seed the random number generator from floating analog pin 0:
    randomSeed(analogRead(0));
}

void loop()
{
    // Random frequency between 20 and 1400 (Hz).
    unsigned long freq = random(20, 1400);
    long duration = random(5, 50);

    tone(SPEAKER, freq, duration);
    delay(random(100, 300));
}

Light theremin

Purely random tones aren't very interesting to listen to, though, as it turns out.

How about taking input from a photo-resistor, to make a light theremin that wails as I move my hand up and down above the sensor? The photoresistor I was using typically reads, on the Arduino, between 110 (with my hand over the sensor) and 800. So I wanted to map that range to audible frequencies the speaker could handle, between about 20 Hz and 5000.

uint8_t LIGHTSENSOR = 0;
void loop()
{
    // Set the frequency according to the light value read off analog pin 0.
#define MAX_SIGNAL 800
#define MAX_FREQ  5000
#define MIN_SIGNAL 380
#define MIN_FREQ    20
    int lightsensor = analogRead(LIGHTSENSOR);
    int freq = (lightsensor - MIN_SIGNAL) 
                * (float)(MAX_FREQ - MIN_FREQ) 
                / (MAX_SIGNAL - MIN_SIGNAL)
               + MIN_FREQ;
    tone(SPEAKER, freq);
}

Random music (chiptunes)

That was fun, but I still wanted to try some random music that actually sounded ... musical. So how about programming the standard scale, and choosing frequencies from that list?

I looked up the frequency for Middle C, then used fractions to calculate the rest of the "just" diatonic scale for C major:

float middle_c = 262.626;
float just[] = { 1., 9./8, 5./4, 4./3, 3./2, 5./3, 15./8 };
#define NUMNOTES (sizeof(just)/sizeof(*just))
float cur_octave = 1.;

Multiplying the frequency by 2 transposes a note up one octave; dividing by two, down one octave. cur_octave will keep track of that.

Now if whichnote is a number from 0 to 7, cur_octave * just[whichnote] * middle_c will give the next frequency to play.

Just choosing notes from this list wasn't all that interesting either. So I adjusted the code to make it more likely to choose a note just one step up or down from the current note, so you'd get more runs.

    rand = random(0, 6);
    if (rand == 0)
        whichnote = (whichnote + 1) % NUMNOTES;
    else if (rand == 1)
        whichnote = (whichnote + 1) % NUMNOTES;
    else
        whichnote = random(0, NUMNOTES);

    float freq = middle_c * just[whichnote];

    // Change octave?
    rand = random(0, 10);
    if (rand == 1 && cur_octave <= 3) {
        cur_octave *= 2.;
    } else if (rand == 2 && cur_octave >= 1) {
        cur_octave /= 2.;
    }
    freq *= cur_octave;

It's still not great music, but it's a fun experiment and I'm looking forward to adding more rules and seeing how the music improves.

Bach

But this left me hungry for real music. What if I wanted to play a real piece of music? Certainly I wouldn't want to type in an array of frequency numbers, or even fractions. I'd like to be able to say A, Ab (for A-flat), Cs (for C-sharp), etc.

So I defined the frequency for each of the notes in the scale:

#define NOTE_Ab 207.652
#define NOTE_A  220.000
#define NOTE_As 233.082
#define NOTE_Bb NOTE_As
#define NOTE_B  246.942
#define NOTE_C  261.626
#define NOTE_Cs 277.183
#define NOTE_Db NOTE_Cs
#define NOTE_D  293.665
#define NOTE_Ds 311.127
#define NOTE_Eb NOTE_Ds
#define NOTE_E  329.628
#define NOTE_F  349.228
#define NOTE_Fs 369.994
#define NOTE_Gb NOTE_Fs
#define NOTE_G  391.995
#define NOTE_Gs 415.305

#define NOTE_REST     0.0
#define NOTE_SUSTAIN -1.0

Then the first part of Bach's 2-part invention #4 in D minor looks like this:

float composition[] = {
    NOTE_D, NOTE_E, NOTE_F, NOTE_G, NOTE_A*2, NOTE_As*2,
    NOTE_Db, NOTE_As*2, NOTE_A*2, NOTE_G, NOTE_F, NOTE_E,
    NOTE_F, NOTE_REST, NOTE_A*2, NOTE_REST, NOTE_D*2, NOTE_REST,
    NOTE_G, NOTE_REST, NOTE_Cs*2, NOTE_REST, NOTE_E*2, NOTE_REST,

    NOTE_D*2, NOTE_E*2, NOTE_F*2, NOTE_G*2, NOTE_A*4, NOTE_As*4,
    NOTE_Db*2, NOTE_As*4, NOTE_A*4, NOTE_G*2, NOTE_F*2, NOTE_E*2,
};

And the code to play it looks like:

    unsigned long note = composition[i++];
    if (note == NOTE_REST)
        noTone(SPEAKER);
    else if (note == NOTE_SUSTAIN)
        ;      // Don't do anything, just let the current tone continue
    else
        tone(SPEAKER, note);

It's a bit tedious to type in the notes one by one like that, which is why I stopped when I did. And as far as I know, the Arduino can only emit one tone at once -- to play the real 2-part invention, you either need a second Arduino, or extra hardware like a wave shield.

Anyway, it was a fun series of experiments, even if none of it produces great music. You can see the source at github: akkana/arduino/music.

Tags: , , ,
[ 19:54 Mar 02, 2012    More hardware | permalink to this entry | ]