Skip to content

aempirei/Bit-Music

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bit Music | Analysis of 1-liner C music

Copyright(c) 2011 by Christopher Abad
20 GOTO 10 | [email protected]

This project was inspired by two youtube videos:

	"Experimental one-line algorithmic music - the 2nd iteration"
	http://www.youtube.com/watch?v=qlrs2Vorw2Y

	"Experimental music from very short C programs"
	http://www.youtube.com/watch?v=GtQdIYUtAHg

The musical generation of the 1-liner C program is of the form:

	unsigned char transform(unsigned long t);

	for(t = 0; t < (1 << N); t++) {
		putchar(transform(t));
	}

This also assumes tha output is used to feed a PCM audio device at 8000hz 8-bit mono.

This process can alternatively be described in many ways. It would first be useful to look at the loop iterator (t) in a specific novel way.  What at first appears to be a simple monotonically increasing sequence may prove to be more interesting and insightful when written as a set of binary vectors {b_n} each representing a single-bit slice of (t) over the entire sequence of iterations.

	  t  =  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15 ... 2^N-1
	     +-------------------------------------------------------------------------
	b_0  |  0   1   0   1   0   1   0   1   0   1   0   1   0   1   0   1 ...   1
	     +-------------------------------------------------------------------------
	b_1  |  0   0   1   1   0   0   1   1   0   0   1   1   0   0   1   1 ...   1
	     +-------------------------------------------------------------------------
	b_2  |  0   0   0   0   1   1   1   1   0   0   0   0   1   1   1   1 ...   1
	     +-------------------------------------------------------------------------
	b_n  |  0 x (2^n) ...   1 x (2^n) ...                                       1
	     +-------------------------------------------------------------------------

b_n(t) = (t>>n)&1

In isolation, each bit-position as an independent sequence generates a square wave. Furthermore, each period from the lowest position upward is doubling. Incidentally, when taken as a vector set, they are orthagonal. These features readily form a very useful linear algebraic basis.

The signal frequency series are:

	f_n = frequency(b_n) (hz)
	f_n = 8000 / 2^(n+1) (hz)
	f_n = { 4000, 2000, 1000, 500, 250, 125, 62.5, 31.25, 15.625, ... } . (hz)

The signal peroids are:

	p_n = 1 / f_n (s)
	p_n = { ..., 0.128, 0.256, 0.512, 1.024, 2.048, 4.096, 8.192, 16.384, ... } . (s)

To select a tone and duration, it is simply a matter of selecting the corresponding bit vectors via an AND mask and composing the two in some manner.

To select the 1000hz tone at bit vector b_2, calculate function b_2(t) = (t>>2)&1
To select a note duration of approximately 1/4th second, calculate the following:

       2^(n+1) = 8000 * 1/4
   n+1 log_2 2 = log_2 2000
           n+1 = log_2 2000
             n = log_2 2000 - 1
             n ~ 11 - 1 ~ 10

Then select b_10 and via function b_10(t) = (t>>10)&1

An entire b_n vector can be calculated as b_n = { b_n(0), ..., b_n(N-1) }

Now, the signals can be composed by basic mathematical operations such as integer multiplication (*) or biwise AND (&) and can be described as binary amplitude modulation (AM) or on-off keying (OOK).

The final signal generation function:

	b_2_t = (t>>2) & 1   // select tone signal
	b_10_t = (t>>10) & 1 // select duration signal
	o = b_10_t & b_2_t   // modulate duration signal by tone signal
	o <<= 6              // select volume

Reduction of the above produces this compact form:

	o = ( (t >> 2) & (t >> 10) & 1 ) << 6

Further reduction:

	o = (t << 4) & (t >> 4) & 64

The same process can be described as a dot-product operation with (s), the selection vector, operating on (t) as s.t, and then another dot-product operation (c), the composition vector, also operating on (t) as c.t, and then finally the volume scalar (v), so that the complete modulation is v(s.t)(c.t).

