/*-
* Copyright (c) 2006 Hans Petter Selasky. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* image_sound.c - A program for making sound from pictures
*
* History:
*
* Tue Apr 11 15:52:13 CEST 2006:
* Fixed a small bug.
*
* Wed Apr 12 11:23:04 CEST 2006
* Added support for logarithmic frequency scale.
*
* Wed Apr 12 21:09:21 CEST 2006
* Fixed a small bug.
*
* This program outputs raw 22100Hz 16-bit signed audio.
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <math.h>
/*
*
* If one has got the SOX package installed, the following commands may
* be helpful under UNIX:
*
* ./image_sound -n 0 | play -c 1 -f s -t raw -r 22100 -s w /dev/fd/0
* ./image_sound -n 4 | sox -t raw -r 22100 -s -w -c 1 /dev/fd/0 test.wav
*
*/
#define D(x) ((double)(x))
#if 1
#define WINDOW_SIZE 256 /* requires 200MHz CPU at least */
#else
#define WINDOW_SIZE 128 /* requires 40MHz CPU at least */
#endif
#define IMAGE_SIZE_Y 256
#define SAMPLE_FREQ 22100 /* Hz */
#define SCALE_FREQ ((SAMPLE_FREQ+0.0) / ((WINDOW_SIZE/2)+0.0))
#define LOW_FREQ (500.0 / SCALE_FREQ) /* Hz */
#define HIGH_FREQ (5000.0 / SCALE_FREQ) /* Hz */
#define BANDWIDTH_LOG 800 /* relative to 1000 */
#define BANDWIDTH_LIN 975 /* relative to 1000 */
#define PRIME 0xffff1d /* a special prime number */
#define GET_PIXEL(p,x,y) ((p).pixel_data[((p).width * (y) * \
(p).bytes_per_pixel) + \
((p).bytes_per_pixel * (x))])
/*
* The following image files, 256x256xRGB,
* were created using GIMP 2.x, by
* selecting "Save as" "C source code"
*/
#ifndef HAVE_IMAGE
#include "my_image00.c"
#endif
static int32_t
get_white_noise(void)
{
static u_int32_t white_noise_rem = 1;
u_int32_t temp;
if (white_noise_rem & 1) {
white_noise_rem += PRIME;
}
white_noise_rem /= 2;
temp = white_noise_rem;
temp ^= 0x800000; /* signed to unsigned conversion */
if(temp & 0x800000) {
temp |= (-0x800000); /* sign extension */
}
return temp;
}
static void
print_sample(int16_t out)
{
printf("%c%c", out & 0xFF, (out >> 8) & 0xFF);
return;
}
/*
* the following routine is
* a classic FIR filter that
* filters the noise
*/
static double
filter_00(double *factor)
{
static double func[WINDOW_SIZE] = { /* zero */ };
static u_int16_t pos;
double temp = 0.0;
u_int32_t x;
func[pos] = get_white_noise() / 512;
pos++;
if(pos >= WINDOW_SIZE) {
pos = 0;
}
for(x = WINDOW_SIZE; x--; )
{
temp +=
factor[x] *
func[(pos+(WINDOW_SIZE-1)-x) % WINDOW_SIZE];
}
return temp;
}
static void
low_pass(double freq, double amp, double *factor)
{
int32_t x, z;
freq -= ((int32_t)(freq / D(WINDOW_SIZE/4))) * (WINDOW_SIZE/4);
z = (D(D(WINDOW_SIZE/2) / (2.0*freq)) * D((int32_t)(2.0*freq)));
if(z < 0) {
z = -z;
}
if(z > (WINDOW_SIZE/2)) {
z = (WINDOW_SIZE/2);
}
factor[(WINDOW_SIZE/2)] += (2.0 * amp * freq) / D(WINDOW_SIZE/2);
freq *= (2.0*M_PI) / D(WINDOW_SIZE/2);
for(x = -z+1; x < z; x++)
{
if(x != 0) {
factor[(x + (WINDOW_SIZE/2))] += (amp * sin(freq * D(x))) / (M_PI*D(x));
}
}
return;
}
static void
high_pass(double freq, double amp, double *factor)
{
/* NOTE: freq must be negative */
factor[WINDOW_SIZE/2] += 1.0*amp;
low_pass(-freq, amp, factor); /* high-pass */
return;
}
static void
band_pass(double freq_a, double freq_b, double amp, double *factor)
{
low_pass(freq_b, amp, factor); /* lowpass */
low_pass(-freq_a, amp, factor); /* highpass */
return;
}
static void
band_stop(double freq_a, double freq_b, double amp, double *factor)
{
factor[WINDOW_SIZE/2] += 1.0*amp;
low_pass(-freq_b, amp, factor); /* lowpass */
low_pass(freq_a, amp, factor); /* highpass */
return;
}
static struct {
double factor[WINDOW_SIZE];
} bp_data[IMAGE_SIZE_Y] = { /* zero */ };
int main(int argc, char **argv)
{
double factor_temp[WINDOW_SIZE] = { /* zero */ };
double factor_curr[WINDOW_SIZE] = { /* zero */ };
double factor_last[WINDOW_SIZE] = { /* zero */ };
double f;
double df;
double bw;
double amp;
double *f_ptr;
u_int32_t x,y,z,t,u;
u_int32_t repeat = 0; /* forever */
u_int32_t bandwidth = 0;
u_int8_t logarithmic = 0;
while(argc--)
{
if((argc >= 1) &&
(strcmp(argv[0], "-n") == 0))
{
repeat = atoi(argv[1]);
}
if((argc >= 1) &&
(strcmp(argv[0], "-b") == 0))
{
bandwidth = atoi(argv[1]);
}
if(strcmp(argv[0], "-l") == 0)
{
logarithmic = 1;
}
argv++;
}
if(bandwidth == 0)
{
bandwidth = logarithmic ? BANDWIDTH_LOG : BANDWIDTH_LIN;
}
if(logarithmic)
{
f = D(HIGH_FREQ);
df = pow(D(HIGH_FREQ) / D(LOW_FREQ),
1.0 / D(IMAGE_SIZE_Y));
bw = D(bandwidth)/D(1000);
for(x = 0; x < IMAGE_SIZE_Y; x++)
{
/* limit the bandwidth used by each pixel */
band_pass(f, (f * df * bw), (1.0 - bw) * 0.5,
&bp_data[x].factor[0]);
f /= df;
}
}
else
{
f = HIGH_FREQ;
df = (HIGH_FREQ-LOW_FREQ) / (IMAGE_SIZE_Y + 0.0);
bw = D(bandwidth)/D(1000);
/* initialize tables */
for(x = 0; x < IMAGE_SIZE_Y; x++)
{
/* limit the bandwidth used by each pixel */
band_pass(f, f+ (df * bw), 1.0,
&bp_data[x].factor[0]);
f -= df;
}
}
/* generate sound */
y = 0;
x = 0;
u = SAMPLE_FREQ/(WINDOW_SIZE/2);
while(1)
{
if(y == 0)
{
y = u;
x++;
if(x >= WINDOW_SIZE) {
x = 0;
if((repeat) && (!--repeat))
{
return 0;
}
}
bcopy(factor_curr, factor_last, sizeof(factor_last));
bzero(factor_curr, sizeof(factor_curr));
for(z = 0; z < IMAGE_SIZE_Y; z++)
{
f = GET_PIXEL(gimp_image,x*(256/WINDOW_SIZE),z*(256/IMAGE_SIZE_Y)) / 512.0;
f_ptr = &bp_data[z].factor[0];
#ifdef DEBUG
printf("%c", (f == 0.0) ? '.' : '#');
#endif
for(t = 0; t < WINDOW_SIZE; t++)
{
factor_curr[t] += f * f_ptr[t];
}
}
#ifdef DEBUG
printf("\n");
#endif
}
else
{
y--;
}
f = D(y) / D(u);
for(t = 0; t < WINDOW_SIZE; t++)
{
factor_temp[t] = (f * factor_last[t]) + ((1-f)*factor_curr[t]);
}
#ifndef DEBUG
print_sample(filter_00(&factor_temp[0]));
#endif
}
return 0;
}