Pages: 1 [2] 3 4   Go Down
Author Topic: toneAC v1.2 - Twice the volume, higher quality, higher frequency, etc.  (Read 14548 times)
0 Members and 1 Guest are viewing this topic.
Toledo, OH
Offline Offline
God Member
*****
Karma: 36
Posts: 510
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The example is okay with the noToneAC() to reset the registers.
Try to remove the noToneAC(), that results in gaps in the sound.
Perhaps the counter has to roll-over ?

I heard something like this once, but couldn't duplicate it so I figured I just had a loose connection.

You're exactly correct on your diagnosis.  The counter is "over the top" so it needs to wrap before resuming the sound.  This isn't very long when using prescaler 1.  But, at 122 Hz and below, prescaler 256 is used which is 256 times longer than prescaler 1 and the reason for the long silence.

Since even more problems can happen when we play notes on each side of the prescaler cutoff frequency, I tweaked your sketch to create a torture test:

Code:
#include <toneAC.h>

void setup() {}

void loop() {
  int f = random(50, 124);
  toneAC(f, 10, 0, true);
}

Since it includes frequencies above 122Hz, it sometimes also uses prescaler 1.  Also, there's no delay so it just hammers the hell of of things.  This sketch should produce a static-like noise as it plays random notes as fast as it can.  Instead, it's a bunch of random clicks with periods of silence as it frequently goes "over the top".

After some testing, I've isolated the problem and have a fix.  In the toneAC.cpp file after the line that reads "ICR1   = top;" add the following line:

Code:
 if (TCNT1 > top) TCNT1 = top;         // Counter over the top, not good, put within range.

With this, the above sketch now produces what you'd expect, static.  During early development, I set TCNT1 = 0 when starting a tone.  That works fine for high frequencies, but poorly at lower frequencies and when changing notes (creates clicks).  It's even worse when driving a two-pin dual LED at ultra low frequencies (like at 2 Hz).  Adding the noToneAC() in your sketch works because when TCCR1A resets the PWM it automatically sets the counter to zero.  While this works, it's not ideal for the above reasons of setting the counter to zero can cause other problems.

In any case, the above line fixes things and I've added it to the development version which will be released in version 1.2.

On a side note, the volume setting doesn't really work well for low frequencies.  It doesn't do much to change the volume and lowers the quality (makes it buzzy).  I'd suggest to always use 10 for the volume, which keeps the quality high.  Only use a lower volume if it's a requirement.

Anyway, thanks for the catch!  And leading me in the right direction with the fix.

Tim
« Last Edit: January 18, 2013, 12:47:09 am by teckel » Logged

Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

Offline Offline
Edison Member
*
Karma: 9
Posts: 1016
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

First of all, I love the library. It will be my first choice in most applications.
I hope to test the Uno/Mega/Leonardo/ATmega8 with version 1.2, after that it should be ready.
That single line of code makes some of my tests sound a lot better.

I have made a checklist for version 1.2, perhaps there are one to two things that make sense.

(1) The code increased a little (just a little) which is always a problem for the ATmega8
library + example version with 1.0, Arduino Uno : 1516
library + example version with 1.1, Arduino Uno : 1670
library + example version with 1.0, ATmega8 : 1336
library + example version with 1.1, ATmega8 : 1472

(2) The ISR uses millis(), so a roll-over of the millis is possible. The length is no longer fail-safe, so using the delay() function with noToneAC() is preferrable for critical situations.

(3) You don't use the 'L' for long values. I don't mind about it in the sketch, but you could use it in your library. I prefer to use 0L, 256L, - 1L and so on, when long integers are involved.

(4) The examples have the extension *.pde. I would prefer *.ino

(5) In the example you use '/* .... */' for comment, but everwhere else '//'.

(6) In the example, you use a potmeter. But for a small test with the library, a very quick result (showcase) would be needed. And a potmeter will make it unnecessary complicated.

(7) You made 10 volume steps, which is great. But for a 'ping' sound, a smooth decrease of the volume is needed. Or am I asking too much for the library ? Are there open source examples of tunes with volume ?

( 8 ) Why is the sound different for this example ?
Code:
int i, freq, vol;

for (i = 0; i<5; i++)

  freq = 900;
  for (vol=10; vol>0; vol--)
    toneAC( freq , vol, 60, false);
  freq = 540;
  for (vol=10; vol>0; vol--)
    toneAC( freq, vol, 60, false);
  delay( 3000);
}

