#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

# a very basic framework for playing music

edited April 2012 Posts: 93

this is a fairly simple framework for playing musical notes with SFXR. it is based on the drum machine example.

the function note() will play notes. at the moment is is very primitive- all it will do is take a number value, in terms of semitones above (or, for negative values, below) C3, and the waveform to use. it also has, in this example, a kick and hat sound which can be used.

if someone else has time, making it so that it checks a table of frequency translations that is generated at setup(), rather than doing the 0.19 * 2^24*freq every time it plays a new note would be just great. maybe even make it so that you can enter in a sting such as note_c#4 rather than 14.

i will be making a program that allows you to write songs now, and will post that once it is complete. that program will plrobably include options for note length/volume and such.

Tagged:

• Posts: 202

Before attacking your precalculation problem let's get some existing things straight, perhaps I don't understand SFXR properly ...

StartFrequency = 0.19 * 2^(freq/24)

1. freq seems to be a relative note number rather than a frequency.

StartFrequency = 0.19 * 2^(rel_note/24)

2. You say it is relative to C3 which has a frequency of about 130Hz.

StartFrequency = 0.13 * 2^(rel_note/24)

(Frequency is given in kHz, right?)

3. An octave is made up of 12 semitones.

StartFrequency = 0.13 * 2^(rel_note/12)

If you're writing a program to make music, you may base your note numbers on MIDI numbers, so A4 (I call THAT a reference, what the hell is C3?) is number 69.

``````function freqOfMidiNote(note_number)
return 0.44 * 2 ^ ((note_number - 69) / 12)
end

``````

While I'm at it, you can now make a table of frequencies:

``````frequencies = {}
for i = 0, 100 do
frequencies[i] = freqOfMidiNote(i)
end

``````
• edited April 2012 Posts: 93

The freq is just a note number, not an actual frequency. I gave it at name because i wanted to avoid mixing it up with the function name. SFXR uses a 0-1 scale rather than hertz, which is why the math seems counterintuitive.

I spent about an hour with a tuner trying to nail down the numbers and I chose C3 because it was the absolute closest to a round value- its frequency in SFXR is 0.1902. To make the equation follow midi scale, simply change the frequency calculation to 0.19 *2((freq-60)/24)

I'm not having a problem with making a frequency table, persay. I just didn't feel like doing it. :P I am working on a program that enables you to write midi-like songs with this system at the moment.

• Posts: 384

Hi folks,

I too have spent many hours tuning SFXR... I found the magic formula to equate keyboard notes with pitches. It's part of my ABCplayer, which reads ABC tunes - the ASCII equivalent of MIDI. Thread with links to code here: http://twolivesleft.com/Codea/Talk/discussion/225/abcplayercodea-play-music-in-your-project#Item_46

Have a look and try it out, let me know if you would like to collaborate. Next I plan to work on different timbres an multivoicing using the ABC standard.

• Posts: 202

I thought an authoritative answer could be a valid reason to bring this thread back from the dead, be it just for education or further inspiration. I also had to correct myself, it bugged me.

The core line in SFXR is this:

``````fperiod=100.0/(p_base_freq*p_base_freq+0.001);

``````

Remember that p_base_freq is expressed in a range of 0...1. Examining the code further reveals that fperiod is not exactly the period, but 8 times supersampled. Let me reflect this:

``````8*fperiod=100.0/(p_base_freq*p_base_freq+0.001);

``````

Also, fperiod is expressed in terms of the sample rate, that means that at a sample rate of 44100 Hz a period value of 44100 means a sound of 1 Hz. It means that fperiod = SR/f, with f = frequency of the sound and SR = sample rate.

``````8*SR/f=100.0/(p_base_freq*p_base_freq+0.001);

``````

Reordering:

``````f/SR=8/100*(p_base_freq*p_base_freq+0.001)

(1) p_base_freq=sqrt(f/SR*100/8-0.001)

``````

You can use the formula (1) directly, with SR = 44100, to calculate the correct value to feed into SFXR.

Quick check: With f = 130 Hz (note C3) the result is p_base_freq = 0.19. KMEB is right. We have a formula for converting a frequency to an SFXR base frequency value.

Next step: Let's ignore the slight offset of 0.001 and write the square root as a power.

``````(2) p_base_freq=(f/SR*100/8)^(1/2)

``````

Express the frequency using a MIDI note. The basics are:

``````f=fN*2^((n-N)/12)

with
n  : MIDI note
N  : base note for this formula (your choice)
fN : frequency of fN

use for example:
N  = 69 (note A4)
fN = 440

or as KMEB did:
N  = 48 (note C3)
fN = 130

``````

Insert f into (2) and simplify:

``````p_base_freq=(fN*2^((n-N)/12)/SR*100/8)^(1/2)

(3) p_base_freq=(2^((n-N)/24)*(fN/SR*100/8)^(1/2)

``````

Using KMEB's choice we get (keep in mind that n is the MIDI note number, therefore n-48, whereas KMEB sets C3 to number 0):

``````p_base_freq=(2^((n-48)/24)*0.19

``````

Phew, it's time to say "and that's it".

• Posts: 384

Wow, @Codeslinger, I'm impressed you tracked down how sfxr works out that value. I had fudged it by finding the 0.5 sfxr input to be A440 and tuning a semitone increment by ear to a multiplier of 1.0296. Your formula makes more sense, so I'll merge it into my code, if that's ok.

• Posts: 146

@Codeslinger very nice analysis, I'm sure that will be helpful for future musical endeavours with codea

• Mod
edited May 2012 Posts: 1,557

Wonderful analysis! I knew if I sat long enough someone would be bothered enough to go reverse-engineer the whole sfxr thing! Thank you, and bravo!

This needs to go in the wiki.

• Posts: 384

Hi @Codeslinger, I don't suppose we could call you back to look at sfxr's SustainTime? It's not in seconds, and I had to take the square root to get manageable durations, but that makes it too short...

• Posts: 202

I'm afraid to come up with an unsatisfying intermediate analysis.

The core lines in SFXR are these:

``````env_length[0]=(int)(p_env_attack*p_env_attack*100000.0f);
env_length[1]=(int)(p_env_sustain*p_env_sustain*100000.0f);
env_length[2]=(int)(p_env_decay*p_env_decay*100000.0f);

``````

If you set attack and decay to zero and sustain to 1, SFXR will generate 100000 samples (precisely, it will generate 100003 samples, but that doesn't matter here). At a rate of 44100 samples per seconds the duration will be a bit more than 2 seconds.

I just compiled SFXR right out of the Codea Runtime to test it and I was right, at least on my Linux system. The formula here is:

``````with d: duration in seconds

p_env_sustain = sqrt(d * 0.441)

where 0.441 = sample rate / 100000

``````

This is difficult to recreate with Codea's Example "Sounds Plus" in the advanced pane, even you "purify" the playSound function. Don't know the reason yet.

• Posts: 384

Oh wow, @Codeslinger, that's interesting. Thanks for looking into this. So we will have to take into account the sample size...

On another note (no pun intended) I notice that the formula you and @KMEB worked on is a bit out of tune. I think you need that 0.001 in there.

Thanks!

• Posts: 384

Thanks @Codeslinger, it works just right. I found when using it on sounds with attack and
decay that if I wanted the entire sound to fade away after x seconds that I should subtract the existing attack and decay values from the result of your formula. As long as the attack and delay aren't longer than x.