ECE 2036 Lab 4 – Build an mbed iPod

 

Apple’s  iPod Classic

 

In this laboratory assignment, the mbed inventor’s kit parts will be used to build a portable music player. There is an existing waveplayer hello world program that will play a wave file from the SD card using the speaker with the driver circuit connected to mbed’s D/A output described in the waveplayer section at https://mbed.org/users/4180_1/notebook/using-a-speaker-for-audio-output/. Import the waveplayer hello world code example as your initial template. Watch the You Tube video on that web page to hear how it sounds. It would probably be a good idea to run the waveplayer hello world program first to verify that the SD card and speaker hardware is setup correctly. Don’t forget that a “sample.wav” wave file is needed on the SD card. The speaker will not be quite as loud using the D/A output as the earlier lab that used the PWM output to drive the speaker.

 

In the wave_player class code used, a timer producing periodic interrupts (mbed Ticker API) is used to control the audio sample rate. These timer interrupts periodically activate a fast interrupt routine that outputs the next D/A value from a buffer and returns, while the wave player routine is reading and buffering audio sample values to be sent later to the D/A from a *.wav format file on the SD card.  This code is already included in the waveplayer hello world example. Your assignment will be to create and add a user interface that allows you to select which song to play on the LCD, play/stop, and adjust the volume using the pushbuttons.

 

It might be a good idea and save time to use the same pin assignments and breakout board placement as the earlier mbed lab. The Shiftbrite is not used, but the other parts (LCD, micro SD, speaker with driver, and pushbuttons) from the earlier lab are used. You can cut and paste the pushbutton and setup LCD code from the earlier mbed lab and add it to the waveplayer helloworld example. Don’t forget the TextLCD and PinDetect *.h and *.cpp files need to be copied over into the project, and the includes for them will need to be added in main.cpp along with the code to setup and activate the pushbutton callbacks. Copy and paste can be used for entire C++ source files in the right column in the mbed compiler window.

 

The player allows a user to select one of the files in the myMusic directory to play using pushbuttons and the TextLCD. A sample code snippet is provided below that reads the filenames from this directory into a C++ vector of strings and prints all of the filenames out to the PC terminal application window. There is an existing C++ structure setup, dirent, that can be used to read directory entries that makes the code relatively short. This code assumes that the SD card file system is already setup in the program. This code shows how the filename strings can be read into a vector. It will need to be modified to display filenames individually on the LCD rather than listing them on the PC.

 

#include <vector>

……

vector<string> filenames; //filenames are stored in a vector string

 

void read_file_names(char *dir)

{

    DIR *dp;

    struct dirent *dirp;

    dp = opendir(dir);

  //read all directory and file names in current directory into filename vector

    while((dirp = readdir(dp)) != NULL) {

        filenames.push_back(string(dirp->d_name));

    }

}

……

 // read file names into vector of strings

 read_file_names("/sd/myMusic");

 // print filename strings from vector using an iterator

 for(vector<string>::iterator it=filenames.begin(); it < filenames.end(); it++)  

  {

    printf("%s\n\r",(*it).c_str());

  }

 

Several wave files will need to be placed on the SD card in the myMusic directory to provide test data. A minimum of four audio files are needed for the demo and you can select your own music or sound effect files. The Audacity audio editor is free and can be used to read in an audio MP3 or WAV file, convert it from stereo to mono, resample it to a 16Khz sample rate and export it as a 16-bit wave file. The *.wav file size is around 2MB for 1 minute of audio.

 

Some existing wave files may not play and will need to be converted using Audacity. Typically the problem is too high of a sample rate (>20Khz?) and they can be resampled at a lower rate in Audacity. The sample *.wav files can then be transferred to the micro SD card using a PC with the SD card reader adapter. Error messages are displayed with printfs to a PC terminal application window by the waveplayer code. Most errors result in no sound.

 

The bottleneck that limits the sample rate is the read time of the SD card. An mbed user in Japan has increased the SPI clock frequency in SDFileSystem.cpp from 1Mhz to 20Mhz while reading the SD card file in the SDFileSystem class and claims even higher sample rates, but it will work as is to at least 16Khz. Increasing the clock might only work on some SD cards and the breadboard jumper wires would need to be short. Mbed’s internal flash is too slow for the wave player and the external SD card must be used.

 

