Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

Thread: I am trying to mix multiple audio files from an SD card to a PWM pin(sound quality)

  1. #1

    I am trying to mix multiple audio files from an SD card to a PWM pin(sound quality)

    Hey guys, it's me Nathan Ramanathan; that guy who made MusicWithoutDelay library:https://github.com/nathanRamaNoodles...c-Synth-Engine.

    Anyway, I've decided to up my game a bit by writing some code that can mix multiple WAV files from an SD card via PWM on a timer interrupt.

    **I know the Teensy Audio library has this, but I want to challenge myself. Also, I'm gunna port this code to Arduino Uno for my library.

    The good news: it works and is very efficient at mixing many audio files at the same time.
    The bad news: There's static noise(even with a Low Pass filter). And the audio quality is not that pretty.
    But it still "sounds" possible to get better quality audio. Pun Intended 😉.

    I was wondering if you guys could try and optimize my code to better quality. I've commented on my code. I've got two buffers for the SD card and the extracting of the files is done in the main loop(), while the audio is played in the timer interrupt portion.

    Thanks,
    -Nathan Ramanathan

    P.S.
    Paul, thanks for the free Teensys. I absolutely love them. Also, I am updating my library to allow playing SD files on an Arduino Uno.

    Code:
    Code:
    /*
        SD WAV mixer via PWM
        by Nathan Ramanathan (Github: https://github.com/nathanRamaNoodles)
    
        Compatibility: Arduino Uno/nano/mini, Teensy 3.x
    */
    //In this project, I decided to improve my c++ skills by playing audio from a SD card onto a PWM pin.
    //Also, I added the ability to mix WAV file sounds, from my other library: https://github.com/nathanRamaNoodles/Polyphonic-Synth-Engine
    //I know this is hard to do, but so far the quality sounds fine on a Arduino UNO and
    //on a Teensy.  But now, I want to make my code more efficient and sound better.
    
    //I'm using a microSD card adapter from Ebay.  Unfortuantely, the SD card only opens when The Teensy's CPU rate is 24 Mhz.
    //On an Arduino Uno, the audio outputs at pin 3.
    //On Teensy, the Audio is on pin 3(but you can change it below).
    //Schematic:  https://raw.githubusercontent.com/nathanRamaNoodles/MusicWithoutDelay-LIbrary/master/MusicWithoutDelay.png
    
    
    //Directions:
    //You can do two things:
    //1. Do nothing: the microcontroller will play one song.
    //2. Type 'p' in Serial: The microcontroller will play two tracks at the same time by mixing them.
    
    
    #include <SPI.h>
    #include "SdFat.h" //faster SD library
    SdFat SD;
    
    char dataFilename[] = "zelda32k.wav"; //wav files
    char dataFilename2[] = "guardian.wav";
    
    #define FS_music 32E3  //Sample Rate at 32,000 Hz
    
    #if defined(__arm__) && defined(TEENSYDUINO)
    #define buffSize 1000              //Buffer Size
    static void timerInterrupt();
    static IntervalTimer sampleTimer;
    const int SD_ChipSelectPin = 10;
    const int audioPin = 3;
    #else
    #define buffSize 128
    #define SET(x,y) (x |=(1<<y))                //-Bit set/clear macros
    #define CLR(x,y) (x &= (~(1<<y)))             // |
    const int SD_ChipSelectPin = 4;
    #endif
    
    File dataFile;
    File guardianFile;
    
    uint8_t *buffPointer;  //pointer
    uint8_t *buffPointer_2;
    uint8_t buf[buffSize];  //buffer
    uint8_t buf_2[buffSize];
    uint8_t backBuf[buffSize]; //backup buffer for efficiency
    uint8_t backBuf_2[buffSize];
    uint8_t lastValue = 0;  //most recent value from SD card played to Speaker
    uint8_t lastValue_2 = 0;
    
    uint32_t filePosition = 0; //Current file position
    uint32_t filePosition_2 = 0;
    
    bool buffTrigger = false; //Triggered when Buffer is ready.(not a good idea, due to lag)
    bool backBuffFlag = false; //Tells when Speaker has finished playing buffer
    bool finished = false; //song is finished
    bool finished_2 = false;
    bool toggle = false; //alternate between backup and main buffer
    
    void setup() {
      Serial.begin(115200);
      //  while (!Serial);
      Serial.println(F("Hi there :)\nType 'p' to play both wav files at same time, or do nothing to let me play only one song."));
      Serial.println(F("Starting simple WAV demo\n"));
    
    
      if (!SD.begin(SD_ChipSelectPin)) {
        Serial.println("SD fail");
        return;
      }
      dataFile = SD.open(dataFilename);
      guardianFile = SD.open(dataFilename2);
      if ( !dataFile || !guardianFile) {
        Serial.println("File not opened");
      }
      dataFile = SD.open(dataFilename, FILE_READ);
      guardianFile = SD.open(dataFilename2, FILE_READ);
      dataFile.seek(44); //seek to beginning of main wav file data
      filePosition = 44;
      guardianFile.seek(44);
      filePosition_2 = 44;
      finished_2 = true; //this stops the guardianFile song
      resume(); // setup our timerInterrupts
    }
    
    void suspend()
    {
    #if defined(__AVR__)
      CLR(TIMSK1, OCIE1B);                            //-Stop audio interrupt
    #endif
    }
    void resume()
    {
    #if defined(__arm__) && defined(TEENSYDUINO)
      sampleTimer.begin(timerInterrupt, 1000000.0 / FS_music);
      analogWriteFrequency(audioPin, FS_music);
    #else
      cli();
      TCCR1A = 0x00;                                  //-Start audio interrupt
      TCCR1B = 0x09;
      OCR1A = 16000000.0 / FS_music;        //-Auto sample rate
      SET(TIMSK1, OCIE1B);                            //-Start audio interrupt
      sei();
    
      TCCR2A = 0xB3;                                  //-8 bit audio PWM
      TCCR2B = 0x01;                                  // |
      OCR2B = 127;
      SET(DDRD, 3);              //-PWM pin at PIN 3 on Arduino Uno
    #endif
    }
    // Function to copy 'len' elements from 'src' to 'dst'
    void copyArray(uint8_t* src, uint8_t* dst, int len) {
      memcpy(dst, src, sizeof(src[0])*len);
    }
    bool secondSongStarted = false;
    void loop() {
      //  if (!secondSongStarted && filePosition > (dataFile.size() / 2)) {  //Starts second song halfway through first song
      //    secondSongStarted = true;
      //    finished_2 = false;
      //    filePosition_2 = 44;
      //    guardianFile.seek(filePosition_2);
      //  }
      if (Serial.available()) {
        char str = Serial.read();
        switch (str) {
          case 'p':
            //        secondSongStarted = false;
            finished = false;
            filePosition = 44;
            dataFile.seek(filePosition);
            finished_2 = false;
            filePosition_2 = 44;
            guardianFile.seek(filePosition_2);
            break;
        }
      }
      if (!backBuffFlag) {
        toggle = !toggle;
        if (toggle) {
          if (!finished)dataFile.read(backBuf, sizeof(backBuf));
          if (!finished_2)guardianFile.read(backBuf_2, sizeof(backBuf_2));
        }
        else {
          if (!finished)dataFile.read(buf, sizeof(buf));
          if (!finished_2)guardianFile.read(buf_2, sizeof(buf_2));
        }
        backBuffFlag = true;
        filePosition += buffSize;
        filePosition_2 += buffSize;
        if (!finished && (dataFile.size() < filePosition)) {
          finished = true;
          Serial.println("Done 1");
        }
        if (!finished_2 && (guardianFile.size() < filePosition_2)) {
          finished_2 = true;
          Serial.println("Done 2");
        }
      }
      else {
        if (!buffTrigger) {
          if (!finished) {
            if (toggle) {
              buffPointer = backBuf;
            }
            else {
              buffPointer = buf;
            }
          }
          if (!finished_2) {
            if (toggle) {
              buffPointer_2 = backBuf_2;
            }
            else {
              buffPointer_2 = buf_2;
            }
          }
          toggle = !toggle;
          buffTrigger = true;
          backBuffFlag = false;
        }
      }
    }
    
    int location = 0;
    #if defined(__AVR__)
    SIGNAL(TIMER1_COMPB_vect)
    #elif defined(__arm__) && defined(TEENSYDUINO)
    static void timerInterrupt()
    #endif
    {
      //  if (buffTrigger) { //I've commented this out because its not efficient
      if (location < buffSize) {
        int sample = ((((finished) ? lastValue : lastValue = buffPointer[location])
                       + ((finished_2) ? lastValue_2 : lastValue_2 = buffPointer_2[location])//add our outputs
                      )
                      >> 2 //Shift 2 bits to the right. This is faster than dividing by Four.
                     )
                     + 127 //add 127 to make it a bit louder
                     ;
        location++; //increment our pointer's location
    
        //OUTPUT to our Audio pin.
    #if defined(__arm__) && defined(TEENSYDUINO)
        if (sample != 127)analogWrite(audioPin, sample);
    #else
        if (sample != 127)OCR2B = sample;
    #endif
      }
      else {
        buffTrigger = false;
        location = 0;
      }
      //  }
    }

  2. #2
    Anybody?

    I used a teensy 3.2 @ 24 Mhz with a micro sd card adapter from ebay. I'm not sure how much more I can improve my code to maximize efficiency from playing audio from an SD card.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,031
    Quote Originally Posted by nathanSuperStar View Post
    **I know the Teensy Audio library has this, but I want to challenge myself.
    ....
    The bad news: There's static noise(even with a Low Pass filter). And the audio quality is not that pretty.
    Maybe do it the easy audio library way, for the sake of testing whether the noise is a hardware problem or something going on within your software.

  4. #4
    I tested it with your Audio library via DAC at pin A14 on my Teensy 3.2. It sounds really good.
    So I guess this means my software is making the humming noise. How do I get rid of it? I think my sampling method isn't efficient, but I tried looking at your source code for output_dac.cpp and it looks like your using DMA? What's that, and is there a way to make my software sound better?

    BTW, your code looks really advanced yet easy to read, good job.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •