| 
  • If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • You already know Dokkio is an AI-powered assistant to organize & manage your digital files & messages. Very soon, Dokkio will support Outlook as well as One Drive. Check it out today!

View
 

Documentation

Page history last edited by Tommy Huang 9 years, 8 months ago

AMBIENT MUSIC PLAYER


 

  


 

I'll be completely honest, my project's demographic was me, but I suppose many others can say the same.

I wanted an mp3 player that was ambient and would sort of relax while I did work; it is heavily aesthetics

oriented. The main issue I had with building this player is the fact that my parts came in on the Monday of

the last week; in the end, I had only six days to build my player!

 

Wow I didn't notice how similar my player was to one of the example lab documentations. Anyways, I

wanted my player to be very sleek, clean and somewhat minimalist. ... and I just read what the other guy

wrote and it's almost the same. Great minds think alike, I guess?

 

design

-----

 

Paper prototype

-----

Well to be honest... my paper prototype was kind of just a square of paper. I had no idea how to put

what I envisioned as a sheet of paper :D


 


 

parts (bold are purchased)

-----

https://www.adafruit.com/products/1086 | arduino micro

https://www.adafruit.com/products/1586 | neopixel ring [24]

https://www.adafruit.com/products/987 | audio amplifier

https://www.adafruit.com/products/1669 | a pair of speakers

https://www.adafruit.com/products/1063 | electret mic & amplifier

https://www.adafruit.com/products/254 | just to make it easier... microsd breakout

https://www.sparkfun.com/products/10608 | mp3 breakout board

> uhhhhhhhhhhhhhh where do you buy this? | HEF 4050 BP

> misc. | perfboarding material, 1000uf capacitor, wires, solder

 

p.s. don't ship with USPS... so much stress from late shipment

 

case 

-----

> 2 pieces of 6x6 frosted white acrylic

> standoffs

> screws and nuts

> a laser cutter, preferably

> hot glue

 

wiring

-----

honestly I'm pretty dang afraid of messing up my player by taking it apart so I'll put the wiring that

isn't already written in labs 5 and 6 here:

 

change audio decoder chip select (CS) to arduino pin A5 
change audio decoder right to MAX98306 (amp) R+
change audio decoder left to MAX98306 (amp) L+
change audio decoder gbuf to MAX98306 R- & L-
add neopixel data input to arduino pin 3 
add neopixel VCC to 1000uf capacitor (+)
add neopixel GND  to 1000uf capacitor ( - ) 
add 1000uf capacitor (+)  to 5V 
add 1000uf capacitor ( - )   to GND 
add MAX4466 (mic amp) output  to arduino pin A0 
add MAX4466 PWR  to

3.3V and arduino AREF

add  MAX4466 GND  to  GND
add MAX98306 LOUT- & ROUT- to speaker-
add MAX98306 LOUT & ROUT to speaker+

 

references

-----

lab 5

lab 6

 

microphone reading

 

code

-----

GitHub for readability


 

final note before I paste the code into this page

Thank you all so much for this amazing summer. This class is my favorite class I've ever taken!


 

/*

 * originally based on frank zhao's player: http://frank.circleofcurrent.com/

 * utilities adapted from previous versions of the functions by matthew seal.

 *

 * (c) 2011, 2012 david sirkin sirkin@cdr.stanford.edu

 *                & akil srinivasan akils@stanford.edu

 */

 

// ---- includes and defines ------------------------------------------------

 

// first step is to include (arduino) sd, eeprom and (our own) mp3 libraries.

 

#include <SD.h>

#include <EEPROM.h>

 

#include <mp3.h>

#include <mp3conf.h>

#include <Adafruit_NeoPixel.h>

 

#define PIN 3

// include the adafruit pcd8544 & gfx libraries for a nokia 5110 graphic lcd.

//----------------------------------------

// Parameter 1 = number of pixels in strip

