element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Holiday Special 20
  • Challenges & Projects
  • Project14
  • Holiday Special 20
  • More
  • Cancel
Holiday Special 20
Blog A QT Py Christmas - add Sound
  • Blog
  • Forum
  • Documents
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Holiday Special 20 to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: ralphjy
  • Date Created: 25 Dec 2020 11:39 PM Date Created
  • Views 1492 views
  • Likes 5 likes
  • Comments 1 comment
  • seeeduino xiao expansion board
  • seeeduino xiao
  • oled
  • audio amplifier
  • cap sense
  • dac
  • neopixel ring
  • holidayspecial20ch
  • adafruit qt py
Related
Recommended

A QT Py Christmas - add Sound

ralphjy
ralphjy
25 Dec 2020

For the final element of my QT Py Christmas ornament, I'd like to add the ability to play some Christmas music.

 

The SAMD21 has a single DAC output that I'll use to generate tones, but I'll also need to add an audio amplifier to drive a speaker and figure out how to access the external flash memory that I added to store the music files.

 

In this blog I'm going to cover adding the audio amplifier and generating the tones using the DAC.  I'll hopefully be able to get the music playing from the flash memory next time.

 

Audio Amplifier

I recently used a PAM8302A audio amplifier Hat with the M5StickC, so I decided to use an Adafruit PAM8302A breakout board for the audio amplifier.  This is a simple mono class D amplifier, so I'll just need to attach power and ground and the DAC output.  Here is the schematic below:

image

There is a shutdown pin to save power when not in use, but I'm not going to use it.  I'm also not driving it differentially, so I'll tie the AIN- input to ground.  I'll add a small 8 ohm speaker to the output.

 

The small SMD potentiometer that is used to adjust the input amplitude is very fragile, so if I have to adjust volume much I may end up adjusting the DAC output swing instead of using the pot (I've read reviews of this pot breaking).

 

Audio Library

I found a SamdAudio library that plays WAV files on the SAMD21 DAC using either an SD card or flash memory https://github.com/hydronics2/SamdAudio  .

Since I just used the SD card interface on my Xiao Expansion board, I'll try this library on that first and then move on to trying it with flash memory on the QT Py (the Xiao doesn't have flash memory and the Expansion board is too big to use with the QT Py on the ornament).

 

Christmas Music

The library includes examples using 8 bit WAV files sampled at 22 and 32 kHz.  I had a difficult time finding Christmas music in that format but I found a fair amount of free music in MP3 format.  For the demo I selected "We Wish You a Merry Christmas".  I found an online converter that will convert MP3 to 8 bit WAV format https://audio.online-convert.com/convert-to-wav  .   I've attached the converted file below.

 

Audio player program

Xiao_play_wav_from_SD_card.ino

/*
  Audio player, non blocking.
  read 8bit mono .wav file, up to 4 channels
  use Audacity to convert your audio file

  Author : AloyseTech
  Modified: BriscoeTech
  03/17/19: https://github.com/hydronics2/SamdAudio

  if the sketch compiles, but no sound, check to make sure:
   - '#include <SdFat.h>' is uncommented in SamdAudio.cpp 
   - 'extern SdFat memory;' is ucommented in SamdAudio.cpp
*/

#include <SdFat.h>  //https://github.com/greiman/SdFat
SdFat memory;

#include <SamdAudio.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

SamdAudio AudioPlayer;

#define NUM_AUDIO_CHANNELS 4 //could be 1,2 or 4 for sound

#define AUDIO_BUFFER_SIZE 512 //512 works fine for 22.05kh, 
//set buffer size to 1024 for 32khz and 44.1khz for Adafruit M0 with QUAD Flash

//indicate sample rate here (use audacity to convert your wav)
const unsigned int sampleRate = 22050; //hz

//your wav file
const char *filename0 = "piano8bit_22khz.wav";
const char *filename1 = "sfx1_8bit_22khz.wav";
const char *filename2 = "sfx2_8bit_22khz.wav";
const char *filename3 = "sfx3_8bit_22khz.wav";
const char *filename4 = "sfx4_8bit_22khz.wav";
const char *filename5 = "we-wish-you-a-merry-christmas.wav";

// SD chip select pin (with ethernet shield : 4)
#define YOUR_SD_CS 2

