Thursday, May 17, 2012

Sonification - sound of sand - 8

Amplitude and spectrum combined - Now we have programmed the resampling correctly and the shape X- and Y-components are now present in both the spectrum and (on a micro level) in the amplitude of the signal. This is conceptually very satisfying and the sound has become much more interesting:

I'll start sonifying real sand grains only after I've explored all the theoretical possibilities. Have patience with me, I still have these possibilities to explore: (1) spectral components, (2) curvature and (3) mapping into "real" space using time differences between the ears of the listener and his distance from the sound source. And (4) I could use the filters of Nsound and try to map 2-D shapes into filtered white noise. Next time I'll do: (1) spectral components.

But I have sonified a few more of my test shapes:


Warning and disclaimerThese sounds could harm your sound system and could startle your pets. Turn the volume down before playing them.

I will stop exploring the X- and Y-shape components for the moment. Next time I'll try to sonify the 2-D spectral components of the shape. Just a few pictures:
 The spectrum of the star shape signal: X = top, Y = bottom.
The waveform of the star shape signal: X = top, Y = bottom.



from Nsound import *
import math


debug1 = False
debug2 = False
debug3 = True


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)


# read a chaincode .chc file that has been generated by SHAPE
def read_chc_file_to_xy_buffers(filename):
    infile = open(filename)
    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()
    return(b_x_chaincode, b_y_chaincode)


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_frequency_duration(shape_list, list_freq, samp_freq, duration):
    res_buf = Buffer()
    real_duration = int(math.ceil(duration * list_freq)) / list_freq
    res_len = int(math.ceil(real_duration * samp_freq))
    t_samp_freq = 1.0 / samp_freq
    t_list_freq = 1.0 / (list_freq * len(shape_list))
    for i in range(res_len):
        res_buf << shape_list[int(round(i * t_samp_freq / t_list_freq))%len(shape_list)]
    return(res_buf)


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

# read file into buffers
b_x_chaincode, b_y_chaincode = read_chc_file_to_xy_buffers("C:\\Users\\user\\Documents\\shape\\shape\\04 star.chc")


# 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_duration = 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)
    b_x_long << resample_list_frequency_duration(list_x, frequency, sampling_rate, sound_pixel_duration)


    if debug3:
        i = i+1
        p = int(len(b_x_chaincode)/5)
        if i%p == 0:
            b = Buffer()
            b = resample_list_frequency_duration(list_x, frequency, sampling_rate, sound_pixel_duration)
            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)
    b_y_long << resample_list_frequency_duration(list_y, frequency, sampling_rate, sound_pixel_duration)

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(sampling_rate, 2)
a[0] = b_x_long
a[1] = b_y_long
a.writeWavefile("C:\\Users\\user\\Documents\\shape\\shape\\04 star xy_freq_shape2.wav")

No comments:

Post a Comment