Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon.

Pages: 1-

PCM tone generator

Name: Anonymous 2011-10-30 12:52

Hey /prog/

I'm trying to teach myself how to generate PCM audio in C, and I can't figure out why this isn't generating a clear sine wave signal when I pipe it to aplay.  Any ideas?


#include <math.h>

#define MIDDLE_C 261.626
#define HZ 8000.0
#define ONE_CYCLE 2.0
#define PI 3.14159265

int main() {

    float frames = HZ / MIDDLE_C;
    float i;
    float n;
    unsigned int x;
    int s, v;

    for(v=0; v<MIDDLE_C; v++) {
        for(i=0; i<frames; i++) {
            n = (1 + sin((i / frames) * (PI * 2))) / 2;
            x = (int)round(0xFF * n);
            putchar(x);
        }
    }

    return 0;

}

Name: Anonymous 2011-10-30 14:58

What the fuckkkkkk, when I run this it outputs a sine wave in the console, but when I change it to output x raw it's a buzzing noise



#include <stdio.h>
#include <math.h>

#define MIDDLE_C 261.626
#define HZ 8000.0
#define ONE_CYCLE 2.0
#define PI 3.14159265

int main() {

        float frames = HZ / MIDDLE_C;
        float i;
        float n;
        unsigned int x;
        int s, v;

        for(v=0; v<MIDDLE_C; v++) {
                for(i=0; i<frames; i++) {
                        n = (1 + sin((i / frames) * (PI * 2))) / 2;
                        x = (int)round(0xFF * n);

                        for(s=0; s<x / 5; s++) putchar('*');
                        putchar('\n');      
                }
        }

        return 0;
}

Name: FrozedVoid 2011-10-30 15:17

>>2
I can't seem to pull the tampon out of my ass.

Name: Anonymous 2011-10-30 15:27

>>3
Next time, FV, follow the directions an LUBE that giant glass butt plug first, yeah?

Name: Anonymous 2011-10-30 15:35

>>1,2

Try this:

#define MIDDLE_C 261.626
#define HZ 8000.0
#define ONE_CYCLE 2.0
#define PI 3.14159265
 
int main() {
 
    float frames = HZ / MIDDLE_C;
    int i;
    float n;
    signed char x;
    int v;
 
    for(v=0; v<MIDDLE_C; v++) {
        for(i=0; i<frames; i++) {
            n = sin((i / frames) * (PI * 2));
            x = (signed char)round(0x7F * n);
            putchar(x);
        }
    }
 
    return 0;
 
}


The problem is you're using unsigned values (0x80 being neutral) for output, while the device expects signed chars (0x00 being neutral). Compare the waveforms:

Signed:

  1    _--_
      /    \
     /      \
  0-|        |        |-
              \      /
               \    /
  1             -__-

Unsigned:

  1          .        .
             |\      /|
             | \_  _/ |
  0-|  _--_  |   --   |-
    | /    \ |
    |/      \|
  1 '        '


The jumps in the unsigned output make it sound more like a buzzing noise.

Name: Anonymous 2011-10-30 15:43

now make FM synthesizer

Name: Anonymous 2011-10-30 16:00

>>5
Thanks for the thoughts, but it actually expects unsigned chars.  This is working as intended as far as I can tell:



#include <stdio.h>
#include <math.h>

#define A4 440
#define HZ 8000.0
#define ONE_CYCLE 2.0
#define PI 3.14159265

float frequency(int note);
void play(int note, float duration);

int main() {

    play(3, 0.1);
    play(2, 0.1);
    play(1, 0.1);
    play(2, 0.1);
    play(3, 0.5);
   
    return 0;

}

float frequency(int note) {
    return pow(2, note / 12.0) * A4;
}

void play(int note, float duration) {
    int samples = (int)round(duration * HZ);
    float freq = frequency(note);

    float period = HZ / freq;

    int v, x, s;
    float n, i;
    for(v=0; v<samples / period; v++) {
        for(i=0; i<period; i++) {
            n = (1 + sin((i / period) * (PI * 2))) / 2;
            x = (int)round(0xFF * n);

            putchar(x);
        }
    }
}

Name: >>5 2011-10-30 16:30

>>7
Are you >>1 or are you just testing his code and concluding it works as intended?

Name: Anonymous 2011-10-30 16:34

>>8
I'm OP, >>2, and >>7

Name: Anonymous 2011-10-30 16:42

OP Here

The code I posted in >>7 works, but seems to go way out of tune.  As far as I can tell this should play Mary Had a Little Lamb but it sounds like shit.


    play(0, 0.3, 1); // ma
    play(-2, 0.3, 1); // ry
    play(-4, 0.3, 1); // had
    play(-2, 0.3, 1); // a
    play(0, 0.3, 1); // lit
    play(0, 0.02, 0);
    play(0, 0.3, 1); // tle
    play(0, 0.02, 0);
    play(0, 0.4, 1); // lamb
    play(0, 0.2, 0);
    play(-2, 0.3, 1); // lit
    play(0, 0.02, 0);
    play(-2, 0.3, 1); // tle
    play(0, 0.02, 0);
    play(-2, 0.4, 1); // lamb
    play(0, 0.2, 0);
    play(0, 0.3, 1);    // lit
    play(3, 0.3, 1);  // tle
    play(0, 0.02, 0);
    play(3, 0.4, 1);  // lamb
    play(0, 0.2, 0);
    play(0, 0.3, 1); // ma
    play(-2, 0.3, 1); // ry
    play(-4, 0.3, 1); // had
    play(-2, 0.3, 1); // a
    play(0, 0.3, 1); // lit
    play(0, 0.02, 0);
    play(0, 0.3, 1); // tle
    play(0, 0.02, 0);
    play(0, 0.3, 1); // lamb
    play(0, 0.02, 0);
    play(0, 0.3, 1); // its
    play(-2, 0.3, 1); // fleece
    play(0, 0.02, 0);
    play(-2, 0.3, 1); // was
    play(0, 0.3, 1); // white
    play(-2, 0.3, 1); // as
    play(-4, 0.6, 1); // snow

Name: Anonymous 2011-10-30 17:15

>>10

have you checked for floating point round off errors if the input to sin gets too large? You can try keeping the angle in the interval 0[, 2.0*pie] when you increment it.

Name: Anonymous 2011-10-30 17:29

>>11
That's why I nested 2 loops (well... that and trying to keep it simple enough for me to understand), so what I pass into sin() never gets larger than one period of the frequency, which in this case is like 400-500HZ

Name: Anonymous 2011-10-30 17:29

Name: Anonymous 2011-10-30 17:39

>>13
Fucking awesome, thanks for sharing.

Name: Anonymous 2011-10-30 17:46

>>10
Those loops are effectively truncating your period to an integer.

Name: Anonymous 2011-10-30 17:48

>>13
Holy shit.

Name: Anonymous 2011-10-30 18:00

>>15
If I dump the values, it's clearly giving me a clean sine wave between 0x00 and 0xFF

Name: Anonymous 2011-10-30 18:08

>>5
How do you figure unsigned looks like the following..

Unsigned:

  1          .        .
             |\      /|
             | \_  _/ |
  0-|  _--_  |   --   |-
    | /    \ |
    |/      \|
  1 '        '

Name: Anonymous 2011-10-30 18:14

>>17
Your inner loop does one period. Your inner loop goes in integer steps. Ergo your period will always be an integer number of samples. You truncate the last part of your wave to fit.

Name: Anonymous 2011-10-30 18:17

>>19
integer / float == float.  I promise you if you comment the putchar and printf("%f\n", n); it'll print floating point numbers between 0 and 1

Name: Anonymous 2011-10-30 18:30

>>20
I guess I'm not getting through to you. Enjoy your off-key music.

Name: Anonymous 2011-10-30 18:31

>>19
Sorry, the light just went on.  One period doesn't fit into n.0 samples.  I have to rewrite that loop.

Thanks!

Name: Anonymous 2011-10-30 18:38

>>21
>>19
Fuck, I'm an idiot.  Thank you for pointing out how.  My song stays in tune (to my tin ear anyway) now.

Here's the code which sucks less, for the crowds of people out there chomping at the bit to get an 8 bit 8KHz version of Mary Had a Little Lamb:


#include <math.h>

#define A4 440
#define HZ 8000.0
#define ONE_CYCLE 2.0
#define PI 3.14159265

float frequency(int note);
void play(int note, float duration, float amplitude);

int main() {

    float amplitude = 0.5;

    play(0, 0.3, amplitude); // ma
    play(-2, 0.3, amplitude); // ry
    play(-4, 0.3, amplitude); // had
    play(-2, 0.3, amplitude); // a
    play(0, 0.3, amplitude); // lit
    play(0, 0.02, 0);
    play(0, 0.3, amplitude); // tle
    play(0, 0.02, 0);
    play(0, 0.4, amplitude); // lamb
    play(0, 0.2, 0);
    play(-2, 0.3, amplitude); // lit
    play(0, 0.02, 0);
    play(-2, 0.3, amplitude); // tle
    play(0, 0.02, 0);
    play(-2, 0.4, amplitude); // lamb
    play(0, 0.2, 0);
    play(0, 0.3, amplitude);    // lit
    play(3, 0.3, amplitude);  // tle
    play(0, 0.02, 0);
    play(3, 0.4, amplitude);  // lamb
    play(0, 0.2, 0);
    play(0, 0.3, amplitude); // ma
    play(-2, 0.3, amplitude); // ry
    play(-4, 0.3, amplitude); // had
    play(-2, 0.3, amplitude); // a
    play(0, 0.3, amplitude); // lit
    play(0, 0.02, 0);
    play(0, 0.3, amplitude); // tle
    play(0, 0.02, 0);
    play(0, 0.3, amplitude); // lamb
    play(0, 0.02, 0);
    play(0, 0.3, amplitude); // its
    play(-2, 0.3, amplitude); // fleece
    play(0, 0.02, 0);
    play(-2, 0.3, amplitude); // was
    play(0, 0.3, amplitude); // white
    play(-2, 0.3, amplitude); // as
    play(-4, 0.6, amplitude); // snow

    return 0;

}

float frequency(int note) {
    return pow(2.0, note / 12.0) * A4;
}

void play(int note, float duration, float amplitude) {
    int samples = (int)round(duration * HZ);
    float freq = frequency(note);

    float period = HZ / freq;

    int v, x, s;
    float n, i;
    for(v=0; v<samples; v++) {
            n = (1 + sin((v / period) * (PI * 2))) / 2;
            x = (int)round(0xFF * n * amplitude);
            putchar(x);
    }
}

Name: >>5 2011-10-30 18:41

>>9
Weird. What did you change to make the audio output work right?

>>18
My original thought (which seemed not to be the case in the end) was that the audio device expected signed chars in the PCM data. So the signed, correct sine wave looks like:

00 ++++++++++++++++ 00 -------------- 00
00 01 ... 7f ... 01 00 ff .. 80 .. ff 00

If however, assuming it's unsigned, you send these bytes:

80 81 ... ff ... 81 80 7f .. 00 .. 7f 80

The wave will start too low, then underflow from 80 to 7f and back.

Name: Anonymous 2011-10-30 18:46

>>24
Raw 8 bit PCM is unsigned bytes from 0x00 to 0xFF (dur), with 0x80 being 0

http://en.wikipedia.org/wiki/Pulse-code_modulation#Modulation

Name: Anonymous 2011-10-30 18:53

>>24
You know we had a thread on FM Synthesis a while back which you might like to read. Check out this post in particular: http://dis.4chan.org/read/prog/1260419313/41

Name: Anonymous 2011-10-30 18:59

>>26
That's awesome, and exactly the type of thing I was working my understanding towards.  Thanks huge.

Name: Anonymous 2011-10-30 19:15

>>27
Good to hear. If you have any questions about the material there I can probably answer them.

Name: Anonymous 2011-10-30 19:32

I wonder how people made music before we got computers. Did they have to calculate the sine waves by hand, or was there some kind of mechanical machine to do it for them?

Name: Anonymous 2011-10-30 19:36

>>28
That thread just blew my fucking mind and led me to this:

http://emusician.com/news/emusic_square_one_fm/

Which is a good background on what FM actually is.  Thanks again.

Name: Anonymous 2011-10-30 20:15

I'm sorry, but I always wanted to learn how to do this shit, but never found reading-friendly info anywhere.

How comes putchar() is being able to output sound?

Name: Anonymous 2011-10-30 20:20

>>31
Because it's being piped into the audio device, or to some other program that is sending it there.

I've always worked with the device directly through alsa or something, and putchar is not what you'd want to use in that case.

Name: Anonymous 2011-10-30 20:21

>>31

Opchan is piping the output of the program to aplay, which is a linux program that takes in binary input and sends it to the speaker. If you are using a linux, you can try typing

cat /dev/urandom | aplay

and you'll get loud white noise.

You would need to use a library, or operate on the device driver yourself in a system dependent manner to use the speakers. I've only used ALSA myself.

Name: Atomic_Bios 2011-10-31 7:18

music(x) = Enchanted songs of divine lyrics that produce sounds.

Name: Anonymous 2011-10-31 7:59

>>33
You can also use OSS and redirect the output to /dev/sound or /dev/dsp (e.g. ./pcm > /dev/sound).

Name: Anonymous 2011-10-31 8:08


Don't change these.
Name: Email:
Entire Thread Thread List