Photon play sampled music (wav file) using DAC

Hello,
Is there an example or tutorial on using the Photon’s DAC output to play a short sound clip? I found this:

and have seen similar arduino projects that use an R-C circuit, but I’d like to take advantage of the capabilities of the Photon’s DAC.

Thanks!

I would like to see this too.

I’d like to know how too. Did you ever find out how to this?

It shouldn’t be too complicated when using SparkIntervalTimer library to push the samples in a “sort of steady” pace to the two DACs.

Try this one

//SYSTEM_THREAD(ENABLED)
SYSTEM_MODE(MANUAL)

#include "SparkIntervalTimer/SparkIntervalTimer.h"
#include <math.h>

const int MIDPOINT = 2048;                  // zenter point for DAC
const int MAX_VOL  = 1600;                  // amplitude around (+/-) center point (no more than 2047! to avoid clipping)
const int SAMPLE_PERIODE = 1000000/44100;   // µs between samples (44.1kHz)
const int SAMPLES = 50;                   
uint16_t  sine[SAMPLES];                    // one periode over 50 samples is aprox. 880Hz 

volatile int r = 0;                         // sample to play on right channel
volatile int l = 25;                        // sample to play on left channel (slightly offset)

IntervalTimer tPlayer;

void setup() {
  pinMode(DAC1, OUTPUT);
  pinMode(DAC2, OUTPUT);
  
  for (int i=0; i < SAMPLES; i++)
  { // precalc one periode sine wave
    double x = 2.0 * M_PI * i / SAMPLES;
    sine[i] = MIDPOINT + MAX_VOL * sin(x); 
  }
  
  tPlayer.begin(playSample, SAMPLE_PERIODE, uSec);
}

void loop() {
}

void playSample()
{
    analogWrite(DAC1, sine[r++]);
    analogWrite(DAC2, sine[l++]);
    
    r %= SAMPLES;
    l %= SAMPLES;
}
1 Like

cool I’ll try that.

right now I am getting some results wiht this:

#include <application.h>
#include <soundsample.h> //an array with sound data created with wav2c

#include "SimpleRingBuffer.h"

#define SPEAKER_PIN DAC1
#define AUDIO_BUFFER_MAX 8192
#define AUDIO_TIMING_VAL 125 /* 8,000 hz */

SimpleRingBuffer audio_buffer

float _volumeRatio = 0.2;

void playRxAudio() {
    unsigned long lastWrite = micros();
    unsigned long now, diff;
    int value;

    while (audio_buffer.getSize() > 0) {
        // ---
        //map it back from 1 byte to 2 bytes
        //map(value, fromLow, fromHigh, toLow, toHigh);
        //value = map(rxBuffer[rxBufferIdx++], 0, 255, 0, 4095);

        //play audio
        value = audio_buffer.get();
        value = map(value, 0, 255, 0, 4095);
        value = value * _volumeRatio;

        now = micros();
        diff = (now - lastWrite);
        if (diff < AUDIO_TIMING_VAL) {
            delayMicroseconds(AUDIO_TIMING_VAL - diff);
        }

        //analogWrite(SPEAKER_PIN, rxBuffer[rxBufferIdx++]);
        tone(SPEAKER_PIN, value);
        lastWrite = micros();
    }
}

void setup(){
    pinMode(SPEAKER_PIN, OUTPUT);
    audio_buffer.init(AUDIO_BUFFER_MAX);

    // setup audio buffer and load sound values into it
    for (int i = 0; i < sounddata_length; i++) { //sounddata_lenght comes from sound_sample.h
        // ---
        //map it back from 1 byte to 2 bytes
        //map(value, fromLow, fromHigh, toLow, toHigh);
        audio_buffer.put(sounddata_data[i]);
    }
}


void loop() {
    playRxAudio();
}

I wonder if I would get improvesd samplrate when using the IntervalTimer lib like you do.