for (i = 0; i<5; i++)

  freq = 900;
  for (vol=10; vol>0; vol--)
  {
    toneAC( freq , vol, 0, true);
    delay(60);
  }
  freq = 540;
  for (vol=10; vol>0; vol--)
  {
    toneAC( freq, vol, 0, true);
    delay(60);
  }
  noToneAC();
  delay( 3000);
}
« Last Edit: January 18, 2013, 03:04:48 am by Krodal » Logged

Toledo, OH
Offline Offline
God Member
*****
Karma: 36
Posts: 510
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

(1) The code increased a little (just a little) which is always a problem for the ATmega8
Great pains have been taken to keep the code as small as possible.  I'm a huge believer in running as much as possible in the background so around 150 additional bytes I believe are important.  It's still much smaller than the tone library.  I show the Arduino Uno code to be 1604 bytes with Arduino v1.0.2 not 1670.  And with the ATmega8 I have it as 1398 instead of 1472 (again v1.0.2).  As a test I created a toneACmini() function that only has a frequency and duration (automatically runs in the background).  With toneACmini() instead of toneAC() it went down to 1488 bytes on the Uno.  But, not sure if 116 bytes is worth confusing people.

(2) The ISR uses millis(), so a roll-over of the millis is possible. The length is no longer fail-safe, so using the delay() function with noToneAC() is preferrable for critical situations.

It uses unsigned long int, so it's about a 50 day range.  That should be good for almost any situation.  The rollover of micros is critical, but ms hardly ever happens in the real world.  Although, I am working on a project right now that I'm trying to get it to run virtually forever.

(3) You don't use the 'L' for long values. I don't mind about it in the sketch, but you could use it in your library. I prefer to use 0L, 256L, - 1L and so on, when long integers are involved.

If there's an advantage I'm not aware of let me know.  Most of my longs are unsigned longs.  Not sure if that makes a difference.

(4) The examples have the extension *.pde. I would prefer *.ino

They need to be .ino for a library so old versions of Arduino can find them.  I'm running only v1.0.2 so everything is a .pde.  I change them to .ino just for the library.  Other library creators told me to use .ino

(5) In the example you use '/* .... */' for comment, but everwhere else '//'.

I hardly ever use '/* .... */'.  I use it in that particular case to try and identify that you can do a bunch of stuff there, not just one line.

(6) In the example, you use a potmeter. But for a small test with the library, a very quick result (showcase) would be needed. And a potmeter will make it unnecessary complicated.

The library is designed for sound, not LEDs.  The sample is "toneAC_demo" which doesn't use a pot.  The one you're talking about is an advanced one that I don't mention anywhere as it's only for more advanced users that may want to use toneAC for other purposes.

(7) You made 10 volume steps, which is great. But for a 'ping' sound, a smooth decrease of the volume is needed. Or am I asking too much for the library ? Are there open source examples of tunes with volume ?

You're asking too much.  I got out my SPL meter and it was HARD to get even 10 volume levels.  A square wave just doesn't work well for creating a true volume level.  I had to cheat to get even 10, there was really only 9 but that would just be confusing.

( 8 ) Why is the sound different for this example ?

They're very close.  But, the first loop is inferior because it uses the foreground method and therefore there's a short period of time where there's no sound, producing a non-constant sound and clicking.  This way is designed for a laymen who who isn't too comfortable with programming but they can still generate sound using this method and would probably be totally happy with that.  The second way is a more proper way that a higher level programmer would use, knowing that the first way would have a very slight but still noticeable gap between notes reducing quality.  I would only ever use the second loop, as I did with the original tone() library as well.

The idea that something runs in the background is very foreign to many novice programmers.  They love delay statements, or a command that just does something for as long as they specify and then returns to them when it's done.  So, toneAC makes it easy for the masses, but still powerful for those who better understand an interrupt/timer driven paradigm.

Tim
« Last Edit: January 18, 2013, 04:38:02 am by teckel » Logged

Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

Offline Offline
Edison Member
*
Karma: 9
Posts: 1016
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

My sketch size was different, because I used my own example.

I like the mini function. Perhaps it will not be used often, it's nice to have as an extra.

Using 'L' or 'UL' for long numbers is something I do for myself, so that I know what I'm doing.

I agree with everything else. Perhaps that ping-pong sound (the second smooth one) can be used as an example ? I'm trying to make more sounds that show what the volume can do.

