Thursday, May 17, 2012

Sonification - sound of sand - 7

An interesting error - In this program I tried to map the shape to the frequency domain while preserving its shape in the amplitude domain. For each different frequency I resampled the shape. This yields an interesting result, both auditively:

And visually:
The error is in the quantization of frequency. I use this formula in the integer domain. The rounding from float to integer maps many different frequencies (especially the higher frequencies) to the same number of samples. This causes the "blockiness" of the spectrum: 


The sampling itself works nicely. For lower frequencies the sampling distortion is invisible. For higher frequencies the shape gets distorted but is still recognizable


This is how it looks in Audacity:

from Nsound import *
import math


def convert_chaincode_to_x(c,x):
    if c == 1 or c == 0 or c == 7:
        x = x + 1
        return (x)
    elif ( c == 2 or c == 6):
        x = x
        return (x)
    else:
        x = x - 1
        return (x)


def convert_chaincode_to_y(c,y):
    if c == 1 or c == 2 or c == 3:
        y = y + 1
        return(y)
    elif c == 4 or c == 0:
        y = y
        return(y)
    else:
        y = y - 1
        return(y)


def convert_xy_frequency(xy, minxy, maxxy, minf, maxf):
    r = float(maxf - minf)/float(maxxy - minxy)
    f = (xy - minxy)*r + minf
    return (f)


def resample_list(nr_samples, list):
    resampled = Buffer()
    for i in range(nr_samples):
        resampled << list[int(i*(len(list)-1)/(nr_samples -1))]
    return(resampled)


# ==============================

debug1  = False
debug2  = False
debug3  = True

# read a chaincode .chc file that has been generated by SHAPE
infile = open("C:\\Users\\user\\Documents\\shape\\shape\\tiny_test.chc")
instr = infile.read()
infile.close()
if debug1:
    print instr

# parse the input file - split it into words
inwords = instr.split(' ')
if debug1:
    print inwords

# delete anything except the chain code
i = 0
for str in inwords:
    if str.find('0E+0') > -1 :
        break
    i = i + 1
inwords = inwords[i+2:len(inwords)-1]
if debug1:
    print inwords


# fill the x and y buffers with the chaincode values
b_x_chaincode = Buffer()
b_y_chaincode = Buffer()

x = 0
y = 0
for str in inwords:
    c = int(str)
    x = convert_chaincode_to_x(c,x)
    b_x_chaincode << x
    y = convert_chaincode_to_y(c,y)
    b_y_chaincode << y

b_x_chaincode = b_x_chaincode - b_x_chaincode.getMean()
b_y_chaincode = b_y_chaincode - b_y_chaincode.getMean()

if debug2:
    b_x_chaincode.plot("x value from chaincode")
    Plotter.show()
    b_y_chaincode.plot("y value from chaincode")
    Plotter.show()


# copy buffer to list so we can point to it by index
list_x = b_x_chaincode.toList()
list_y = b_y_chaincode.toList()


# generate a frequency modulated x and y signal
b_x_long = Buffer()
b_y_long = Buffer()

min_f = 40.0
max_f = 2000.0
sampling_rate = 44100.0
sound_pixel_length = sampling_rate * 0.01

min_x = b_x_chaincode.getMin()
max_x = b_x_chaincode.getMax()

if debug3:
    i=0
for x in b_x_chaincode:
    frequency = convert_xy_frequency(x, min_x, max_x, min_f, max_f)
    nr_samples = int(round(sampling_rate / frequency))
    temp = Buffer()
    temp << resample_list(nr_samples, list_x)
    for j in range(int(math.ceil(sound_pixel_length/nr_samples))):
        b_x_long << temp

    if debug3:
        i = i+1
        p = int(len(b_x_chaincode)/5)
        if i%p == 0:
            b = Buffer()
            b = resample_list(nr_samples, list_x)
            b.plot(frequency)
            Plotter.show()

b_x_long.normalize()

min_y = b_y_chaincode.getMin()
max_y = b_y_chaincode.getMax()
for y in b_y_chaincode:
    frequency = convert_xy_frequency(y, min_y, max_y, min_f, max_f)
    nr_samples = int(round(sampling_rate / frequency))
    temp = Buffer()
    temp << resample_list(nr_samples, list_y)
    for i in range(int(math.ceil(sound_pixel_length/nr_samples))):
        b_y_long << temp
b_y_long.normalize()


# code the x and y signal into the left and right channel of an audio stream
# write the audio stream into a .wav file
a = AudioStream(44100.0, 2)
a[0] = b_x_long
a[1] = b_y_long
a.writeWavefile("C:\\Users\\user\\Documents\\shape\\shape\\tiny_test_xy_freq_shape.wav")

No comments:

Post a Comment