New library for PWM playback from SD cards: SimpleSDAudio

Tuttut:
Hehe, I now got stereo at fullrate working - I've rewritten the audio output interrupt routine completely in ASM. Next step is heading to 4-channel audio output at 8-bit or stereo-16-bit-output...

Hi there,

just stumbled upon your library, it's great!
Many thanks!!

The last version on your website (v1.01) seems to date previous to this rewrite.
Any chance of posting this new version?
I would like to do 16-bit mono @8mhz, so I could use the extra resources...

Thanks again for sharing this!!

Regards Dennis

Hello,

This looks like a great library. Any chance that it could be ported over to include support for the chipkit Uno32? How difficult do you think it would be to do so? It appears that PWM would be relatively simple to port over, but I am not sure about the SD card interface. Any ideas?

With the 80MHz 32bit processor on chipkit, you could have faster SD card read times and high sample rates that would enable more processor free time and better sound. The chipkit is also natively 3.3V, so no level shifters are necessary.

-Thomas

Well, I am starting to port the code over to the chipkit Uno32. First I had to port the library back to Arduino 0023, since that is what the chipkit software--MPIDE--is based on. This was mostly just changing

#include <Arduino.h>

to

#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

as well as converting the examples from .ino back to .pde files. After this massaging, I got it to compile in 0023.

The code is not pretty at the moment since I am using a lot of "#if defined (PIC32MX)" to switch between AVR and PIC specific code. I have the SD card library working on the Uno32, so now I need to port over the PWM.

I am running the SD card at 400kHz during initialization and then at 20MHz for everything after that. It is looking like the PWM will be sampled at 78125Hz for full rate and 39062.5Hz for half rate. These values were chosen to give perfect 8bit resolution on the PWM and still be close to the original 62500 and 31250Hz values.

cant wait to see some examples (video) of how it sounds....

For anyone interested in further details regarding my chipkit port, follow the progress at chipKIT32/chipKIT-core · Discussions · GitHub.

-Thomas

Might I make a request?...

that SdPlay.deInit(); would release whatever SPI timers etc that were grabbed by calling init().

I'm trying to use this in the same project with the Servo.h library, but there's apparently a conflict.
Once I call SdPlay.init(), I can no longer position servos where I want, even if I'm not playing a sound.

Not really a problem since I don't need to do both simultaneously.
I could init(), setFile, play etc., then deInit before returning, but deInit isn't putting things back the way they were...

Any suggestions?
-- thanks

Hi,
I released a new version of the library. The core audio output function is now written in assembler and thus allowing playback of either STEREO and FULLRATE or HALFRATE with four channels. Two channels can be combined to get 16-bit resolution using only a resistor and a trimmer. But in this case the faint noise of the controller can be heard, so I recommend using cheap Schmitt-Trigger buffers (like 74HC14 or else) to get rid of the noise that is on top of the digital signals of the Arduino. With HALFRATE and QUADRO mode and those Schmitt-Trigger circuit you can get a quality that is very good also for music or sound with big dynamics. I is still not full CD quality but quite near to it.

Also I did a patch that you can apply to Arduino that frees the timer0 from all the core functions and makes it available for audio output (timer2 is then used instead of timer0 for core functions like millis and so on). This way audio can be output on pins 6 and 5 instead of pins 9 and 10 on ATmega328 based platforms like Uno, Duemillanove and Ethernet or on all 4 pins together allowing also the Uno to do 16-bit stereo output... :grin:

Further, for Windows user I included the SOX binaries in the library so you can convert WAV-files using the drag'n'drop targets.

Have fun!

Hi Tuttut,

Great work, many thanks for this!!!

As for audio filtering, I've tested with a "Passive, first order low-pass RC filter", which seems to clean out most of the noise with just a resistor and a capacitor, see Low-pass filter - Wikipedia.
I found a 1kohm resistor and a 2.2qf capacitor work out for me, but to be honest, I used a trial and error approach...
Not sure if this even comes in the neighborhood of your buffer approach, but I found the improvement more than adequate for my project.