// Parameter 2 = Arduino pin number (most are valid)

// Parameter 3 = pixel type flags, add together as needed:

//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)

//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)

//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)

//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)

Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);

 

// setup microsd and decoder chip select pins, and the decoder-specific pins.

 

#define sd_cs         17        // 'chip select' line for the microsd card

 

#define mp3_cs        A5        // 'command chip select' connect to cs pin

#define mp3_dcs       A1        // 'data chip select' connect to bsync pin

#define mp3_rst       -1        // 'reset' connects to decoder's reset pin

#define mp3_dreq      A2        // 'data request line' connect to dreq pin

 

// now assign pins for the graphic lcd (carried over from the etch-a-sketch).

 

#define lcd_clk        7        // 'serial clock' connect to lcd's clk pin

#define lcd_din        6        // 'serial data input' connects to din pin

#define lcd_dc         5        // 'data/command input' connect to d/c pin

#define lcd_cs        -1        // 'slave chip select' connects to  cs pin

#define lcd_rst        4        // 'reset' connects to graphic lcd rst pin

 

// 'read_buffer' is the amount of data read from microsd and sent to decoder.

// it's probably best to keep this a factor of 2, up to about 1kb (2kb is the

// max). you might change this if you experienced skips during song playback.

 

#define read_buffer  128        // size (bytes) of the microsd read buffer

#define mp3_vol      237        // default volume. range min=0 and max=254 (a

                                // good range is from 200-235)

 

// file names are 13 bytes max (8 + '.' + 3 + '\0'), and the file list should

// fit into the eeprom. for example, 13 * 40 = 520 bytes of eeprom are needed

// to store a list of 40 songs. if you use shorter file names, or if your mcu

// has more eeprom, you can change these.

 

#define max_name_len  13

#define max_num_songs 40

 

// id3v2 tags have variable-length song titles. that length is indicated in 4

// bytes within the tag. id3v1 tags also have variable-length song titles, up

// to 30 bytes maximum, but the length is not indicated within the tag. using

// 60 bytes here is a compromise between holding most titles and saving sram.

 

// if you increase this above 255, look for and change 'for' loop index types

// so as to not to overflow the unsigned char data type.

 

#define max_title_len 60

 

const int sampleWindow = 3; // Sample window width in mS (50 mS = 20Hz)

unsigned int sample;

 

// ---- global variables ----------------------------------------------------

 

// instantiate a graphic lcd object using the pins that we #define'd earlier.

// comment out the graphics lines to save memory if you're not using the lcd.

 

//Adafruit_PCD8544 lcd = Adafruit_PCD8544(lcd_clk, lcd_din, lcd_dc, lcd_cs, lcd_rst);

 

// 'File' is a wrapper of the 'SdFile' data type from the sd utility library.

 

File sd_file;                   // object to represent a file on a microsd

 

// store the number of songs in this directory, and the current song to play.

 

unsigned char num_songs = 0, current_song = 0;

 

// an array to hold the current_song's file name in ram. every file's name is

// stored longer-term in the eeprom. this array is used in 'sd_file.open()'.

 

char fn[max_name_len];

 

// an array to hold the current_song's title in ram. it needs 1 extra char to

// hold the '\0' that indicates the end of a character string. the song title

// is found in 'get_title_from_id3tag()'.

 

char title[max_title_len + 1];

 

// the program runs as a state machine. the 'state' enum includes the states.

// 'current_state' is the default as the program starts. add new states here.

 

enum state { DIR_PLAY, MP3_PLAY, PAUSED };

state current_state = DIR_PLAY;

 

//---- module functions -----------------------------------------------------

 

// you must open a song file that you want to play using 'sd_file_open' prior

// to fetching song data from the file. you can only open one file at a time.

void sd_file_open() {  

  // first, find the file name (that's stored in eeprom) of the current song.

  get_current_song_as_fn();

 

  // then open the file using the name we just found (stored in 'fn' global).

  sd_file = SD.open(fn, FILE_READ);

 

  // find the current song's title tag (if present) then print it to the lcd. 

  print_title_to_lcd();

}

 

// read a number of bytes from the microsd card, then forward them to the Mp3

// library's 'play' function, which streams them out to the decoder chip.

void mp3_play() {

  unsigned char bytes[read_buffer]; // buffer to read and send to the decoder

  unsigned int bytes_to_read;       // number of bytes read from microsd card

 

  // first fill the 'bytes' buffer with (up to) 'read_buffer' count of bytes.

  // that happens through the 'sd_file.read()' call, which returns the actual

  // number of bytes that were read (which can be fewer than 'read_buffer' if

  // at the end of the file). then send the retrieved bytes out to be played.

 

  // 'sd_file.read()' manages the index pointer into the file and knows where

  // to start reading the next batch of bytes. 'Mp3.play()' manages the index

  // pointer into the 'bytes' buffer and knows how to send it to the decoder.

  bytes_to_read = sd_file.read(bytes, read_buffer);

  Mp3.play(bytes, bytes_to_read);

 

  // 'bytes_to_read' is only smaller than 'read_buffer' when the song's over.

  if (bytes_to_read < read_buffer) {

    sd_file.close();

 

    // if we've been in the MP3_PLAY state, then we want to pause the player.

    if (current_state == MP3_PLAY) {

      current_state == PAUSED;

    }

  }

}

 

// continue to play the current (playing) song, until there are no more songs

// in the directory to play. 2 other sd library methods (that we haven't used

// here) can help track your progress while playing songs: 'sd_file.size()' &

// 'sd_file.position()'. you can use these to show say, the percent of a song

// that has already played.

 

void dir_play() {

  if (sd_file) {

    mp3_play();

  }

  else {

    // since 'sd_file' isn't open, the recently playing song must have ended.

    // increment the index, and open the next song, unless it's the last song

    // in the directory. in that case, just set the state to PAUSED.

 

    if (current_song < (num_songs - 1)) {

      current_song++;

      sd_file_open();      

    }

    else {

      current_state = PAUSED;

    }

  }

}   

 

// ---- setup and loop ------------------------------------------------------

 

// setup is pretty straightforward. initialize serial communication (used for

// the following error messages), mp3 library, microsd card objects, then the

// graphic lcd. then open the first song in the root library to play.

 

void setup() {

  analogReference(EXTERNAL);

 

  strip.begin();

  strip.show(); // Initialize all pixels to 'off'

 

  attachInterrupt(2, interrupt_Skip, RISING);

  attachInterrupt(3, interrupt_Back, RISING); 

  attachInterrupt(4, interrupt_Pause, RISING);

 

  // write a 0 to all 512 bytes of the EEPROM

  for (int i = 0; i < 512; i++)

    EEPROM.write(i, 0);

 

  // initialize the mp3 library, and set default volume. 'mp3_cs' is the chip

  // select, 'dcs' is data chip select, 'rst' is reset and 'dreq' is the data

  // request. the decoder sets the 'dreq' line (automatically) to signal that

  // its input buffer can accommodate 32 more bytes of incoming song data.

 

  // the decoder's default state prevents the spi bus from working with other

  // spi devices, so we initialize it first.

  Mp3.begin(mp3_cs, mp3_dcs, mp3_rst, mp3_dreq);

  Mp3.volume(mp3_vol);

 

  // initialize the microsd (which checks the card, volume and root objects).

  sd_card_setup();

 

  // putting all of the root directory's songs into eeprom saves flash space.

  sd_dir_setup();

 

  // the program is setup to enter DIR_PLAY mode immediately, so this call to

  // open the root directory before reaching the state machine is needed.  

  sd_file_open();

}

 

// the state machine is setup (at least, at first) to open the microsd card's

// root directory, play all of the songs within it, close the root directory,

// and then stop playing. change these, or add new actions here.

 

// the DIR_PLAY state plays all of the songs in a directory and then switches

// into PAUSED when done. the MP3_PLAY state plays one specific song and then

// switches into PAUSED. this sample player doesn't enter the MP3_PLAY state,

// as its goal (for now) is just to play all the songs. you can change that.

 

void loop() {

  switch(current_state) {

    case DIR_PLAY:

      dir_play();

      break;

 

    case MP3_PLAY:

      mp3_play();

      break;

 

    case PAUSED:

      break;

  }

 

  static int i = 0;

   unsigned long startMillis= millis();  // Start of sample window

   unsigned int value = 0;   // peak-to-peak level

 

   unsigned int signalMax = 0;

   unsigned int signalMin = 1024;

 

   while (millis() - startMillis < sampleWindow) {

      sample = analogRead(0); 

      if (sample < 1024)  // toss out spurious readings

      {

         if (sample > signalMax)

         {

            signalMax = sample;  // save just the max levels

         }

         else if (sample < signalMin)

         {

            signalMin = sample;  // save just the min levels

         }

      }

   }

 

   value = map(signalMax - signalMin, 0, 1023, 0, 128);

   if (value < 32){

     value = value/3;

   }

   if (32 <= value < 64){

     value = value/2;

   }

   strip.setPixelColor(i, value*.9, value/2, 0);

   strip.show();

   if (i == 23){

     i=0;

   }

   else {

     i++;

   }

}

 

void pause_play() {

  if (current_state == DIR_PLAY){

    current_state = PAUSED;

  }

  else{

    current_state =  DIR_PLAY;

  }

}

 

void interrupt_Pause() {

  static unsigned long last_interrupt_time = 0;

  unsigned long interrupt_time = millis();

  // If interrupts come faster than 200ms, assume it's a bounce and ignore

  if (interrupt_time - last_interrupt_time > 200) {

    pause_play () ;

  }

  last_interrupt_time = interrupt_time;

}

 

void interrupt_Skip() {

  static unsigned long last_interrupt_time = 0;

  unsigned long interrupt_time = millis();

  // If interrupts come faster than 200ms, assume it's a bounce and ignore

  if (interrupt_time - last_interrupt_time > 200) {

    if (current_song != num_songs - 1) {

       Mp3.cancel_playback();

       sd_file.close();

       current_song++;

       sd_file_open();

    }

    last_interrupt_time = interrupt_time;

  }

}

 

void interrupt_Back() {

  static unsigned long last_interrupt_time = 0;

  unsigned long interrupt_time = millis();

  // If interrupts come faster than 200ms, assume it's a bounce and ignore

  if (interrupt_time - last_interrupt_time > 200) {

      if (current_song != 0) {

        Mp3.cancel_playback();

        sd_file.close();

        current_song--;

        sd_file_open();

    }

  }

  last_interrupt_time = interrupt_time;

}

 

Comments (1)

jane.zzjiang@... said

at 8:17 pm on Aug 19, 2014

We love your idea of an ambient music player. It’s well packaged and beautiful constructed.

We do like your design, sleek, clean and minimalist as you said. You have an artistic vision. The perfboard with the buttons was well designed. Also you have smartly placed every device, which makes us feel it’s a real art. The MP3 does change the lights along with the music volume, creating a beautiful ambient. Good job.

We really enjoy your documentation. You are very good at sketching/prototyping. The design/state diagram/Verplank diagram are well drawn. We appreciate you pointing out the different wiring of your circuits, which gives others a clear guidance. Also thank you for making your codes well commented, which made it portable and readable for others. It would be nice if you could tell us your strategy to lit the LEDs and how you like your product in the document.

Overall, great work this quarter and hope you had fun.

Best,
Jane, David, Nik, Samyuktha, Xinyi

You don't have permission to comment on this page.