square wave generator

Square wave = sign(sinusoidal wave)
So then why isn't the following code creating a square wave:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
int sgn(double d){
  if (d>=0) d=1;
   else d=-1;
  return d;
    }

static void generate_square(const snd_pcm_channel_area_t *areas, 
                           snd_pcm_uframes_t offset,
                           int count, double *_phase)
{
    static double max_phase = 2. * M_PI;
    double phase = *_phase;
    double step = max_phase*freq/(double)rate;
    unsigned char *samples[channels];
    int steps[channels];
    unsigned int chn;
    int format_bits = snd_pcm_format_width(format);
    unsigned int maxval = (1 << (format_bits - 1)) - 1;
    int bps = format_bits / 8;  // bytes per sample 
    int phys_bps = snd_pcm_format_physical_width(format) / 8;
    int big_endian = snd_pcm_format_big_endian(format) == 1;
    int to_unsigned = snd_pcm_format_unsigned(format) == 1;
    int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
                    format == SND_PCM_FORMAT_FLOAT_BE);
    double amplitude_scale = amplitude/8.56;


    // verify and prepare the contents of areas 
    for (chn = 0; chn < channels; chn++) {
            if ((areas[chn].first % 8) != 0) {
                    printf("areas[%i].first == %i, aborting...", chn , areas[chn].first);
                    exit(EXIT_FAILURE);
            }
            samples[chn] = (((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
           if ((areas[chn].step % 16) != 0) {
                   // printf("areas[%i].step == %i, aborting...  ", chn areas[chn].step);
                   exit(EXIT_FAILURE);
            }
            steps[chn] = areas[chn].step / 8;
            samples[chn] += offset * steps[chn];
    }
    // fill the channel areas 
    while (count-- > 0) {
            union {
                    float f;
                    int i;
            } fval;
            int res, i;
            if (is_float) {
		    int a = sgn(amplitude_scale * sin(phase) * maxval);
                    fval.f = (float) a;
                    res = fval.i;
            } else { 
		    int b = sgn(amplitude_scale * sin(phase) * maxval);
                    res = b;
	    }
                    

            if (to_unsigned)
                    res ^= 1U << (format_bits - 1);
            for (chn = 0; chn < channels; chn++) {
                    // Generate data in native endian format 
                    if (big_endian) {
                            for (i = 0; i < bps; i++)
                                    *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
                    } else {
                            for (i = 0; i < bps; i++)
                                    *(samples[chn] + i) = (res >>  i * 8) & 0xff;
                    }
                    samples[chn] += steps[chn];
            }
            phase += step;
            if (phase >= max_phase)
                    phase -= max_phase;
    }
    *_phase = phase;

}


If I take out the sgn function from the code, it generates a sine wave. Any help would be greatly appreciated.
1
2
3
		    int a = sgn(amplitude_scale * sin(phase) * maxval);
                    fval.f = (float) a;
                    res = fval.i;


I don't know what you're trying to do with that union, but what you're doing there will give you complete garbage. Why aren't you just using 'a' directly like you do with 'b' right below it?



EDIT:

oh wait... I get it. You're taking the float's binary data and reinterpretting it as ints so you can bitshift it. Ugh.

In that case it would help to know some of the constants so we can narrow the problem down.


Is to_unsigned true?
what about is_float?
and big_endian?
Last edited on
Hi. Thanks for the reply.

is_float is false,
to_unsigned is false,
big_endian is false
Shouldn't the correct syntax be:
amplitude_scale * sgn(sin(phase)) * maxval;
I mean do you not want to amplify the square wave by amplitude_scale and maxval (because you do so with the sinusoid one)?

The sgn() function looks fine to me. The only problem which comes to my mind now is somewhere an integer gets overflown but I don't think that's the case. Just have in mind that signed int has a max value of 32768 on most of the systems.
Did you step through the code with the debugger and see what happens at each line of code?
What do you have when you expect a square wave? A null signal? A garbage signal?

I tried googling to see if sgn() could be a function name already used by some other library. Google has already found your question in 3 forums in the first 2 results pages :-P Try renaming it in case it would be the problem?
Please don't use case sensitive function names. Try being more expresive. Then you can read the code without having to chase down the function to see what it does. Maybe:

int return_sign_of_argument( double d );

More expressive names really pay off in the long run.

PS, the return value is not really a square wave. Its just a negative or positive one. It becomes a square wave or sine wave only when combined with is predecessors and successors in something like a chart or on an oscope. The function does not return a square wave, just a value.
Last edited on
Yes thanks for all your help guys.
The solution: amplitude_scale * sgn(sin(phase)) * maxval;
I originally placed the sgn function for the entire calculation instead of the sin() function only.
Topic archived. No new replies allowed.