only problem is I cant get enough gain out of it. With the tone() and noTone() it is much louder. And I don’t want to build an amp :stuck_out_tongue:

hi
how are you?
i have one wave file with C language.
i would like to play this c type of wave file with DAC on particle.
but i don't know how to do it.
like this

#define NUM_ELEMENTS 110250

char wave_data[] = {
128, 126, 126, 128, 128, 128, 128, 126, // 0-7
126, 126, 126, 126, 126, 126, 128, 128, // 8-15
128, 128, 128, 128, 128, 128, 128, 128, // 16-23
128, 128, 128, 126, 126, 126, 126, 126, // 24-31
126, 126, 126, 126, 126, 126, 126, 126, // 32-39
126, 126, 126, 126, 126, 128, 128, 128, // 40-47
128, 128, 128, 128, 128, 128, 130, 130, // 48-55
...}

can you help me?

i found one DAC code
can i use this one?

#include "speaker.h"

SYSTEM_THREAD(ENABLED);

/* Configure audio output */
uint16_t bufferSize = 128;
Speaker speaker(bufferSize);

uint16_t audioFrequency = 22050; // Hz

/* Configure signal parameters */
uint16_t amplitude = 2000; // signal goes from 0 to 65535
uint16_t frequency = 1000; // Hz
uint32_t audioSignal = 0; // current value of the audio signal

/* Generate a sawtooth signal into the Speaker buffer.
 * A sawtooth goes up until a limit, then goes back to 0.
 */
void generateSawtooth(uint16_t *buffer)
{
  uint32_t increment = ((uint32_t) frequency * amplitude) / audioFrequency;

  for (uint16_t i = 0; i < bufferSize; i++) {
    buffer[i] = audioSignal;
    audioSignal += increment;
    if (audioSignal > amplitude) {
      audioSignal -= amplitude;
    }
  }
}

/* Particle function to change the volume of the sawtooth signal by
 * changing the maximum value.
 */
int changeAmplitude(String arg)
{
  long num = arg.toInt();
  if (num >= 0 && num < 65535)
  {
    amplitude = (uint16_t) num;
    if (audioSignal > amplitude) {
      audioSignal = 0;
    }
  }
  return amplitude;
}

/* Particle function to change the sawtooth frequency */
int changeFrequency(String arg)
{
  long num = arg.toInt();
  if (num > 0 && num < audioFrequency)
  {
    frequency = (uint16_t) num;
  }
  return frequency;
}

/* Set up the speaker */
void setup() {
  Particle.function("amp", changeAmplitude);
  Particle.function("freq", changeFrequency);

  // Write initial data in the audio buffer. Not mandatory, but if you
  // don't do this you'll start with 1 buffer of silence.
  generateSawtooth(speaker.getBuffer());

  // Start the audio output
  speaker.begin(audioFrequency);
}

/* Generate more audio data when needed */
void loop() {
  // Call ready as often as possible to avoid the speaker playing the same buffer multiple times.
  if (speaker.ready()) {
    // Write new audio into the buffer returned by getBuffer
    generateSawtooth(speaker.getBuffer());
  }
}

The library you are using there was created by @jvanier and some documentation about how to use it can be found here

BTW, if you don’t intend to modify your wave_data I’d use const char instead as this saves RAM space.

yes, i already saw this link.
but i don’t know how to use this.
if you are possible , i hope you will help me.
thank you

The example code does fill a buffer with a sawtooth signal on the fly.
Each time the code calls generateSawtooth(speaker.getBuffer()); the signal data will be regenerated and stored in the provided buffer.
Since you are not intending to use the sawtooth signal, you won’t need to call that.
You would only need to provide your wave_data[] once and let it play over and over.

BTW, the speaker object wants the wave signal as 16bit PCM (uint16_t 0…65535) and not 8bit (char 0…255) what your wave_data[] currently is.

sorry, i didn’t understand correctly yet.
can you explain with simple code ?
thank you for your help