Once again, many thanks!!!

Regards Dennis

@cobra18t: I like your attempt of porting my library. I included your header-file-stuff for earlier Arduino versions but I never tried the lib on older Arduino IDEs. For SD access you can use my reduced SD library - you have to adapt the sd_l0-files, but the other layers should work quickly on any architecture after that.

@PWBarret: The servo-library (re)initializes the servos after detaching and re-attaching all servos. So try this order: 1. SimpleSDAudio: init -> Play... -> deInit, then Servo: Attach -> control -> detach, then continue with SimpleSDAudio. Both libraries can not work the same time because they share the same timers.

@gateway: You are right, a RC filter might often be an easy way to improve the sound quality on PWM outputs. It works better the higher the PWM frequency is -> SimpleSDAudio uses the highest possible frequency and therefore a RC filter should work pretty well.

Tuttut.

@Tuttut: I saw my changes in the newest version, I will have to try it out in 0023. Thank you for abstracting the SD card library so that I only had to change the "0" level. That made it much easier.

I have a video of a basic 8-bit 78.125kHz stereo sample running on my ChipKit Uno32 using a minimal setup. Check it out:

-Thomas

Thanks for the suggestion...
Maybe it's the servo library that's not properly honoring the detach method then...
If I never attach the servos at all, the sound is good.
But if in my PlaySD function, if I detach both servos upon entry, then init, play, deinit, the sound is garbled.

Hi Tuttut-

I havent kept up with things since you first posted the initial library.. (I see its come very far).. great job!.. and a HUGE benefit to the community IMHO! (thanks)

I am going to re-read the posts.. and go to your site and download the latest..etc..

1.) Does the Arduino board/version matter to use this library? Can the board be a 3.3v @8Mhz board and still be able to utilize this lib?

2.) how much space/resources do we have to 'do' other things WHILE playing audio back?

3.) I saw mention of it being able to be used with IDE v23? IS this added/supported now? or must I use the 1.x+ IDE's?

I have a project in mind that I intend to use this lib for... but the board/pcb is a 'VERY' minimal Arduino circuit running at 3.3v and internal clock @ 8Mhz and SD socket...

(actually.. Im trying to run it off a coin cell.. think thre will be enough voltage (3.0v I believe most coin cells).. if there is enough current for the SD card?

I remember there needs to be a cap in-line with the speaker out line... so I'll probably look at adding that 'on-board' to the pcb itself.....so end result is just solder speaker wires to pcb pads. (or reading up on the low pass filter to put on board)

off to look over the public methods available to us.. and check for any examples on how to utilize each one.

thanks!!

Hi xl97,

  1. The lib is tested on Arduinos using mega328 or mega2560 Arduino based plattforms. I don't know if it will work on things like Leonardo or those USB-supporting-AVR-Devices.
    If you want to use a 8 MHz Arduino you need to convert the samples using the batch-files for 8 MHz, this will half the sampling rate but is often still good enough. If you use a 3.3V Arduino you can attach the SD card without those resistor-divider-stuff directly to the Arduino.

  2. Audio-Lib takes about 1.3KB of RAM, which is a lot especially on mega328 devices. The timer that belongs to the audio-output-pin is fully utilized by the lib and therefore not available anymore. The processor time amount needed is also very high, I guess at nearly 80-90% for highest quality audio stereo. But often that is enough for applications that does not need much power.

  3. I've added some stuff to allow for work in versions prior 1.x, but I never tested that. I think I have heard other people got it to work with v23.

The cap is by the way only to block DC currents from the speaker. It does not act as a low-pass so far as the PWM frequency is out of the hearing range for 16 MHz Arduinos. For 8 MHz you should use the highest rate, then it should be ok as well.

tuttut

@Tuttut

Any suggestions for changing the code so multiple files could be played at once/asynchronous from each other. Would I just keep track of a "_fileinfo" object and a buffer for each file playing? Then the worker routine would service each buffer as necessary and the PWM output would be some sort of summation of the individual outputs?

Note that this is for my port of your library to the faster ChipKit compatible microcontrollers. My ideal scenario would be to create a library capable of 4-8 channels of audio. You could designate 1 or more of those channels to be protected, by which I mean they would not stop playing if the user requests another file to be played. Instead, the next file would go to the next available unprotected channel, where "next" is determined by a ring buffer.

As an example, Super Mario Brothers sounds. Assuming 4 channel audio. You would want the background music to be protected, because you don't want it to stop when the sound effects play, so maybe it plays through channel 0. If you jump, then the jump sound effect would play in channel 1. Then you shoot two fireballs while you are in the air, whose sounds play in channels 2 and 3. If the first fireball hits a bad guy, then the jump sound would be stopped and the hit sound would play back in channel 1.

-Thomas

Hi,
I also thought about extending the library to multi-playback, even on AVR based plattforms. On AVR, the interrupt itself creates big overhead due the many PUSH and POP stuff (I think ARM has a somewhat nicer architecture there), so doing things in interrupt is not the best way. On Arduino, if sample buffer is not changed, I works a little like a ping-pong buffer, because a whole sector of samples is read before the buffer is released for playback. If multi-playback of different sounds starts sector aligned, two different sectors can be read and added together in playback buffer and the buffer can be released as one block. This become more complex when not sector aligned.

For your approach I like your idea of buffering the sounds in different memory locations and maybe adding them in interrupt (maybe also scale the values for volume control). SD cards tend to be damn slow if sectors are not read in order, to overcome this your approach could lead to read n sectors from file 1 and then n sectors from file 2. With n being a number bigger than 1, you can trade more possible simultaneos channels at the cost of a bigger starting delay.

BTW: But decide and try yourself, getting the best compromise involved a huge efford of research even for that simple "SimpleSDLibrary" on AVRs. It was a long way to find out that simply using a high PWM rate even at low sampling rates is the most important factor on the way to get nicer audio output. On that way I also tried things like storing 1 bit delta-sigma-coded audio on a SD card and playing that back at high rates through SPI output. It worked good, but involved heavy conversion on PC side and adding sounds or even modifying the volume is not possible anymore. In the end I considered it not simple enough for Arduino world...

I got this working on an Arduino Uno (R3) in under 30 minutes this morning, very happy to hear the audio coming out of my speaker - however, when I tried connecting an LM386N-3 based 1W amplifier (ebookbrowse.com - ebookbrowse Resources and Information.) with a set gain of 50 (adjustable input attentuation via POT) to the output pin (using the recommendations in the docs for powered speakers), all I got was beeping noises rather than audio?

Any advice appreciated!

sorted - was a dry joint on a cap.

Opps, i think I dumped my first post... appologies if this ends up being a duplicate.

I am having some unexpected behavior with some SimpleSDAudio code. I am basically working on a lightsaber and I want the code to play a HUM.AFM file whenever something else isn't playing.

Simple enough. The issue I am running in to is that SdPlay.isStopped() returns false (0) when the code goes in to the first "loop(void)" even though nothing is playing. While that is easy enough to code around it seems it should in fact return true (1). Once kicked over through one loop it seems to work fine.

void loop(void) 
{
Serial.println(F("Loopy"));
 Serial.println(SdPlay.isStopped());
  if (SdPlay.isStopped()) {
    SdPlay.setFile("Hum.afm");
    SdPlay.play();
}
 while(!SdPlay.isStopped()) {
    SdPlay.worker();
   }
}

Anyway.... not a huge deal, it just seemed odd.

Further details..... my "fix" was just calling to

SdPlay.setFile("");

In the void setup() section. That seems to get the ball rolling too.

Pin 44 on my mega 2560 is committed to another function that I cannot change. So is it possible to use a different timer (like timer4) for PWM output with this code?