The existing waveplayer hello world example code in main.cpp plays using a string constant (i.e. “sample.wav”) argument for the filename, this code will need a minor modification to accept a C style string variable as the argument for the filename to play. Note that C++ strings are stored a bit differently than C strings (no null character at end), but there is a function to convert them, c_str(),  that can be found in the earlier filename example code. Don’t forget that the filename to play will need a prefix with the full path name (“/sd/myMusic/”). Also the file to play needs to be opened each time before playing and once it stops playing, it must be closed.

 

Basic WavePlayer (70%) - Use two pushbuttons to scroll forwards or backwards through the filenames string vector and display the current filename on line 1 of the text LCD (you can assume filenames are less than the 16 characters on one line in the LCD ). Do not display the filename’s “.wav” file extension on the LCD. A handy function to remove the “.wav” file extension is substr(). When advancing beyond the last filename in the vector, it should wrap around back to the beginning. A third pushbutton will be used to start playing the filename currently displayed on the text LCD. Right before the song starts playing, line 2 on the LCD displays the message “now playing”. If the play pushbutton is hit again while a song is playing it stops the player, clears line 2 on the LCD and goes back to the select a song to play state in the text LCD. When a song finishes playing, it also returns to the select a song state. It probably makes sense to setup all of the pushbuttons using a callback as was done in the earlier lab. Note: on MACs you will want to add code to ignore the extra hidden files that start with “.”.

 

Since the wave_player class C++ code is setup as a separately compiled library and linked after compilation to the main program, it is not possible to share global variables (set by pushbuttons) with main.cpp. The play/stop function variable and the volume variable for the last part will need to be passed to the wave_player class as an argument using a pointer or reference. The play method in the wave_player class will need additional arguments added to pass these. Since the play/stop value changes with pushbutton interrupts asynchronously, a pointer is needed and pass by value will not work to stop a song while it is playing. The volatile keyword is not critical on these two variables (play/stop and volume) since the main program always writes them and the wave player class only reads them. The for (slice=0… loop is a good place to break out of the player code for the stop pushbutton as it turns off interrupts and the D/A when exiting this loop.

 

Add SD card detect feature (15%) – As you may have noticed, writing files without an SD card plugged in can result in the mbed “blue LEDs of death”. It is possible to read a pin on the SD breakout board that detects when an SD card is inserted. Details on how this pin functions can be found near end of the SD card file system cookbook page. Modify the SDFileSystem class code to add another argument for the new CD pin (currently unused in code and not hooked up in hardware). Use this new pin argument in the class to set up the pin and add a new member function, SD_inserted, that returns true if an SD card is inserted and false if not. Read the handbook page on DigitalIn to see how to configure the internal pullup resistor using the mode method. Modify your main program code so that this new class member function is used when the program first starts. If there is no SD card present, print “Insert SD Card” on line 1 of the Text LCD and loop constantly checking for the SD card to be inserted. Once it is inserted, clear the message and start the player program that selects a song using the pushbuttons. You can assume that the SD card is not removed once the program passes this initial check. A short time delay will be required after the SD card is inserted to allow it to power up correctly after insertion and a call to disk_initialize() also helps to make it operate more reliably after a power on insertion.

 

Add volume control (10%) – Use the fourth pushbutton to add volume control. For faster operation, the wave_player dac_out method actually sends out an unsigned integer value (i.e., AnalogOut write_u16 method) to the D/A instead of using scaled floating point numbers. Find this line of code and add a scaling operation that multiples the D/A value by (16 - volume) and then divides the D/A value by 16 (with a shift of 4-bits right or “>>4”) as the value is sent to the D/A to adjust the volume. Note: integer operations are faster than floating point and shifts are faster than a divide. The fourth volume control pushbutton cycles through the values (0..15) and sets the volume variable to control the volume. The initial default volume setting should be full volume (i.e., no scaling). Add an argument to pass a pointer or reference to the volume variable to the play method in wave_player.cpp. The play method can copy the pointer to a global variable (in waveplayer.cpp) so that the interrupt routine, dac_out, will then be able to access it for scaling. Since the volume value changes with pushbutton interrupts asynchronously, a pointer is needed (i.e., copy by value will not work).

 

Upgrade to a Boom Box (5%) – It is possible to connect to amplified PC speakers (or use a headphone with a driver or audio amplifier circuit) using the audio jack breakout board in your parts kit. See the “Using PC speakers with mbed” section at https://mbed.org/users/4180_1/notebook/using-a-speaker-for-audio-output/. The lab TAs can loan you the extra resistor and capacitor needed.