This isn’t tested, but in principle this should do the job

SYSTEM_THREAD(ENABLED);

#include "speaker.h"

const uint16_t wave_data[] = {
....
};

uint16_t audioFrequency = 22050; // Hz
Speaker  speaker(sizeof(wave_data));

void setup() {
  memcpy(speaker.getBuffer(), wave_data, sizeof(wave_data));
  speaker.begin(audioFrequency);
}

Olena, did you have any success with this ??

Sorry to resurrect an old thread, but what is the example from the speaker library example supposed to sound like? I’ve been having a fair amount of difficulty getting it to work. As of right now, I’m getting a sound similar to “SHHH… Thump Thump.” Can someone confirm that this is correct? I have a larger aspiration to build my own wifi doorbell, but wasn’t a fan of the DFPlayer audio solution.

Going a route without DFPlayer is proving to be more complicated than I expected…

I tried converting my own audio file into the uint16_t format, and after whittling it down quite a bit, I received the same “SHHH… Thump Thump.” that I was getting before. This tells me that either my hardware is wired up incorrectly (I don’t think so), or there’s something wrong with the code (more likely). I’ll keep playing with it, but if anyone is able to offer some assistance, I would appreciate it!

Thanks!

How did you obtain your sound data?
You could try filling buffer with a sine wave and play that - what do you hear then?
How have you wired your setup?
What speaker are you trying to drive?
Have you got an amplifier?

Your symptoms could fit the effect of having a too low impedance speaker straining the DAC beyond its capabilities.


Update:
I have now tested the sample sound provided with the library and it plays some “didum, didum, …” sound when feeding the DAC signal to some earphones - obvioulsy having GND connected too.

The sound data was obtained in the same manner that the library creator used. I used a wav file, converted it to RAW using Audacity, then converted that with your html that was packaged with the library. I was able to finally get the expected sound out of the speaker by continuing to remove data from my sound variable, but I’m not really sure why. This is my setup -

Particle Photon
LM386 amplifier module (unsure of ohm rating)
4 ohm speaker (measured to actually be 3 ohms)

The amp is powered from a 5v usb input to the photon, and the signal is connected to the DAC pin. Really, that’s all my wiring.

I also have an SD card reader wired up to the photon (pins D1-D4), but it’s not being used at the moment. I believe that the issue was a compiler issue, or possibly too low impedance of a speaker. I did encounter several errors when attempting to compile, and was only successfully able to flash the photon once I removed 80% of my original sound data.

What was the original size of your sound sample?

About your SD card: When using D1…D4 you are using D1 as SlaveSelect pin? If not, coult it be that you are using soft SPI? That wouldn’t be the best option here.
You could use SPI1 (D2…D4 with a freely configurable SS pin), but that’d still not allow for the maxium speed.
Max speed can be achieved via SPI (A3…A5 with free choice for SS) - unless you really need stereo sound.

My code doesn’t reference the SD card at all at the moment, but yes, D1 is used as the SlaveSelect. I’m still learning how this all works, but I thought I had read that the photon has 2 options for SPI and this was the faster one? I can certainly switch it to the analog inputs if that is the faster option. My only desire is to have the audio play as soon as the switch is triggered. I’m not sure that the choice of SPI will make that huge of a difference for playback timing.

Also, I tried bypassing my amp and hooking the speaker up to the DAC pin directly, and sadly didn’t get any better results than before. If anything I could hear something slightly different in the “SHHH…” part, but it still mostly sounded like static.

The Photon has two SPI interfaces available but the primary one (SPI) is the faster one
https://docs.particle.io/reference/device-os/firmware/photon/#setclockdividerreference
image

Can you post your converted audio data (or send a link to download the file)?

For your wiring: Event if it's something really simple can you still draw up a schematic and post that?
Images are so much easier to grasp and won't be misinterpreted as easily compared to verbal descriptions.