A page in the playground section would be nice. I can make one, with links to your code and this thread.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
But, not sure if 116 bytes is worth confusing people.
think not, but by the time their sketches fill up all memory and the last 116 bytes are critical I assume they aren't confused any more by a stripped lib smiley-wink
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Toledo, OH
Offline Offline
God Member
*****
Karma: 36
Posts: 510
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
But, not sure if 116 bytes is worth confusing people.
think not, but by the time their sketches fill up all memory and the last 116 bytes are critical I assume they aren't confused any more by a stripped lib smiley-wink

If I only knew how to set a define in a sketch so the library knew about it I'd be done by now.

Tim
Logged

Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

Offline Offline
Edison Member
*
Karma: 9
Posts: 1016
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Don't bother about the mini, it's not that important.

Example 1 : alert (soft and loud)
Code:
  // soft and loud alert.
  // For the toneAC library.
  // public domain
  int i, j;

  // Soft alert
  for( i=0; i<10; i++)
  {
    for( j=0; j<3; j++)
    {
      toneAC( 720, 3, 0, true);
      delay( 50);
      toneAC( 610, 3, 0, true);
      delay( 50);
      noToneAC();
      delay( 30);
    }
    delay( 500);
  }

  delay( 2000);
 
  // Loud alert
  for( i=0; i<10; i++)
  {
    for( j=0; j<3; j++)
    {
      toneAC( 720, 10, 0, true);
      delay( 50);
      toneAC( 610, 10, 0, true);
      delay( 50);
      noToneAC();
      delay( 30);
    }
    delay( 500);
  }

  delay( 2000);
« Last Edit: January 18, 2013, 06:54:47 pm by Krodal » Logged

Toledo, OH
Offline Offline
God Member
*****
Karma: 36
Posts: 510
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Don't bother about the mini, it's not that important.

Example 1 : alert (soft and loud)
Code:
  // soft and loud alert.
  // For the toneAC library.
  // public domain
  int i, j;

  // Soft alert
  for( i=0; i<10; i++)
  {
    for( j=0; j<3; j++)
    {
      toneAC( 720, 3, 0, true);
      delay( 50);
      toneAC( 610, 3, 0, true);
      delay( 50);
      noToneAC();
      delay( 30);
    }
    delay( 500);
  }

  delay( 2000);
 
  // Loud alert
  for( i=0; i<10; i++)
  {
    for( j=0; j<3; j++)
    {
      toneAC( 720, 10, 0, true);
      delay( 50);
      toneAC( 610, 10, 0, true);
      delay( 50);
      noToneAC();
      delay( 30);
    }
    delay( 500);
  }

  delay( 2000);

If you want a loud alert from a piezo, it's best to first find its harmonic frequency.  You can do it with a miltimeter.  Just look for the frequency where the most current is drawn.  It will typically be quite a large spike right at the loudest point.  If you want something loud, this will be the frequency to play.  With a piezo, it's typically 4,000 Hz.

Tim
Logged

Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

Offline Offline
Edison Member
*
Karma: 9
Posts: 1016
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Example 2 : A whistling kettle with boiling water sound.
This is a full sketch.

Code:
// Example for the toneAC library
// A whistling kettle with boiling water sound.
// public domain

#include <toneAC.h>

void setup()
{
}

void loop()
{
  int i, freq, freq_extra, freq_shift, vol;

  freq = 1440;
  vol = 1;

  for (i=0; i<1000; i++)
  {
    freq_shift = random (-15, 15);
    freq += freq_shift;
   
    // Stay around the frequency 1440
    if (freq < 1440)
      freq ++;
    else
      freq --;

    vol = i/50;     
    if (vol > 10)
      vol = 10;
    if (i > 800)
      freq_extra = (vol * vol) + 800;
    else
      freq_extra = (vol * vol) + i;
   
    toneAC( freq + freq_extra, vol, 0, true);
    delay(30);
  }
  noToneAC();
 
  delay(5000);
}
Logged

Toledo, OH
Offline Offline
God Member
*****
Karma: 36
Posts: 510
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Don't bother about the mini, it's not that important.

To save a bit of compiled code size, use toneAC(). instead of noToneAC().  noToneAC() was meant to be familiar to the Tone library and the noTone() command.  But, it's totally not needed for toneAC() and as a bonus you save 10 bytes.

Also, I created a TONEAC_TINY switch that allows you to save around 110 bytes that uses an alternate version of toneAC().  Would be useful for projects where maximum speed or the lowest code size is required.

I'm considering creating another library named something like ToneTiny or NewTone where it's a plug-in replacement for Tone including pin assignment but is much smaller, faster, and better quality.  I know lots of people that have timer interrupt issues with the Tone library and are looking for an alternative.  Not that toneAC doesn't provide that already, but toneAC is designed more for highly accurate AC switching speed and maximum volume.