At this point, this becomes an interesting point of study for Kolmogorov complexity and the computation of music.

Just given about 9 tones and 8 time intervals, it would be easy to sequence somewhat novel audio scores, but we're going to continue the breakdown of this basis to improve upon these numeric tools.

Given that the initial basis {b_n} is orthagonal, but of row rank significantly lower than its column rank, it would be of moderate interest to see if a complete linear independant orthagonal basis of rank 2^N could be generated from the original basis in some simple way. Given this, then in an albiet rather trival manner, any sequence of length 2^N could be managed in a rather long "1-liner". But then provided sufficient knowledge in the theory of data compression, an optimization between a minimal vector subset and a maximal signal approximation could be reached using only the most basic of algebraic operations.

Construction of a complete orthagonal basis of rank 2^N inituitively can be imagined to be by time-scaling of any of the original basis. One additional fact to remember is that each of the square-waves are self-similar and only differ in their time-scale. Also, provided that the total time interval of a complete sequence aligns on the period boundary of every basis vector, then the complete basis will retain orthagonality.  So only a single representative vector from the basis is needed to generate an entire basis of rank 2^N provided a simple time-scaling function can be determined. Firstly, selection of the basis vector with minimal period is simply:

	b_0(t) = (t & 1)

To scale a function in range by a constant factor of (k) would be k*f(n), while to scale a function in domain by the same factor would be f(n/k).  So then it should follow that the period of the basis vector b_0 can be expanded simply by b_0(t/(k+1)) = (floor(t/(k+1)) & 1). The original b_0 square wave is an odd function, analogous to the sine function, so therefore given k = { 0, ..., 2^N-1 } then the complete basis can be generated by:

	b'_k = { b_0(2 * t / (k + 1)) } = { floor(2 * t / (k + 1)) & 1 }

With the frequency of each basis vector:

	f'_k = frequency(b'_k) (hz)
	f'_k = (8000/k) mod 8000 (hz)
	f'_k = { 0, 4000, 2666, 2000, 1600, 1333, 1142, 888, 800, 727, 666, ... } . (hz)

Unfortunately, unlike the DST, the discrete implementations of this basis are not orthagonal because of aliasing problems at period boundaries of many of the square waves.  When the wave frequency does not divide the number of discrete samples in the signal, then an imbalance in the distribution of the high and low states of the signal. Even tho this does not mean the basis is not linear independant, the basis loses some of the simplicities in decomposition calculations. Orthagonalization of this basis can be performed at the cost of significant complexity. A famous compromise between the hilbert square-wave basis and the advantages of orthagonality is the "Haar Wavelet".

Trying to revise the original modulation scheme with a target frequency of 2600hz, we get the following:

	k = 8000/2600 ~ 3.08
	b'_3.08 = b_0(2*t/(3.08+1)) = floor(2*t/(3.08+1))&1 = floor(t*2/4.08)&1 // select tone
	b_10_t = (t>>10) & 1 // select duration signal
	o = b_10_t & b'3.08  // modulate duration signal by tone signal
	o <<= 6              // select volume

One may notice that when a frequency that is significantly misaligned with the total signal period is selected, many artifacts arise in the output signal.

Reduction of the above produces this compact form:

	o = ( ((t>>10)&1) & (((int)floor(t*2.0/4.08))&1) ) << 6

Given a number of output signals o = {o_k}, these signals can then be composed together with a few possible methods, namely binary XOR (^), binary OR (|) and arithmetic addition (+), each with various auditory effects.  Namely, addition runs the risk of clipping as the mean signal power approaches the range limit of the output channel (in this case 8-bit or 255), OR may saturate all of the output bits if the signal becomes too complex, and XOR may cancel out common-mode signals of similar power.

	o' =  o_0 + o_1 + ... + O_n
	o' =  o_0 ^ o_1 ^ ... ^ O_n
	o' =  o_0 | o_1 | ... | O_n

More shit to come, obviously.

About

Analysis of 1-Liner Music

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published