void setup()
{
  pinMode(YOUR_SD_CS, OUTPUT);

if ( !memory.begin(YOUR_SD_CS) )
  {
     //the sd card error was found, exit this function and to not attempt to finish
     Serial.println("can't start card");
  }

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();

   // write to display
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  display.setCursor(15, 25);
  display.print("We Wish You A");  
  display.setCursor(11, 40);
  display.print("Merry Christmas!");
  display.display();    
  
  Serial.begin(115200);
  while (!Serial); // open the serial to start!

  Serial.print("Initializing Audio Player...");
  if (AudioPlayer.begin(sampleRate, NUM_AUDIO_CHANNELS, AUDIO_BUFFER_SIZE) == -1) 
  {
    Serial.println(" failed!");
    return;
  }
  Serial.println(" done.");

  AudioPlayer.play(filename5, 0); //play 5th file on channel 0

  Serial.println("Playing file.....");
}

void loop()
{  
  if (Serial.available()) 
  {
    char c = Serial.read();
    Serial.println(c); //for debug
    
    if ( c == 'o') 
    {
      AudioPlayer.play(filename5, 0); //playing file on channel 0
      Serial.println("playing audio file on channel 0");
    }
    if ( c == 'p') 
    {
      AudioPlayer.play(filename5, 1); //playing file  on channel 1
      Serial.println("playing audio file on channel 1");
    }
      if ( c == 'k') 
    {
      AudioPlayer.play(filename5, 2); //playing file on channel 2
      Serial.println("playing audio file on channel 2");
    }
      if ( c == 'l') 
    {
      AudioPlayer.play(filename5, 3); //playing file on channel 3
      Serial.println("playing audio file on channel 3");
    }    
  } 
}

 

 

Demo Video

Here is a demo of WAV file playing from the SD card on the Xiao Expansion board.  I'm not getting much volume from the little speaker, so I'll need to look into that.  And certainly not hifi sound image.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

 

The final step will be to get the WAV file to play from the external 2MB flash memory that I added to the QT Py.

 

 

Links to previous blogs

A QT Py Christmas

A QT Py Christmas - add Cap Sense Buttons

