Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Generation of sound

57 views
Skip to first unread message

Uwe

unread,
Dec 12, 2009, 7:47:02 PM12/12/09
to
Hi

I'd like to write a program for learning Morse code in C/C++/Nasm
under (Debian/GNU) Linux.

What I need is a function that generates a sinus-wave like sound (of a
specific volume, pitch and duration) and a function that causes a
pause (of a specific duration). As far as I know there are basically
two ways to do this: With the console speaker or the sound card. Does
anybody have a solution for one or even better both cases?

Thanks in advance.

Uwe

Mark Hobley

unread,
Dec 13, 2009, 4:08:07 AM12/13/09
to
Uwe <gurg...@gmx.de> wrote:

> What I need is a function that generates a sinus-wave like sound (of a
> specific volume, pitch and duration)

There is a package called beep that does this.

http://www.johnath.com/beep/

Mark.

--
Mark Hobley
Linux User: #370818 http://markhobley.yi.org/

Jasen Betts

unread,
Dec 13, 2009, 4:51:52 AM12/13/09
to

man console_codes
(under the title "Linux Console Private CSI Sequences")
(^G to sound the bell, nanosleep to wait or play a tone of frequency 0Hz )

for soundcard audio there's /dev/audio which is low-fi 8-bit mono at 8000Hz
sample rate. probably all you need. pick a frequency that's a factor of
8000 (eg 400Hz 500Hz, 800Hz or 1000hz and compute a sine wave table.)

for a simple sound card test I do

cat /dev/urandom > /dev/audio

Tauno Voipio

unread,
Dec 13, 2009, 6:57:27 AM12/13/09
to


Have you Googled 'linux morse'?

There were nearly half a million hits, and at least the
first ones seemed pretty good.

Get the source code and have a look.

If you *have* to use assembler in Linux, please use 'as'
(the GNU assembler).

--

Tauno Voipio, OH2UG
tauno voipio (at) iki fi

John Hasler

unread,
Dec 13, 2009, 8:10:04 AM12/13/09
to
Uwe writes:
> I'd like to write a program for learning Morse code in C/C++/Nasm
> under (Debian/GNU) Linux.

thumper/~ apt-cache search morse
aldo - Morse code training program
bsdgames - collection of classic textual unix games
codegroup - Convert any file, including binary, into 5 letter code
cwdaemon - morse daemon for the parallel or serial port
cwirc - X-Chat morse plugin
libacme-bleach-perl - Perl module for really clean programs
qrq - high speed morse trainer, similar to DL4MM's Rufz
tlf - Console mode general purpose CW keyer, logging- and contest program
cw - Command-line frontend to unixcw
cwcp - Ncurses frontend to unixcw
unixcw-dev - Development files for Morse programs
unixcw - Shared library for Morse programs
xcwcp - Qt frontend to unixcw
xdemorse - GTK+ Morse Code Decoding Software

--
John Hasler
jha...@newsguy.com
Dancing Horse Hill
Elmwood, WI USA

Jasen Betts

unread,
Dec 13, 2009, 4:56:18 AM12/13/09
to
On 2009-12-13, Uwe <gurg...@gmx.de> wrote:
> Hi
>
> I'd like to write a program for learning Morse code in C/C++/Nasm
> under (Debian/GNU) Linux.

this is for your education right? because such programs already
exists, GPL source is available. (eg: "morse")

look in the "hamradio" section

Uwe

unread,
Dec 14, 2009, 9:35:12 PM12/14/09
to
It's me that wants to use the program. And I know there are already
lots of Morse tutor programs. But there are 2 reasons I want to write
one for myself anyway. First it's a challenge and I want to know how
the program works that I use. And second that's the only way I can get
a program that perfectely fits my needs.

I tested cw a while ago and it worked at least with the sound card. I
studied the source code but the function that I thought would generate
the sound called another function which called another function and
yet another function but after about 10 levels down I wasn't sure
whether I lost track and gave up. Yesterday I tested it again but the
Morse code output was wrong: The letters didn't match the
corresponding Morse codes.


I tested 'cat /dev/urandom > /dev/audio': It sounded like white noise.

I wrote a program whose output is a sine wave table:
(At least what I thought Jasen Betts meant by this)

...
for ( i = 0 ; i < (int)( 8000.0 * mult ) ; i++ )
{
printf( " %f" , freq2 * sin( 2.0 * PI * freq1 / 8000.0 * (double)
i ) );
}
...

I executed: ./sine-wave <param1> <param2> <param3> > /dev/audio
It generates a sine-wave like sound.

I testet several parameter combinations and found out that the sound
changes it's frequency sometimes but I couldn't figure out why. mult
determines the duration of the sound. But freq1 and freq2 seem to have
no effect. It looks like I don't completely understand how what's
written to /dev/audio defines its sound output.

How can I change the pitch and the volume?


And about the console speaker:

I found out that with some Linux versions sound in the text console
and terminal emulators seems to be completely disabled at least when I
test it within the bash with 'printf "\a"' or with a C program that
contains 'printf("\a")'. I added 'set bell-style audible' to /etc/
inputrc, added 'setterm -bfreq 400 -blength 500' to ~/.bashrc, /etc/
bash.bashrc, ~/.profile and /etc/profile and executed 'xset b 50 400
500' under X . But no beep at all! Otherwise I would be glad because
normally I hate those system beeps but not in this case.

