A crude 1KHz sinewave generator using bash...

Code junkies hangout here

Moderators: ChrisThornett, LXF moderators

A crude 1KHz sinewave generator using bash...

Postby Bazza » Tue Mar 12, 2013 1:37 pm

A very simple crude sinewave generator.

The file required is generated inside the code, is linear interpolated and requires /dev/audio to work. Ensure you have this device, if not the download oss-compat from your OS's repository...

It lasts for about 8 seconds before exiting and saves a 65536 byte file to your working directory/drawer/folder as sinewave.raw. Use an oscilloscope to check the waveform generated...

This will be the test waveform soon for the AudioScope when the timebase of the scope is finished...

It is entirely Public Domain and you may do with it as you please...

Bazza, G0LCU...

Code: Select all
#!/bin/bash
#
# 1KHz.sh
#
# A very simple DEMO crude sinewave generator using the device /dev/audio.
# It is an eight second burst and generates an approximation of a pure sinewave using linear interpolation.
# The "sinewave.raw" file length is 65536 bytes in size...

# Zero the raw file...
> sinewave.raw

# This is the b byte data list for the crude sinewave.
data="\\x0f\\x2d\\x3f\\x2d\\x0f\\x03\\x00\\x03"

# Generate the file as an eight second burst...
for waveform in {0..8191}
do
        printf "$data" >> sinewave.raw
done

# Now play back a single run of the raw data for about eight seconds.
cat sinewave.raw > /dev/audio

# End of 1KHz.sh DEMO...
# Enjoy finding simple solutions to often very simple problems... ;o)
73...

Bazza, G0LCU...

Team AMIGA...
User avatar
Bazza
LXF regular
 
Posts: 1476
Joined: Sat Mar 21, 2009 11:16 am
Location: Loughborough

Postby IamPete » Thu Apr 03, 2014 11:53 am

Hi Bazza

Sorry to bring up such an old thread but just discovered your very handy bash routine above.
Just the thing I need to generate some tones in near real time.

A question I have is, what formula did you use to "synthesize" the 1KHz frequency?
I need to generate several different tones (same sampling frequency).

Any help greatly appreciated.
Pete.
IamPete
 
Posts: 14
Joined: Thu Apr 03, 2014 11:34 am

Postby Bazza » Thu Apr 03, 2014 8:42 pm

Scratch that one...
You might like this one instead, 8 second burst 1KHz pure
sinewave as a .WAV file that uses ALSA aplay command...

The one you have tried uses a logarithmic approach because it
uses /dev/audio, /dev/dsp is much better and this a much more
advanced variant.

It is still 8 bit depth, mono, unsigned integer at 8KHz sampling
and the the header can be used for literally ANY 65536 byte
RAW file you care to generate.

/dev/audio and /dev/dsp have linear interpolation, which is what
I prefer as external single or double pole filtering is simple to do.
OTOH the .WAV file generated is a very pure sinewave due to
DSP filtering.

Once run the sinewave.wav file can be saved anywhere and used
on other devices like my 'phone which is now my signal source
for AudioScope.sh

Try a triangular waveform using linear related HEX values in
place of the sine values for 8 points...

Be well aware of word wrapping, copy and paste errors, etc...

Enjoy...

EDIT:
This is the basis for an arbitrary LF Audio Function Generator
although the one I am starting to work on will sample at 48HKz,
stereo, 16 bit depth, etc...
Code: Select all
#!/bin/bash --posix
# 1HKz.sh
# Generate 1KHz pure sinewave as a .WAV file.
> /tmp/sinewave.wav
# 44 byte WAV header block that can be used for ANY 65536 byte RAW file...
printf "\x52\x49\x46\x46\x24\x00\x01\x00\x57\x41\x56\x45\x66\x6d\x74\x20\x10\x00\x00\x00\x01\x00\x01\x00\x40\x1f\x00\x00\x40\x1f\x00\x00\x01\x00\x08\x00\x64\x61\x74\x61\x00\x00\x01\x00" >> /tmp/sinewave.wav
# Add sinewave data to the header block, 8 bytes x 8192 times = 65536 bytes.
for n in {0..8191}
do
   printf "\x80\x26\x00\x26\x7F\xD9\xFF\xD9" >> /tmp/sinewave.wav
done
# Linux flavours using ALSA aplay...
aplay /tmp/sinewave.wav
# OSX 10.7.5 version using afplay...
#afplay /tmp/sinewave.wav
exit 0
73...

Bazza, G0LCU...

Team AMIGA...
User avatar
Bazza
LXF regular
 
Posts: 1476
Joined: Sat Mar 21, 2009 11:16 am
Location: Loughborough

Postby IamPete » Thu Apr 03, 2014 9:20 pm

Hi Bazza and thanks for the reply.

Since posting my original question, I have subsequently worked out how to modify the hex values of the look-up table to get other frequencies and also modified your script so as not to write to a file but rather a variable and then printf the variable to /dev/dsp.

The reasons I prefer not to use any audio players is that they require the audio to be played from a file (which I'm not doing) and also I need to play the tone for as long as a key is pressed thus it's easier to play from a variable than a fixed length file containing the audio samples.

For my application, super accurate or low distorion tones are not required so both /dev/audio and /dev/dsp will suffice.

If I need to save the samples as a wav, I can always append a header to a file then add the audio samples after it.

Thanks.
Pete.
IamPete
 
Posts: 14
Joined: Thu Apr 03, 2014 11:34 am

Postby Bazza » Thu Apr 03, 2014 10:16 pm

Hmmm, I missed the real time bit

I did this a couple of years ago in Python and you might find it interesting.
I was going to make a crude single tone _organ_ using a similar principle but I couldn't be bothered.

http://code.activestate.com/recipes/578 ... er-4177147
73...

Bazza, G0LCU...

Team AMIGA...
User avatar
Bazza
LXF regular
 
Posts: 1476
Joined: Sat Mar 21, 2009 11:16 am
Location: Loughborough

Postby IamPete » Thu Apr 03, 2014 10:51 pm

Thanks for that Bazza.
What would be nice is to add a small AM/SW transmitter and modulator to your morse script so that the tones can be heard on a nearby radio.
The youngsters should enjoy that.

Below a schematic for a simple low power transmitter that shouldn't break any laws.
One could decrease range by using an AC coupled dummy load (several Kilo-ohms).
Keep in mind that the self contained xtal osc has a TTL output.

Image
IamPete
 
Posts: 14
Joined: Thu Apr 03, 2014 11:34 am

Postby Bazza » Fri Apr 04, 2014 6:15 am

Hi IamPete...

Wonderful Stuff.
That has made my day.

I assume the Transformer is the Maplin LT700 one?

Get your shell script working and post on here. There are people
that are interested; me in particular...

Terrific.
Thanks matey...
73...

Bazza, G0LCU...

Team AMIGA...
User avatar
Bazza
LXF regular
 
Posts: 1476
Joined: Sat Mar 21, 2009 11:16 am
Location: Loughborough

Postby IamPete » Fri Apr 04, 2014 6:43 am

Hi Bazza

Glad you liked it.
The transformer type is not critical.
The headphone output of sound cards are pretty tolerant of loads in the 32 to 1000 ohm range so as long as the primary of the transformer has an impedance in this range you're fine.

For the secondary, the only real important thing is for it not to have too high a DC resistance else the volt drop to the osc will be too high.

You can use a LT700 if you have one lying about.
Small 100V line to 4/8 ohm transformers (as used on public address systems) would also work and so would a small mains transformer (mains side to computer, low voltage side to osc).
Mains transformers don't have a good frequency response but for this application, they're just fine.

For the osc module, they are widely available (loads of different freqs), cheap as chips and easy to solder to even with no PCB.

As for the script, I will post as soon as it's done and tested.

Pete.
IamPete
 
Posts: 14
Joined: Thu Apr 03, 2014 11:34 am

Postby Bazza » Fri Apr 04, 2014 7:53 am

Terrific stuff. Yeah, I know about transformers...

Looking forwards to the code...

BTW, I guess you probably already know that the bash 'read' builtin can be used something akin to INKEY$ in BASIC...

CYA and great stuff...
73...

Bazza, G0LCU...

Team AMIGA...
User avatar
Bazza
LXF regular
 
Posts: 1476
Joined: Sat Mar 21, 2009 11:16 am
Location: Loughborough

Postby IamPete » Fri Apr 04, 2014 6:37 pm

Hi Bazza

Herewith preliminary script and test circuit.
Note that both a still work in progress.

The application is to be able to control four small DC motors (the type often used in toys) using no special ports (such as LPT, Serial or USB) thus eliminating all the complexities associated with the code required to access these ports.
This leaves only the audio output.
What the script does is monitor the "w", "a", "s" and "z" keys and whilst any one is pressed, it outputs a corresponding tone.
The circuit uses four tone decoders to receive these tones and convert them to a high or low to drive the motors.
The "t" key terminates the script.

Note that the tones are a bit rough still and there are "clicks" whilst looping.
The circuit also still needs some refinement.

The script:

Code: Select all
#!/bin/bash

if [ true ] ; then

# ================================================
# Populate variables
# ================================================

GoUp="w"
GoDn="z"
GoRt="s"
GoLt="a"
Terminate="t"

Table1K="\x0f\x2d\x3f\x2d\x0f\x03\x00\x03"
Table2K="\x8d\x9e\x61\x71\x71\x61\x9e\x8d"
Table3K="\x0f\x2d\x00\x2d\x0f\x03\x3f\x03"
Table4K="\x0b\x0f\x2d\x00\x2d\x0f\x03\x3f\x03\x0b"

# ================================================
# Generate the 4 lookup tables to give aprox
# 1/2 sec tones.
# Note that the tones have "clicks" when looping
# this is due to the sample values not being
# optimized, however since the audio output will
# be driving PLL tone decoders (1 per tone), these
# "clicks" are not a problem.
# Needs dedicated hardware interface.
# ================================================

for waveform in {0..511}
do
Data1K=$Data1K$Table1K
Data2K=$Data2K$Table2K
Data3K=$Data3K$Table3K
Data4K=$Data4K$Table4K
done


# ================================================
# Main loop
# ================================================

while : ; do

# ================================================
# detect keypress
# ================================================

read -sn1 keypress

# =================================================
# check which key pressed
# =================================================

if [ "$keypress" = "$GoUp" ]
   then
      printf "$Data1K" > /dev/audio &   
fi


if [ "$keypress" = "$GoDn" ]
   then
      printf "$Data2K" > /dev/audio &
fi


if [ "$keypress" = "$GoRt" ]
   then
      printf "$Data3K" > /dev/audio &   
fi


if [ "$keypress" = "$GoLt" ]
   then
      printf "$Data4K" > /dev/audio &   
fi


if [ "$keypress" = "$Terminate" ]
   then
      echo "Terminated"
      exit 0   
fi

done
echo ""
exit 0

fi 2>/dev/null


The schematic:

Image[/code]
IamPete
 
Posts: 14
Joined: Thu Apr 03, 2014 11:34 am

Postby Bazza » Fri Apr 04, 2014 8:12 pm

Hi IamPete...

Sticking rigidly to round numbers and using /dev/dsp which is MUCH easier to code for, you can get frequency accuracies much better than 0.1%.

/dev/dsp has a linear amplitude relationship as opposed to /dev/audio which is logarithmic...

Having said that because /dev/audio AND /dev/dsp are linear interpolation then the highly frequency accurate pseudo-square waves generated are actually quite good.

Using 16 bytes per frequency, pseudo-code:-
Code: Select all
500Hz == "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
1000Hz == "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"
2000Hz == "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF"
4000Hz == "\x00\xFF\x00\xFF\x00\xFF\x00\xFF\x00\xFF\x00\xFF\x00\xFF\x00\xFF"

All the same length and frequency accurate...
73...

Bazza, G0LCU...

Team AMIGA...
User avatar
Bazza
LXF regular
 
Posts: 1476
Joined: Sat Mar 21, 2009 11:16 am
Location: Loughborough

Postby IamPete » Fri Apr 04, 2014 8:24 pm

Brilliant, thanks Bazza.
The xFF is a bit loud so changed them to xAF.
IamPete
 
Posts: 14
Joined: Thu Apr 03, 2014 11:34 am

Postby Bazza » Fri Apr 04, 2014 8:37 pm

Just one point.
Sound cards are usually output inverted, i.e. \x00 is maximum output \xFF is minimum output. With a sinewave or squarewave this is immaterial but if you want, say, a pulse then be aware.

/dev/dsp and /dev/audio are 8 bit unsigned integer so the DSP __zero_offset__ point is \x7F or \x80 allowing for any bit error in the DSP device...

Here is looking forwards to more of this stuff...

Thanks for making my day, I have enjoyed it...
73...

Bazza, G0LCU...

Team AMIGA...
User avatar
Bazza
LXF regular
 
Posts: 1476
Joined: Sat Mar 21, 2009 11:16 am
Location: Loughborough

Postby IamPete » Fri Apr 04, 2014 8:48 pm

Glad you enjoying this as much as I am, after all this is exactly what makes Linux so nice, having the freedom to experiment without the constraints imposed by a corporation that decides what we should be able to do and how.

It's only a pity one can't change the sample rate and number of bits for /dev/dsp or /dev/audio without resorting to IOCTL, which then necessitates using something like C which would need compiling and have a bunch of lib dependencies which would no doubt cause a few problems across different OS' like Linux and Mac.
Assuming of course that all /dev/dsp and audio can actually support higher resolutions.
IamPete
 
Posts: 14
Joined: Thu Apr 03, 2014 11:34 am

Postby Bazza » Fri Apr 04, 2014 9:44 pm

Hi IamPete...

There is really only one phrase for /dev/dsp and /dev/audio:-
"Lowest common denominator"...

However it is very easy to _read_ from, and _write_ to, them.

Back to listening watch... ;o)
73...

Bazza, G0LCU...

Team AMIGA...
User avatar
Bazza
LXF regular
 
Posts: 1476
Joined: Sat Mar 21, 2009 11:16 am
Location: Loughborough

Next

Return to Programming

Who is online

Users browsing this forum: No registered users and 2 guests