New library for PWM playback from SD cards: SimpleSDAudio

@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?

Hi,
for performance reasons, audio pins are fixed in the library. But the library is written in a way, that all that must be defined to use a certain timer is placed in the file SimpleSDAudioDefs.h, so take a look in that file, maybe you can patch it for your purposes. All defines there are used from assembly as well as c-files, so there should no need to touch any other files.

@TroyO
Hi, I've analysed your stop-issue, I think you are right, there is a little bug in the library that clears the stop-flag in the init-function. Calling SdPlay.stop(); after .init should also solve the issue.

good to know tuttut-

@TroyO-

I saw the 'double press' in the video..

glad it can all be 'fixed' with a line of code. :slight_smile:

I'll be using this heavily in a project over the next 3-4 weeks..

I have high hopes for this lib. :slight_smile:

(will be starting next week hopefully!) :slight_smile:

This is a really good lib. Been playing with it all morning, really should be doing work on my other project, and I've had no problem playing files converted with:

$ sox inputfile -b 8 -r 64000 -c 1 -t raw outputfile

Good work on getting all this together, I bought an arduino with a few projects in mind, but never thought
I'd be playing music off of the thing. Keep up the awesome progress!

Fantastic library, got it working within few minutes! :slight_smile:
Is it possible to play Audio in parallel while Arduino does other things? I wasn't able to figure out how. I wanted to see if I can dump WaveShield in favor of this library. WaveShield allows me to play Audio regardless of other things I'm doing, which is essential for my Alarm clock (shows time, blinking dots while playing alarm audio file).
I did some testing with SimpleSDAudio and it looks like if delay between calling SdPlay.worker() greater than 5 milliseconds, playback slows down and becomes choppy... Even calling Serial.println ("test") slows down playback noticeably. Here's example of code from loop:

void loop(void) {
  Serial.println ("running");
  //delay (5);
   if (!SdPlay.isPlaying() ) SdPlay.play();
  // Let the worker work until playback is finished
  SdPlay.worker();
 }

Let me know if there's a trick to it :slight_smile:
Maybe buffer can be auto-filled by Interrupt?

bratan:
Fantastic library, got it working within few minutes! :slight_smile:
Is it possible to play Audio in parallel while Arduino does other things? I wasn't able to figure out how. I wanted to see if I can dump WaveShield in favor of this library. WaveShield allows me to play Audio regardless of other things I'm doing, which is essential for my Alarm clock (shows time, blinking dots while playing alarm audio file).
I did some testing with SimpleSDAudio and it looks like if delay between calling SdPlay.worker() greater than 5 milliseconds, playback slows down and becomes choppy... Even calling Serial.println ("test") slows down playback noticeably. Here's example of code from loop:

void loop(void) {

Serial.println ("running");
 //delay (5);
  if (!SdPlay.isPlaying() ) SdPlay.play();
 // Let the worker work until playback is finished
 SdPlay.worker();
}



Let me know if there's a trick to it :)
Maybe buffer can be auto-filled by Interrupt?

Don't quote me on this, but from looking at the code, I think you could do such given that you make a call to the SdPlay.worker() function often enough to keep the buffer filled. I'm not sure how much of the processor is left once playing audio, but it seems you could squeeze in a few other non time dependent things. (Like adding up time or checking for a button and updating its state...simple things)

From what I have gathered, the SdPlay.worker() function reads the next sector of the sdcard into a buffer. So if you call it often enough, you should be okay, just don't ask for anything that needs a ton of resources while playing audio.

Feel free to correct me if I'm wrong, but this is what I have gathered so far.

Edit: Just from playing with the serial I can see what you are saying, but I haven't come up with a solution.
Edit2: If you hear the skip when using serial at 9600, move it to a higher speed. I'm running at 115200 just to see, and there isn't a noticeable skip when using the serial commands in the demo.

Hi

I'm exited about this library. I might have a troublesome SD Card module.
I keep getting an error code: 49. I cant find out what code 49 is.
If I use Arduino's standard SD example listfiles, I get a correct list of files on the SD card. The SdFat library from Google Code Archive - Long-term storage for Google Code Project Hosting. also read the SD Card with the same wiring. So the wiring must be correct. And I use SdPlay.setSDCSPin(10); as pin 10 is my CS pin.
Then I commented the line: SPSR |= (1 << SPI2X);
Same result.

My sd module is this one http://www.lipoly.de/index.php?main_page=product_info&products_id=213383

I really hope someone can with this problem.

EDIT ----
It was not the SD Card Module but the SD Card itself. I tried with another one. Now it works.

timberwolf9:
Don't quote me on this, but from looking at the code, I think you could do such given that you make a call to the SdPlay.worker() function often enough to keep the buffer filled. I'm not sure how much of the processor is left once playing audio, but it seems you could squeeze in a few other non time dependent things. (Like adding up time or checking for a button and updating its state...simple things)

From what I have gathered, the SdPlay.worker() function reads the next sector of the sdcard into a buffer. So if you call it often enough, you should be okay, just don't ask for anything that needs a ton of resources while playing audio.

Feel free to correct me if I'm wrong, but this is what I have gathered so far.

Edit: Just from playing with the serial I can see what you are saying, but I haven't come up with a solution.
Edit2: If you hear the skip when using serial at 9600, move it to a higher speed. I'm running at 115200 just to see, and there isn't a noticeable skip when using the serial commands in the demo.

No you are right, my thoughts exactly (about buffer). I also guessed that increasing baud rate will improve things, but never tested it so you just proved it :slight_smile: It makes sense. However I'm just wondering how did they pull off the trick with WaveShield? I can do lots of heavy things with microprocessor while wave is playing, it never skips a beat... Can same be accomplished with this library? :slight_smile: Or because there's no DAC it's impossible to do? I'm curious to hear what author thinks.

AllanB:
Hi

I'm exited about this library. I might have a troublesome SD Card module.
I keep getting an error code: 49. I cant find out what code 49 is.
If I use Arduino's standard SD example listfiles, I get a correct list of files on the SD card. The SdFat library from Google Code Archive - Long-term storage for Google Code Project Hosting. also read the SD Card with the same wiring. So the wiring must be correct. And I use SdPlay.setSDCSPin(10); as pin 10 is my CS pin.
Then I commented the line: SPSR |= (1 << SPI2X);
Same result.

My sd module is this one http://www.lipoly.de/index.php?main_page=product_info&products_id=213383

I really hope someone can with this problem.

EDIT ----
It was not the SD Card Module but the SD Card itself. I tried with another one. Now it works.

Just my two cents, I had this same error and was to fix it by reformatting the sd as FAT. You may be able to do the same with your sd, but don't use the quick format or the card will give the same error.