Is there a way to enable/disable sound in general in the text console
and terminal emulators?
Does the console speaker only can generate sound through 'printf
"\a"'?


Would be great if somebody could either answer my questions or can
give me a link where I can find the information I need.

Regards,
Uwe

Bernhard Agthe

unread,
Dec 15, 2009, 3:11:37 AM12/15/09
to
Hi,

Uwe wrote:
> I tested 'cat /dev/urandom > /dev/audio': It sounded like white noise.

It's supposed to be.

> I wrote a program whose output is a sine wave table:
> (At least what I thought Jasen Betts meant by this)
> ...

> I executed: ./sine-wave <param1> <param2> <param3> > /dev/audio
> It generates a sine-wave like sound.

Good, you're almost there.

> I testet several parameter combinations and found out that the sound
> changes it's frequency sometimes but I couldn't figure out why. mult
> determines the duration of the sound. But freq1 and freq2 seem to have
> no effect. It looks like I don't completely understand how what's
> written to /dev/audio defines its sound output.

Well, the frequency isn't set in your program - basically you feed the
output into /dev/audio as fast as the program generates the table - you
need to do some timing. For example a short delay between outputting
samples?

But then you'll get intermittent sound because the program may not wake
up in time - producing a small break.

If I were you, I'd record a sound for the dot and the line into *.ogg
files. Load them upon your program's startup and play them as needed.
That's the technique I used for a talking calculator - just play
pre-recorded samples (numbers in my case). Use the libSDL for the
purpose, it has a sound section. Check out <http://www.libsdl.org/>

> How can I change the pitch and the volume?

Volume should be simple, just turn the button ;-) It should be in the
libSDL, really.

> And about the console speaker:

You'll find that some terminals don't have a console speaker, you'll
actually find the console speaker emulated via sound card... Forget
about changing your console settings, but rather look into your terminal
application's configuration menu, you'll find options for "console
beeb", including flashing the screen instead of beep. But really, forget
about using it for the morse program, rather enjoy having a real sound
card. The times of listening to music on the PC speaker are long gone,
thankfully...

Ciao...

Alan Curry

unread,
Dec 15, 2009, 4:09:27 AM12/15/09
to
In article <dc9d8fcc-eaa9-4a03...@e27g2000yqd.googlegroups.com>,

Uwe <gurg...@gmx.de> wrote:
>I wrote a program whose output is a sine wave table:
>(At least what I thought Jasen Betts meant by this)
>
>...
>for ( i = 0 ; i < (int)( 8000.0 * mult ) ; i++ )
>{
> printf( " %f" , freq2 * sin( 2.0 * PI * freq1 / 8000.0 * (double)
>i ) );
>}
>...
>
>I executed: ./sine-wave <param1> <param2> <param3> > /dev/audio
>It generates a sine-wave like sound.

You printed a bunch of values out in human-readable (decimal ASCII) format
and fed it to the audio device. Well, that's not going to work very well.

>
>I testet several parameter combinations and found out that the sound
>changes it's frequency sometimes but I couldn't figure out why. mult
>determines the duration of the sound. But freq1 and freq2 seem to have
>no effect. It looks like I don't completely understand how what's
>written to /dev/audio defines its sound output.

For /dev/audio, the default data format is 8-bit ulaw, mono, 8000Hz. ulaw is
an extra complication you won't want to deal with right now.

You can change the format with an ioctl, but none of the available formats
are ASCII, so printf is not going to be involved in generating them. To keep
the first example as simple as possible, it would be nice to avoid ioctl, so
I suggest using /dev/dsp instead. It's the same as /dev/audio, but with a
different default mode that doesn't use ulaw.

So let's prepare some data for /dev/dsp, in its default mode, which is
8000Hz 8-bit mono. Your formula for calculating the numbers is mostly
correct, but you need to fit them to the proper range and format them as
8-bit unsigned bytes. That means they need to be in the range of 0 to 255,
and if you use that entire range you'll be getting the maximum loudness
(at the current mixer settings of course).

Rename your freq2 to "amplitude" and then instead of
printf("%f", freq2*sin(...))
do it like this:
unsigned char sample = 128 + amplitude * sin(...);
putchar(sample);

And make sure the amplitude is 127 or less, so that all the values of
128 + amplitude * sin(something)
will be in the range of a single 8-bit unsigned value.

Here's a working example:

$ cat tone.c
#include <stdio.h>
#include <math.h>

#define AMPLITUDE 127
#define FREQUENCY 220
#define SAMPLERATE 8000
#define PI 3.141592653589793
#define DURATION 10

int main(void)
{
int i;
for(i=0; i<DURATION*SAMPLERATE; ++i) {
unsigned char x;
x = 128 + AMPLITUDE * sin((double)i/SAMPLERATE * FREQUENCY * (2*PI));
putchar(x);
}
return 0;
}
$ gcc -Wall tone.c -lm -o tone
$ ./tone > /dev/dsp

--
Alan Curry

Jasen Betts

unread,
Dec 15, 2009, 4:50:18 AM12/15/09
to
On 2009-12-15, Uwe <gurg...@gmx.de> wrote:
> I tested cw a while ago and it worked at least with the sound card. I
> studied the source code but the function that I thought would generate
> the sound called another function which called another function and
> yet another function but after about 10 levels down I wasn't sure
> whether I lost track and gave up. Yesterday I tested it again but the
> Morse code output was wrong: The letters didn't match the
> corresponding Morse codes.
>
>
> I tested 'cat /dev/urandom > /dev/audio': It sounded like white noise.
>
> I wrote a program whose output is a sine wave table:
> (At least what I thought Jasen Betts meant by this)
>
> ...
> for ( i = 0 ; i < (int)( 8000.0 * mult ) ; i++ )
> {
> printf( " %f" , freq2 * sin( 2.0 * PI * freq1 / 8000.0 * (double)
> i ) );
> }
> ...

try this: (it wasn't as simple as I expected)
it builds a table of 800 sampls (100ms) and writes it repeatedly to
/dev/audio.

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

const int cBias = 0x84;
const int cClip = 32635;

static char MuLawCompressTable[256] =
{
0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};

unsigned char LinearToMuLawSample(short sample)
{
int sign = (sample >> 8) & 0x80;
if (sign)
sample = (short)-sample;
if (sample > cClip)
sample = cClip;
sample = (short)(sample + cBias);
int exponent = (int)MuLawCompressTable[(sample>>7) & 0xFF];
int mantissa = (sample >> (exponent+3)) & 0x0F;
int compressedByte = ~ (sign | (exponent << 4) | mantissa);

return (unsigned char)compressedByte;
}


int main( int argc, char **argv)
{
int f,d;
float a;
unsigned char buf[800];
FILE *audio;

if ( argc <2 || sscanf(argv[1],"%d",&f) < 1 )
{
fprintf(stderr,"Usage: %s <freq(Hz)> [duratiion(ms)] [amplitude]\n",argv[0]);
return 1;
}

audio=fopen("/dev/audio","wb");
if(!audio)
{
perror("can't open /dev/audio");
return 1;
}

{
int i;
d=1000;
a=1;
if(argc>2)sscanf(argv[2],"%d",&d);
if(argc>3)sscanf(argv[3],"%f",&a);
f/=10;
for(i=0;i<800;++i)
{
// int c=
buf[i]=LinearToMuLawSample(0x7fff*a*sin(0.00125*i*M_PI*f));
// printf("%4d ",c);
}
// printf("\nF=%d D=%d\n",f,d);
}

d<<=3;
while (d>=800)
{
fwrite(buf,1,800,audio);
d-=800;
}
if(d>0) fwrite(buf,1,d,audio);
fclose(audio);
return 0;

Florian Rehnisch

unread,
Dec 16, 2009, 9:08:55 AM12/16/09
to
o Uwe <gurg...@gmx.de>:

> Does the console speaker only can generate sound through 'printf
> "\a"'?

beep uses the speaker.
--
flori
Vim-Hilfe auf Deutsch http://www.florianrehnisch.de/vimhelp/

Jan Panteltje

unread,
Dec 16, 2009, 10:13:12 AM12/16/09
to
On a sunny day (Wed, 16 Dec 2009 14:08:55 +0000 (UTC)) it happened
eix...@gmx.de (Florian Rehnisch) wrote in
<2009-12-15$21.0...@x.vimhelp.de.vu>:

/* test39.c */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>


/* beep 'duration' microseconds with frequency 'frequency' */
int beep(int duration, int freq)
{
int scale;

if(freq <= 0) return 0;
if(duration <= 0) return 0;

scale = 1193046 / freq;
outb(0xb6, 0x43);
outb(scale & 0xff, 0x42);
outb(scale >> 8, 0x42);
outb(inb(0x61) | 3, 0x61); /* set bit 0 and 1, timer 2 gate and lsp gate */
usleep(duration);
outb(inb(0x61) & ~3, 0x61); /* reset bit 0 and 1, timer 2 gate and lsp gate*/

return 1;
}


int main(int argc, char **argv)
{
if(argc != 3)
{
fprintf(stderr, "Usage: beep microseconds frequency\n");

exit(1);
}

iopl(3);

if(! beep( atoi(argv[1]), atoi(argv[2]) ) ) exit(1);

exit(0);
}


grml: ~ # gcc -Wall -o test39 test39.c

grml: ~ # ./test39
Usage: beep microseconds frequency
grml: ~ # ./test39 100000 1000
grml: ~ # ./test39 1000000 1000
grml: ~ # ./test39 10000000 1000

Uwe

unread,
Dec 17, 2009, 9:08:10 PM12/17/09
to
Lots of good ideas!

Bernhard Agthe wrote:
> If I were you, I'd record a sound for the dot and the line into *.ogg
> files. Load them upon your program's startup and play them as needed.
> That's the technique I used for a talking calculator - just play
> pre-recorded samples (numbers in my case). Use the libSDL for the
> purpose, it has a sound section. Check out <http://www.libsdl.org/>

The site looks interesting and I like its cross-platform approach.

Apart from that I'd like to keep it as simple as possible. So I'm
still interested in a solution for the console speaker. The "terminal
application's configuration menu" you're talking about: I know what
you mean but neither the konsole nor the xterm emulators that run on
the Sidux Live-DVD (based on Debian) I'm actually using have such a
menu. And of course it's the same with the text consoles.

Alan Curry wrote:
> So let's prepare some data for /dev/dsp, in its default mode, which is
> 8000Hz 8-bit mono. Your formula for calculating the numbers is mostly
> correct, but you need to fit them to the proper range and format them as
> 8-bit unsigned bytes. That means they need to be in the range of 0 to 255,
> and if you use that entire range you'll be getting the maximum loudness
> (at the current mixer settings of course).

I see. The definitions of frequency and amplitude of a sine wave are
basically the same as in physics. Only the range is 0 <= y <= 255
instead of -amplitude <= y <= amplitude and I have to redirect the
binary data instead of ASCII code.

I adjusted my program and it works! At least on my notebook. On my
computer each beep is preceeded by another short beep. (I think that's
why the output of the cw program I tested wasn't correct.) Do you know
a remedy for that?

And thanks for keeping it simple.

Jasen Betts wrote:
> try this: (it wasn't as simple as I expected)
> it builds a table of 800 sampls (100ms) and writes it repeatedly to
> /dev/audio.

I'm afraid that's to difficult for me at the moment. I understand the
syntax of your program but especially the LinearToMuLawSample function
is beyond my head.

But the "audio=fopen("/dev/audio","wb");" line in your program gave me
the idea to alter the program based on Alan Curry. I added:

FILE *stream;
stream = fopen("/dev/dsp","wb");
fputc( <output of the sine function>, stream);
close(stream);

No need for a bash redirection anymore.

Florian Rehnisch wrote:
> beep uses the speaker.

I suppose you mean that program already Mark Hobley posted a link to.
First I thought it just uses "printf("\a")" but that's not true.

It uses:
ioctl(console_fd, KIOCSOUND, (int)(CLOCK_TICK_RATE/parms.freq));
ioctl(console_fd, KIOCSOUND, 0);
to turn sound on or off respectively.

I'm going to test this next time.

I googled KIOCSOUND and found an interesting link:
http://www.netzmafia.de/skripten/hardware/speaker/simplesound.html (On
German)

@ Jan Panteltje:

I wrote a program that copied the main parts of what you've posted
(without printing error messages). Tests on the text console showed
there's only a short clicking sound at the beginning at one at the end
of what should be the beginning and the end of the sound.

Do you have any idea how to correct this?
And could you explain the line "outb(0xb6, 0x43);" to me? What's the
meaning of "0xb6"?

@ Everybody:

Do you have any recommendations on books for further reading?
What do you think about the O'Reilly Books "Linux System Programming"
or "Understanding the Linux Kernel"?

Thanks a lot for your detailed replies.

Uwe

Alan Curry

unread,
Dec 17, 2009, 10:35:19 PM12/17/09
to
In article <f4d41a74-3d1b-447b...@9g2000yqa.googlegroups.com>,

Uwe <gurg...@gmx.de> wrote:
>
>Apart from that I'd like to keep it as simple as possible. So I'm
>still interested in a solution for the console speaker. The "terminal
>application's configuration menu" you're talking about: I know what
>you mean but neither the konsole nor the xterm emulators that run on
>the Sidux Live-DVD (based on Debian) I'm actually using have such a
>menu. And of course it's the same with the text consoles.

Hold down the Ctrl key, move the mouse pointer into the xterm window, and
press one of the mouse buttons. Then try the other buttons!

Menubars are just a waste of screen space don't you know

--
Alan Curry

Jan Panteltje

unread,
Dec 18, 2009, 7:56:56 AM12/18/09
to
On a sunny day (Thu, 17 Dec 2009 18:08:10 -0800 (PST)) it happened Uwe
<gurg...@gmx.de> wrote in
<f4d41a74-3d1b-447b...@9g2000yqa.googlegroups.com>:

>@ Jan Panteltje:
>
>I wrote a program that copied the main parts of what you've posted
>(without printing error messages). Tests on the text console showed
>there's only a short clicking sound at the beginning at one at the end
>of what should be the beginning and the end of the sound.

Well, then you copied something wrong, or are not using an IBM compatible PC :-)


>Do you have any idea how to correct this?

Try running the original,
sudo ....


>And could you explain the line "outb(0xb6, 0x43);" to me? What's the
>meaning of "0xb6"?

Those numbers starting with '0x' are hexadecimal values.
That instruction sets the hardware counter, a 8253, to channel 2, mode 3.

You need to look up the PC's hardware, the chips datasheets for the function of those specific registers:
http://www.osdever.net/documents/8253.pdf

If you want to go via the kernel, and not directly addres I/O, you should use ioctls.

ASM version:
http://www.assembly.happycodings.com/code58.html

Uwe

unread,
Dec 18, 2009, 6:03:15 PM12/18/09
to
Alan Curry wrote:
> Hold down the Ctrl key, move the mouse pointer into the xterm window, and
> press one of the mouse buttons. Then try the other buttons!

I get 3 different option menus (by pressing the right, left and both
buttons). Couldn't enable the audible bell only the visual bell. But
at this point I have better alternatives for the generation of sound.
Nevertheless it's always interesting to discover previously hidden
features.

Jan Panteltje wrote:
> Well, then you copied something wrong, or are not using an IBM compatible PC :-)

Instead of 'outb(0xb6, 0x43);' I wrote 'outb(0x6b, 0x43);'. Normally
I'm not prone to typing incorrecty so I haven't thought of that as a
cause.

I tried to find the error and found a reply on a forum where I asked a
similar question a while ago:

mov al, 10111110b
out 43h, al
(in Nasm)

which is equivalent to

outb(0xbe, 0x43);
(in C)

So I tried this and it beeped. But Bit3 differs compared with your
value (0xb6 == 10110110b). I understand this now because of your
http://www.osdever.net/documents/8253.pdf link: There's an 'x' in the
M2 (=Bit3) field of Mode 3. (I suppose 'x' means it doesn't matter if
0 or 1.) The 8253.pdf is the information I looked for.

While testing I found a behavior of the programs I don't understand:
First I ran the incorrect program (with 0x6b): No beep.
Then I ran the correct program (with 0xb6): Beep.
Then I ran the INcorrect program again: BEEP!
(It's the same after restarting the computer.)

Do you know why?
Is there an effect of 'outb(0xb6, 0x43);' within the timer that can't
be changed by another outb?

A question about the value 1193046 in your program:
I found slightly different values e.g.
on http://www.netzmafia.de/skripten/hardware/speaker/simplesound.html
it equals to 1190000 and
on http://www.johnath.com/beep/beep.c it's 1193180 and
in 8253.pdf it reads 1.19 MHz (which either means 1190000 or somewhere
between 1185000 and 1194999).

The difference is only about 0.3 %. Compared to this the difference
between C and C# is about 6 %, so 0.3 % is probably hard to hear.
After all mainly the control of duration should be precise in a Morse
tutor program not the control of frequency. But do you know the exact
value?

Uwe

Jan Panteltje

unread,
Dec 18, 2009, 7:05:10 PM12/18/09
to
On a sunny day (Fri, 18 Dec 2009 15:03:15 -0800 (PST)) it happened Uwe
<gurg...@gmx.de> wrote in
<aa127704-f509-4d7b...@26g2000yqo.googlegroups.com>:

>value (0xb6 == 10110110b). I understand this now because of your
>http://www.osdever.net/documents/8253.pdf link: There's an 'x' in the
>M2 (=Bit3) field of Mode 3. (I suppose 'x' means it doesn't matter if
>0 or 1.) The 8253.pdf is the information I looked for.

Yes 'x' mean 'do not care'.

>While testing I found a behavior of the programs I don't understand:
>First I ran the incorrect program (with 0x6b): No beep.
>Then I ran the correct program (with 0xb6): Beep.
>Then I ran the INcorrect program again: BEEP!
>(It's the same after restarting the computer.)
>
>Do you know why?

restarting, do you mean a cold start?
When power is off, the registers lose their value, and when power
comes on the chip will go to any defaults.

If you just warm-booted, say 'restart', then the registers should keep their values.
Lets see
0x6b = 0110 1011 counter 1 read/load MSB mode 5 BCD

0xb6 = 1011 0110 counter 2 LSB, then LSB mode 3 Binary

So the wrong instruction next time 'up' will go to counter 1,
an not affect the previous correctly initialised counter 2.


>Is there an effect of 'outb(0xb6, 0x43);' within the timer that can't
>be changed by another outb?

I think I just answered that.


>A question about the value 1193046 in your program:
>I found slightly different values e.g.
>on http://www.netzmafia.de/skripten/hardware/speaker/simplesound.html
>it equals to 1190000 and
>on http://www.johnath.com/beep/beep.c it's 1193180 and
>in 8253.pdf it reads 1.19 MHz (which either means 1190000 or somewhere
>between 1185000 and 1194999).

It has something to do with the clock:
http://blogs.msdn.com/oldnewthing/archive/2004/12/02/273721.aspx
You can change the value to 1193180 if you like, I dunno where I got my value from,
that code is very old, IIRC from some DOS program...


>The difference is only about 0.3 %. Compared to this the difference
>between C and C# is about 6 %, so 0.3 % is probably hard to hear.
>After all mainly the control of duration should be precise in a Morse
>tutor program not the control of frequency. But do you know the exact
>value?

See the above link, and why.

I am not sure how exact that value is, in a modern PC for sure I would not expect a separate crystal,
but somehow the frequency to be created by some divider in the chipset from the main clock, but I am not sure.

I think you could google yourself for questions like this :-)

Uwe

unread,
Jan 10, 2010, 2:18:01 PM1/10/10
to
Jan Panteltje wrote:
> 0x6b = 0110 1011 counter 1 read/load MSB mode 5 BCD
>
> 0xb6 = 1011 0110 counter 2 LSB, then LSB mode 3 Binary
>
> So the wrong instruction next time 'up' will go to counter 1,
> an not affect the previous correctly initialised counter 2.

The mistake was that I thought "outb(0xb6, 0x43);" would assign 0xb6
to port 0x43. But passing a value to 0x43 means programming one of 3
counters depending on Bit 7 and 6 of that value.

----------------------------------------

During the last days I did many tests on two different computers and
each with different Linux versions. I mainly used 3 programs based on
the ideas, links and listings of you all. Every program works for at
least one computer/Linux combination (so I suppose the programs are
correct) but unfortunately none of them works for all combinations.
There are combinations in which all programs run and there's one in
which none of the programs work.

Program A:

Direct access of ports 0x42, 0x43 and 0x61 with outb() and inb():
(Mainly the program of Jan Panteltje)

#include <stdlib.h>
#include <sys/io.h>

int main( int argc , char **argv )
{
if ( argc != 3 )
exit(1);

int freq = atoi( argv[1] );
int dura = atoi( argv[2] );

if ( freq <= 0 || dura <= 0 )
exit(2);

iopl( 3 );

int scale = 1193046 / freq;

outb( 0xb6 , 0x43 );
outb( scale & 0xff , 0x42 );
outb( scale >> 8 , 0x42 );
outb( inb( 0x61 ) | 3 , 0x61 );

usleep( dura );
outb( inb( 0x61 ) & ~3 , 0x61 );

exit(0);
}

Test results:
Program compiled with gcc.
Command and parameters (at least): # ./<program-name> 440 1000000
(440 Hz and 1000000 micro-secs)
(Exception: More convenient values when speaker was too loud)

(X means it worked, O means it didn't work, P means it partially
worked, - means not tested)
(First character: text console, second character: KDE/Xfce with
'konsole'/'xterm' command)

................ | Notebook | Computer |
-----------------+----------+----------+
Knoppix Live-DVD |....XX....|....XX....|
-----------------+----------+----------+
Debian installed |....O-....|....--....|
-----------------+----------+----------+
Kanotix Live-CD..|....OO....|....XX....|
-----------------+----------+----------+
Sidux Live-DVD.. |....OO....|....XX....|
-----------------+----------+----------+
Xubuntu Live-CD..|....OO....|....XX....|
-----------------+----------+----------+
Mandriva Live-CD |....X-....|....XX....|
-----------------+----------+----------+

It's difficult with the notebook: Only Knoppix and Mandriva work. But
the sound with Mandriva is significantly louder in comparison with
Knoppix! That means Linux can change the volume of the console speaker
on the notebook and probably it can turn it off completely! And this
could be the reason why it doesn't work with the other Linux versions.

Can I change the volume of the console speaker on a notebook
(manually)? There's an "fn"-key and keys with loudspeaker symbols on
them but they don't have any effect. Does a notebook have two
different loadspeakers like an ordinary computer have an internal and
external speakers or does a notebook just have one?

The program runs within Linux. Could Linux therefore block port access
and could I prevent it from doing so?

Does anybody have an idea how to fix this?

----------------------------------------

Program B:

With ioctl() and KIOCSOUND:
(Based on the links http://www.johnath.com/beep and
http://www.netzmafia.de/skripten/hardware/speaker/simplesound.html)

#include <fcntl.h>
#include <stdlib.h>
#include <linux/kd.h>

int main(int argc, char **argv)
{

int freq, dura, fd, ticks;

if (argc == 3)
{
freq = atoi(argv[1]);
dura = atoi(argv[2]);
}
else
exit(1);

if (freq <= 0 || dura <= 0)
exit(2);

fd = open("/dev/console", O_WRONLY);
ticks = (int)(1190000.0 / (double)freq);

ioctl(fd, KIOCSOUND, ticks);
usleep(dura);
ioctl(fd, KIOCSOUND, 0);

close(fd);
}

Test results:
Program compiled with gcc.
Command and parameters (at least): # ./<program-name> 440 1000000
(440 Hz and 1000000 micro-secs)
(Exception: More convenient values when speaker was too loud)

(X means it worked, O means it didn't work, P means it partially
worked, - means not tested)
(First character: text console, second character: KDE/Xfce with
'konsole'/'xterm' command)

................ | Notebook | Computer |
-----------------+----------+----------+
Knoppix Live-DVD |....OO....|....OO....|
-----------------+----------+----------+
Debian installed |....O-....|....--....|
-----------------+----------+----------+
Kanotix Live-CD..|....OO....|....XX....|
-----------------+----------+----------+
Sidux Live-DVD.. |....OO....|....OO....|
-----------------+----------+----------+
Xubuntu Live-CD..|....OO....|....XX....|
-----------------+----------+----------+
Mandriva Live-CD |....X-....|....XX....|
-----------------+----------+----------+

I thought it depends on whether the kernel supports ioctl() with
KIOCSOUND. But the same kernels of Kanotix and Xubuntu work only on
the computer! (I didn't check what the ioctl() function returns.) Is
there a way to change this behaviour (without having to recompile the
kernel)?

In a forum I read there's a pc speaker module I could unload with
'modprobe -r pcspkr'. But 'modprobe -l pcspkr' lists this module.

ioctl() with KIOCSOUND plays the sound on the console speaker so does
it perform in the end essentially the same as program A (accessing
port 0x42, 0x43, 0x61)?

----------------------------------------

Program C:

With sin() and /dev/dsp:
(Based on the messages of Alan Curry and Jasen Betts)

#include <stdio.h>
#include <stdlib.h>
// #include <math.h>
#include <cmath>

#define PI 3.141592654
#define SAMPLERATE 8000.0

int main( int argc , char **argv )
{
int i, wave;
double vol, freq, sec;
FILE *fp;
char *dev;

if ( argc == 5 )
{
vol = atof( argv[1] );
freq = atof( argv[2] );
sec = atof( argv[3] );
dev = argv[4] ;
}
else
exit(1);

fp = fopen( dev , "wb" );

for ( i = 0 ; i < (int)( SAMPLERATE * sec ) ; i++ )
{
wave = 128 + (int)( vol / 100.0 * 127 * sin( 2.0 * PI * freq /
SAMPLERATE * (double)i ) );
fputc( wave , fp );
}

fclose( fp );
}

Test results:
Program compiled with g++.
Command and parameters (at least): # ./<program-name> 80 440 1 /dev/
dsp (80% of the volume maximum, 440 Hz, 1 sec and /dev/dsp)
(Exception: More convenient values when speaker was too loud)

(X means it worked, O means it didn't work, P means it partially
worked, - means not tested)
(First character: text console, second character: KDE/Xfce with
'konsole'/'xterm' command)

................ | Notebook | Computer |
-----------------+----------+----------+
Knoppix Live-DVD |....XX....|....OO....|
-----------------+----------+----------+
Debian installed |....X-....|....--....|
-----------------+----------+----------+
Kanotix Live-CD..|....XX....|....OO....|
-----------------+----------+----------+
Sidux Live-DVD.. |....OO....|....PP....|
-----------------+----------+----------+
Xubuntu Live-CD..|....XX....|....PP....|
-----------------+----------+----------+
Mandriva Live-CD |....X-....|....PO....|
-----------------+----------+----------+

In some cases there was no sound (partly) because /dev/dsp was
missing. I created /dev/dsp with 'mknod /dev/dsp c 14 3'.

Computer: "OO" entries:
No sound plus error messages:
"modprobe: FATAL: Error running install command for sound_slot_0" and
"Segmentation fault"
Is there a module missing or any other problem?

Computer: "PP" entries:
There are beeps but each beep that is longer than 1 sec and have a
higher pitch than 880 Hz is clearly preceeded by another short beep.

On the computer there's either no sound at all or there's this short
preceeding beep. Could this be a hardware problem or is there a chance
for me to fix it?

Notebook/Kanotix and Notebook/Mandriva:
With the same volume parameter (80%) the sound was very loud and
couldn't be changed with the volume control keys on the keyboard (fn-
key + keys with loudspeaker symbols). That's a minor problem because
the program can control the volume but can I change the maximum volume
output of /dev/dsp?

Jan Panteltje

unread,
Jan 10, 2010, 3:30:55 PM1/10/10
to
On a sunny day (Sun, 10 Jan 2010 11:18:01 -0800 (PST)) it happened Uwe
<gurg...@gmx.de> wrote in
<bc86b5c0-4fd6-47a8...@l30g2000yqb.googlegroups.com>:

Are you shure you did run it as root?
You need to be root when addrsion I/O directly.

Uwe

unread,
Jan 10, 2010, 5:13:03 PM1/10/10
to
Jan Panteltje wrote:
> Are you shure you did run it as root?
> You need to be root when addrsion I/O directly.

I did all tests as root.

Jan Panteltje

unread,
Jan 10, 2010, 5:54:32 PM1/10/10
to
On a sunny day (Sun, 10 Jan 2010 14:13:03 -0800 (PST)) it happened Uwe
<gurg...@gmx.de> wrote in
<148d2b70-0619-401e...@p8g2000yqb.googlegroups.com>:

There is an other issue, this soft I wrote is for the classic IBM PC hardware ONLY.
Some laptops may not be compatible with that as they use propriety hardware.
This is why Linux or the OS is important, to shield your applications from the hardware.
So my soft is not for all notebooks.
I think the way to go if you want *every* Linux computer to work, is via /dev/dsp,
or sometimes /dev/dsp0 (/dev/dsp may be a link to /dev/dsp0), or the alsa hw:X,Y or plughw:X,Y devices.
/dev/dspX is for OSS sound system, older systems have that, you then perhaps need the modules to
interface to the alsa sound system, I think those are called snd_pcm_oss, and snd_mixer_oss.

Indeed as others have pointed out, you can make a little sound file and do something like:
play sine-1000.wav
'play' is a command line front end to 'sox'.
sox should really be on your system.
You can download a 1000 Hz sine wave file from my site:
ftp://panteltje.com/pub/sine-1000.wav

Uwe

unread,
Jan 11, 2010, 12:44:14 AM1/11/10
to
Jan Panteltje wrote:
> There is an other issue, this soft I wrote is for the classic IBM PC hardware ONLY.
> Some laptops may not be compatible with that as they use propriety hardware.
> This is why Linux or the OS is important, to shield your applications from the hardware.
> So my soft is not for all notebooks.
> I think the way to go if you want *every* Linux computer to work, is via /dev/dsp,
> or sometimes /dev/dsp0 (/dev/dsp may be a link to /dev/dsp0), or the alsa hw:X,Y or plughw:X,Y devices.
> /dev/dspX is for OSS sound system, older systems have that, you then perhaps need the modules to
> interface to the alsa sound system, I think those are called snd_pcm_oss, and snd_mixer_oss.
>
> Indeed as others have pointed out, you can make a little sound file and do something like:
> play sine-1000.wav
> 'play' is a command line front end to 'sox'.
> sox should really be on your system.
> You can download a 1000 Hz sine wave file from my site:
> ftp://panteltje.com/pub/sine-1000.wav

I don't understand why you think it's a hardware issue. Isn't the fact
that program A runs on the notebook (with Knoppix and Mandriva) a
proof that the program is compatible with the hardware? And when I
change the Linux version and the program stops working don't you think
this Linux version (of course in combination with the hardware) is the
reason for that?

My notebook is a HP pavilion ze4400 and I could email the HP support.
As far as I understand all I need is a "Programmable Interval
Timer" (PIT) 8253/8254 chip at port 0x43 (with Counter 2 at port0x42),
a "Peripheral Port Interface" (PPI) 8255 chip at port 0x61, a "Clock
Generator" 8284 chip (with 1.19 MHz) and a speaker that are connected
in the right way (like described in http://www.osdever.net/documents/8253.pdf
or http://sites.google.com/site/yaoxuchen/Home/musicbox). Is this
right? If yes I could just ask the HP support whether my notebook have
the necessary prerequisites.

Thanks for your suggestions anyway but it's not necessarily my goal to
have a program that runs on every Linux computer. I prefer simple
solutions I can understand like the 3 programs I tested. But I don't
understand how a wav-file works. And I took a glance at the OSS home
page and it seemed like a rather complex approach. On the other hand a
universal and cross-platform solution is a good thing but it's not my
intention at the moment.

Jan Panteltje

unread,
Jan 11, 2010, 8:19:46 AM1/11/10
to
On a sunny day (Sun, 10 Jan 2010 21:44:14 -0800 (PST)) it happened Uwe
<gurg...@gmx.de> wrote in
<834d24c0-8416-44d7...@35g2000yqa.googlegroups.com>:

Well, wave files are simple, you can look that up on the net.
It is just samples of the audio in a file, with a header that gives info
about the sample frequency, how many bits per sample, mono-stereo, etc.

I was thinking that in a notebook the speaker can be connected to
the soundcard output, and not to the timer that my software solution uses.
If you are *lucky* the timer may appear as a 'PC speaker' in the mixer.
So, did it, and if so did you have that volume slider in the mixer open?
Try typing
alsamixer
in a terminal on the notebook, to see what inputs are available.

Uwe

unread,
Jan 11, 2010, 11:53:37 AM1/11/10
to
Jan Panteltje wrote:
> Well, wave files are simple, you can look that up on the net.
> It is just samples of the audio in a file, with a header that gives info
> about the sample frequency, how many bits per sample, mono-stereo, etc.
>
> I was thinking that in a notebook the speaker can be connected to
> the soundcard output, and not to the timer that my software solution uses.
> If you are *lucky* the timer may appear as a 'PC speaker' in the mixer.
> So, did it, and if so did you have that volume slider in the mixer open?
> Try typing
> alsamixer
> in a terminal on the notebook, to see what inputs are available.

I just found the solution by myself and signed in in order to send a
message.

First I tried the headphones with no result. Then I found KMenu ->
Multimedia -> Volume Control (command: gnome-volume-control) and there
it was: There's a "PC Speaker" option. The volume control has no
effect but I can enable the PC Speaker and that works!

The alsamixer program has the same "PC Speaker" option (it's just
ncurses). So thanks for your message.

And I'm going to google wave files :-)

Jan Panteltje

unread,
Jan 11, 2010, 12:16:51 PM1/11/10
to
On a sunny day (Mon, 11 Jan 2010 08:53:37 -0800 (PST)) it happened Uwe
<gurg...@gmx.de> wrote in
<5a6d34be-6f94-4603...@r5g2000yqb.googlegroups.com>:

#ifndef _WAVE_HEADER_H_
#define _WAVE_HEADER_H_

typedef struct
{ /* header for WAV-Files */
uint8_t main_chunk[4]; /* 'RIFF' */
uint32_t length; /* length of file */
uint8_t chunk_type[4]; /* 'WAVE' */
uint8_t sub_chunk[4]; /* 'fmt' */
uint32_t length_chunk; /* length sub_chunk, always 16 bytes */
uint16_t format; /* always 1 = PCM-Code */
uint16_t modus; /* 1 = Mono, 2 = Stereo */
uint32_t sample_fq; /* Sample Freq */
uint32_t byte_p_sec; /* Data per sec */
uint16_t byte_p_spl; /* bytes per sample, 1=8 bit, 2=16 bit (mono) 2=8 bit, 4=16 bit (stereo) */
uint16_t bit_p_spl; /* bits per sample, 8, 12, 16 */
uint8_t data_chunk[4]; /* 'data' */
uint32_t data_length; /* length of data */
} wave_header;

#endif /* _WAVE_HEADER_H_ */

You could have a look at the source my audio processor, the one I use:
http://panteltje.com/panteltje/xpequ/index.html
It has a nice wave form display, equalizer, AGC, output for LED lights,
and a command line only option.

0 new messages