Tim
Logged

Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

Toledo, OH
Offline Offline
God Member
*****
Karma: 36
Posts: 510
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm considering creating another library named something like ToneTiny or NewTone where it's a plug-in replacement for Tone including pin assignment but is much smaller, faster, and better quality.  I know lots of people that have timer interrupt issues with the Tone library and are looking for an alternative.  Not that toneAC doesn't provide that already, but toneAC is designed more for highly accurate AC switching speed and maximum volume.

It was so easy I just did it, NewTone is a working library.  Plug-in replacement for the Tone library, but over 1,200 bytes smaller compiled code size, faster, better quality sound, etc.  Also uses timer 1 if you're having a conflict on timer 2 with the Tone library and some other library or hardware.  Seriously took 30 minutes to write.

A little bit surprising is that toneAC still produces slightly smaller code (60-170 bytes).  This is possible because toneAC doesn't need to find the ports and masks for the pins as it's fixed.  But, I believe NewTone still has a place for those who need smaller, tighter code, yet need the flexibility of connecting to any pin.  toneAC is still better in every way, with the exception being the fixed pins, but that's also why it's so good.

I'll probably package up NewTone and upload it later today for those who need a flexible drop-in replacement for the Tone library.

Tim
« Last Edit: January 19, 2013, 03:01:53 am by teckel » Logged

Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I'm considering creating another library named something like ToneTiny or NewTone
TinyTony? sounds Italian, would fit the Arduino smiley-wink
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Edison Member
*
Karma: 9
Posts: 1016
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Example 3: Tone Tutorial with fade out tones.
You have to listen to this for yourself. It is not like a piano, because it sounds very computer/robotic.

Use the tone tutorial : http://arduino.cc/en/Tutorial/tone
But connect the loudspeaker to two output pins as is required for the toneAC library.

Replace the setup() function with this:
Code:
// Example for toneAC with tone fade out.
// Using the tone tutorial : http://arduino.cc/en/Tutorial/Tone
// public domain

void setup()
{
  for (int thisNote = 0; thisNote < 8; thisNote++) {
    // to calculate the note duration, take one second
    // divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000/noteDurations[thisNote];
   
    // Add some to the length for the pauses between the notes.
    noteDuration += noteDuration/2;
    int i, vol;
    // Setting the volume to 12 is a trick to reduce the code.
    // The maximum volume is 10, but 12 is allowed.
    vol = 12;
    for (i=0; i<=noteDuration; i+=25)
    {
      // Double the frequency to make it sound better.
      toneAC( 2* melody[thisNote], vol, 0, true);
      // fade out
      if (vol > 0)
        vol--;
      delay( 25);
    }
    noToneAC();
  }
}
Logged

Greenville, IL
Offline Offline
Edison Member
*
Karma: 15
Posts: 1330
Warning Novice on board! 0 to 1 chance of errors!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


 @teckel

 I tried your sketch on a Bobuino with a 1284 chip, the speaker buzzed but nothing more. The board is found halfway down this page. http://www.crossroadsfencing.com/BobuinoRev17/

 This may be asking too much but, would you consider making your library work with the 1284P? I have built a few boards using the chip.

This page may help you in the process if your interested. http://maniacbug.wordpress.com/2011/11/27/arduino-on-atmega1284p-4/

 I will be happy to test for you of course.  smiley

Logged


Toledo, OH
Offline Offline
God Member
*****
Karma: 36
Posts: 510
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I tried your sketch on a Bobuino with a 1284 chip, the speaker buzzed but nothing more. The board is found halfway down this page. http://www.crossroadsfencing.com/BobuinoRev17/

This may be asking too much but, would you consider making your library work with the 1284P? I have built a few boards using the chip.

This page may help you in the process if your interested. http://maniacbug.wordpress.com/2011/11/27/arduino-on-atmega1284p-4/

I will be happy to test for you of course.  smiley

I had believed it would work with the 1284P.  Don't have one to confirm, but looking into it further I see that the ATmega1284P is different.  I've updated my development v1.2 to support ATmega640, ATmega644, ATmega1281, ATmega1284P and ATmega2561.  I've private messaged you a file for you to test to verify it works on the ATmega1284P.  The pins you should use are D12 and D13 (the timer 1 PWM pins) on that microcontroller.

Let me know how it works.

Tim
Logged

Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

Pages: 1 [2] 3 4   Go Up
Jump to: