Linux Format forums Forum Index Linux Format forums
Help, discussion, magazine feedback and more
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

A crude 1KHz sinewave generator using bash...
Goto page 1, 2  Next
 
Post new topic   Reply to topic    Linux Format forums Forum Index -> Programming
View previous topic :: View next topic  
Author Message
Bazza
LXF regular


Joined: Sat Mar 21, 2009 11:16 am
Posts: 1474
Location: Loughborough

PostPosted: Tue Mar 12, 2013 1:37 pm    Post subject: A crude 1KHz sinewave generator using bash... Reply with quote

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:
#!/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...
Back to top
View user's profile Send private message
IamPete



Joined: Thu Apr 03, 2014 12:34 pm
Posts: 14

PostPosted: Thu Apr 03, 2014 12:53 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
Bazza
LXF regular


Joined: Sat Mar 21, 2009 11:16 am
Posts: 1474
Location: Loughborough

PostPosted: Thu Apr 03, 2014 9:42 pm    Post subject: Reply with quote

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:
#!/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...
Back to top
View user's profile Send private message
IamPete



Joined: Thu Apr 03, 2014 12:34 pm
Posts: 14

PostPosted: Thu Apr 03, 2014 10:20 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
Bazza
LXF regular


Joined: Sat Mar 21, 2009 11:16 am
Posts: 1474
Location: Loughborough

PostPosted: Thu Apr 03, 2014 11:16 pm    Post subject: Reply with quote

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/578010-simple-morse-code-practice-oscillator/?in=user-4177147
_________________
73...

Bazza, G0LCU...

Team AMIGA...
Back to top
View user's profile Send private message
IamPete



Joined: Thu Apr 03, 2014 12:34 pm
Posts: 14

PostPosted: Thu Apr 03, 2014 11:51 pm    Post subject: Reply with quote

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.

Back to top
View user's profile Send private message
Bazza
LXF regular


Joined: Sat Mar 21, 2009 11:16 am
Posts: 1474
Location: Loughborough

PostPosted: Fri Apr 04, 2014 7:15 am    Post subject: Reply with quote

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...
Back to top
View user's profile Send private message
IamPete



Joined: Thu Apr 03, 2014 12:34 pm
Posts: 14

PostPosted: Fri Apr 04, 2014 7:43 am    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
Bazza
LXF regular


Joined: Sat Mar 21, 2009 11:16 am
Posts: 1474
Location: Loughborough

PostPosted: Fri Apr 04, 2014 8:53 am    Post subject: Reply with quote

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...
Back to top
View user's profile Send private message
IamPete



Joined: Thu Apr 03, 2014 12:34 pm
Posts: 14

PostPosted: Fri Apr 04, 2014 7:37 pm    Post subject: Reply with quote

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:

#!/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:

[/code]
Back to top
View user's profile Send private message
Bazza
LXF regular


Joined: Sat Mar 21, 2009 11:16 am
Posts: 1474
Location: Loughborough

PostPosted: Fri Apr 04, 2014 9:12 pm    Post subject: Reply with quote

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:

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...
Back to top
View user's profile Send private message
IamPete



Joined: Thu Apr 03, 2014 12:34 pm
Posts: 14

PostPosted: Fri Apr 04, 2014 9:24 pm    Post subject: Reply with quote

Brilliant, thanks Bazza.
The xFF is a bit loud so changed them to xAF.
Back to top
View user's profile Send private message
Bazza
LXF regular


Joined: Sat Mar 21, 2009 11:16 am
Posts: 1474
Location: Loughborough

PostPosted: Fri Apr 04, 2014 9:37 pm    Post subject: Reply with quote

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...
Back to top
View user's profile Send private message
IamPete



Joined: Thu Apr 03, 2014 12:34 pm
Posts: 14

PostPosted: Fri Apr 04, 2014 9:48 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
Bazza
LXF regular


Joined: Sat Mar 21, 2009 11:16 am
Posts: 1474
Location: Loughborough

PostPosted: Fri Apr 04, 2014 10:44 pm    Post subject: Reply with quote

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...
Back to top
View user's profile Send private message
View previous topic :: View next topic  
Display posts from previous:   
Post new topic   Reply to topic    Linux Format forums Forum Index -> Programming All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Linux Format forums topic RSS feed 


Powered by phpBB © 2001, 2005 phpBB Group


Copyright 2011 Future Publishing, all rights reserved.


Web hosting by UKFast