Attachments:
Play this audio clip
  • Sign in to reply
  • sobellinni
    sobellinni over 3 years ago

    /*
    I made my own sound driver  because the one in this blog does not  work 

    this sketch will play random files  labeled with

    numbers  */



    #include <Arduino.h>
    #include <SPI.h>
    #include "SdFat.h"
    #include "Adafruit_SPIFlash.h"
    #include "Adafruit_ZeroTimer.h"

    // Uncomment to run example with custom SPI and SS e.g with FRAM breakout
    // #define CUSTOM_CS A5
    // #define CUSTOM_SPI SPI

    #if defined(CUSTOM_CS) && defined(CUSTOM_SPI)
    Adafruit_FlashTransport_SPI flashTransport(CUSTOM_CS, CUSTOM_SPI);

    #elif CONFIG_IDF_TARGET_ESP32S2
    // ESP32-S2 use same flash device that store code.
    // Therefore there is no need to specify the SPI and SS
    Adafruit_FlashTransport_ESP32 flashTransport;

    #else
    // On-board external flash (QSPI or SPI) macros should already
    // defined in your board variant if supported
    // - EXTERNAL_FLASH_USE_QSPI
    // - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI
    #if defined(EXTERNAL_FLASH_USE_QSPI)
    Adafruit_FlashTransport_QSPI flashTransport;

    #elif defined(EXTERNAL_FLASH_USE_SPI)
    Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS, EXTERNAL_FLASH_USE_SPI);

    #else
    #error No QSPI/SPI flash are defined on your board variant.h !
    #endif
    #endif
    Adafruit_SPIFlash flash(&flashTransport);

    // file system object from SdFat
    FatFileSystem fatfs;
    File myFile;


    // This example can have just about any frequency for the callback
    // automatically calculated!
    float freq = 8000.0; // 8 KHz
    int phase = 0;
    int fnote = 0;
    int val =0;
    int writebufcounter = 0;
    int writebufpointer = 0;
    int readbufcounter = 0;
    int readbufpointer = 0;
    byte bval=0;
    byte fval=0;
    byte soundbuffer[2][256];
    bool bufferfull[2];


    int soundbytes;


    int whichbuf =0;
    int filenumb;
    char filenumber[10];
    char filename[10];

    // timer tester
    Adafruit_ZeroTimer zerotimer = Adafruit_ZeroTimer(3);

    void TC3_Handler() {
    Adafruit_ZeroTimer::timerHandler(3);
    }

    // the timer callback

    void TimerCallback0(void){
    digitalWrite(9, LOW);
    //read buffer till all read and make it not full
    if (bufferfull[readbufpointer]==true){
    bval = soundbuffer[readbufpointer][readbufcounter];
    readbufcounter++;
    if (readbufcounter>255){

    bufferfull[readbufpointer] = false; //enable file to write
    if (readbufpointer==0) digitalWrite(6, LOW);
    if (readbufpointer==1) digitalWrite(7, LOW);

    readbufcounter = 0;
    readbufpointer = 1 & (readbufpointer ^1); // point to the other buffer
    }


    }
    else {
    bval = 0x80;
    }




    val = ((int)bval<<2)&0x3ff;
    analogWrite(0, val );

    digitalWrite(9, HIGH);
    }


    void play(const char *fname) {
    bufferfull[0]=false;
    bufferfull[1]=false;
    writebufpointer =0;
    readbufpointer =0;
    digitalWrite(8, HIGH);
    myFile = fatfs.open(fname);
    if (myFile) {
    Serial.println(fname);
    Serial.println(myFile.size());
    Serial.println("b4readin");
    }

    for(int i =0; i<44; i++)
    myFile.read(); // eliminate header
    Serial.println("counted44");

    while (soundbytes = myFile.available()) { // find out how may bytes are still available

    if (bufferfull[writebufpointer]==false){ //buffer available
    fval= myFile.read();
    //Serial.write(fval);
    soundbuffer[writebufpointer][writebufcounter] = fval; // fill sound buffer
    writebufcounter++;
    if (writebufcounter>255){
    bufferfull[writebufpointer] = true; //stop file to write
    if (writebufpointer==0) digitalWrite(6, HIGH);
    if (writebufpointer==1) digitalWrite(7, HIGH);
    writebufcounter = 0;
    writebufpointer = 1 & (writebufpointer ^1);; // point to the other buffer
    }
    }
    }

    // file is done reading, see if there is still a buffer to fill

    while (bufferfull[writebufpointer]=false){

    soundbuffer[writebufpointer][writebufcounter] = 0x80; // fill sound buffer with audio 0
    writebufcounter++;
    if (writebufcounter>255){
    bufferfull[writebufpointer] = true;
    if (writebufpointer==0) digitalWrite(6, HIGH);
    if (writebufpointer==1) digitalWrite(7, HIGH);
    writebufcounter = 0;
    }
    //DONE
    }
    digitalWrite(8, LOW);
    }


    void setup() {

    // Open serial communications and wait for port to open:
    Serial.begin(115200);
    while (!Serial) {
    delay(1); // wait for serial port to connect. Needed for native USB port only
    }

    Serial.print("Initializing Filesystem on external flash...");

    // Init external flash
    flash.begin();

    // Open file system on the flash
    if ( !fatfs.begin(&flash) ) {
    Serial.println("Error: filesystem is not existed. Please try SdFat_format example to make one.");
    while(1) yield();
    }

    Serial.println("initialization done.");
    pinMode(6, OUTPUT);
    pinMode(7, OUTPUT);
    pinMode(8, OUTPUT);
    pinMode(9, OUTPUT);

    tc_clock_prescaler prescaler = TC_CLOCK_PRESCALER_DIV1;

    zerotimer.enable(false);
    zerotimer.configure(prescaler, // prescaler =1
    TC_COUNTER_SIZE_16BIT, // bit width of timer/counter
    TC_WAVE_GENERATION_MATCH_PWM // frequency or PWM mode
    );

    zerotimer.setCompare(0, 6000); // 48000000/8000 = 6000
    zerotimer.setCallback(true, TC_CALLBACK_CC_CHANNEL0, TimerCallback0);
    zerotimer.enable(true);
    }

    void loop() {

    filename[0]=0;
    filenumb = random(36);
    itoa(filenumb,filenumber,10);

    strcat(filename,filenumber);
    strcat(filename, ".wav");



    Serial.println(filename);

    play(filename);
    delay(1000);

    }

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube