MP3 Player With Network Interface By Michael Somersmith School of Information Technology and Electrical Engineering, University of Queensland.
Abstract Compressing raw audio with the MP3 standard can reduce the storage space required by approximately one twelfth. [17] This has made it possible to play audio from devices with limited storage space, for example mobile phones, PDA’s and flash based MP3 players. Their small size also makes them more convenient and flexible to use than using traditional audio CDs.
through standard track controls: play/pause, stop, next and previous track. The player decodes these and sends them as commands to the server which will either modify the data stream accordingly (i.e. Stop or pause) or go back to the Winamp play list and open a new file to stream (next and previous). The final implementation of the player operates as you would hope it to.
Acknowledgements Rebecca Somersmith for her love and support. Peter Sutton for allowing me to take on a thesis topic that I enjoyed, and providing the direction and organisation that I sometimes required. To all the musicians that make the music.
Table of Contents Chapter 1 – Introduction ............................................................................................... 2 1.1 Motivation............................................................................................................... 2 1.2 Solution................................................................................................................... 3 1.3 Thesis overview .............................................................................................
5.3.6 Playing MP3 data........................................................................................... 35 5.3.7 Making the music sound as the artist intended .............................................. 38 5.3.7.1 Speeding up the network transfer ........................................................... 40 5.3.7.2 Removing clicks from the playback ....................................................... 41 5.3.8 Playing more than one song........................................................
List of figures Figure 1 The network layers [1] ....................................................................................... 7 Figure 2 The AudioTron [3] ............................................................................................. 8 Figure 3 Functional diagram of the AudioTron [3] .......................................................... 9 Figure 4 Motorola SimpleFi [4.].......................................................................................
Chapter 1 – Introduction 1.1 Motivation MP3s are quickly becoming a very popular format for audio storage. As a result, the number of MP3 players available on the market is increasing rapidly. Most of these players require the data to be stored locally on the player and the data is usually transferred onto the player from a computer. Though there are many players now available on the market most people still listen to MP3s played through their computer.
ideal for playback as a stream of data remote from where they are stored. This is not a new idea; it is commonly done on a PC, where people can play MP3s that are store on other computers on a network, but has been unavailable for playback away from computers. Finding a solution to this spatial problem was the intention of the current thesis, i.e. to build a stand alone audio device capable of playing MP3 data that is stored on and retrieved from network computers. 1.
By the completion of the thesis, the player was operating as you would hope it to. It can play MP3s that are stored anywhere on the network, whether the data be on the computers’ hard drives or on a CD in their CDROM drives. All sample rates are supported and all bit rates can be played up to 250kbbs, including variable bit rates. The user can navigate through the play list using the controls on the player regardless of whether it is currently playing.
Chapter 4 explains how the development platform was chosen for the project. The development platform for this project was a microcontroller based solution with networking and MP3 decoding capabilities. Chapter 5 outlines how the software was developed on the development platform and a Windows PC. The development flows through from selecting the operating system with TCP extensions to a fully functional MP3 player.
Chapter 2 – Background 2.1 What are MP3s? MP3 stands for MPEG Audio Layer 3. MPEG is the name of a working group established under the joint direction of the International Standards Organisation/International Electrotechnical Commission (ISO/IEC), whose goal is to create standards for the compression of digital video audio. More precisely, MPEG defines the syntax of audio and video format needing low data rates, as well as operations to be undertaken by decoders.
2.2 The TCP/IP protocol. TCP/IP stands for Transmission Control Protocol/Internet Protocol. It is frequently referred to as a “stack” because of the relation to the layers through which all the data must pass at both client and server ends of a data exchange. These layers refer to the different protocol implementation at different layers in the design as shown in figure 1.
2.3 Current MP3 players with a network interface. As mentioned in the introduction the inspiration for the topic predominantly came from the AudioTron [3] from Turtle Beach a PC sound card processor manufacturer. This was the first non-PC based MP3 player that could play MP3’s being stored remotely from the player.
Figure 3 Functional diagram of the AudioTron [3] To operate the AudioTron the user must install software to enable interaction with the Windows file system and internet. The same software is used to compile the song play list. The user can navigate through the songs on the player using remote control or controls on the player and the song information is shown on a display on the front panel. After the release of the AudioTron, Motorola released a similar device called SimpleFi [4].
the PC software for it to operate, reduce the autonomy of the device. The SimpleFi’s main advantage is that it can transmit the data wirelessly. Specification Supported Formats AudioTron Motorola SimpleFi All MP3 bit rates and sample All MP3 bit rates and frequencies. sample frequencies. WMA (Windows media audio) Most bit rates and sample frequencies.
Chapter 3 – Specifications The main requirement for the player to be implemented is the ability to able provide similar functionality to the AudioTron but at a fraction of the cost. Both the AudioTron and Motorola SimpleFi provide good solutions to the problem described but their cost is far too high. They are even more expensive than most of the MP3 players available that use large hard drive as local storage.
To achieve the above specifications the product was broken up into the components shown in figure 5. Graphical user interface Central Processing Line out level CD quality audio MP3 decoding Play List Management File transferring TCP/IP capabilities Interaction with windows file system Buffer Management User controls Figure 5 Overall design layout An embedded solution needs to be achieved that has TCP/IP capabilities and can interact with the Windows file system through the TCP.
Chapter 4 – The development platform When planning how to implement this device it quickly became apparent that it would require far too much time (beyond the time available for thesis) to design and construct it from raw components. So the first stage of the implementation was to investigate which components had already been developed and which components requiring development.
physical layer was primarily based on one that is compatible with the microcontroller. Important factors when deciding on a microcontroller and an operating system was for their combination to provide enough power but also a flexible environment to develop in. The selection of the microcontroller and the operating system it uses greatly affected the overall cost of the system, so it was important to choose a solution that fulfilled the requirements at the lowest cost. 4.
without worrying about handling the threads related to handling the network protocol. The operating system would then handle the stack management and the protocol layers. Keeping this in mind research was conducted looking for existing embedded solutions with network interfaces and in turn the appropriate microcontroller would be chosen. 4.2.
development boards combined with TCP/IP development kits exceed US$500. And this is only for the networking side of the project. The obvious alternative is the Ethernut project. Figure 6 Ethernut development kit [9] With a free OS, TCP/IP environment and hardware design for a development kit, it provides the most attractive solution. It is possible to manufacture the hardware from the design they freely supply and they manufacture the kits for sale.
The hardware features of the Ethernut kit are as follow: Atmel ATmega 103 processor running at 3.6864 Mhz Realtek IEEE 802.3 compliant Ethernet controller (10Mbit) RS-232 serial port 128 Kbyte of programmable flash ROM 32 Kbyte SRAM 22 input/output lines Two 8-bit and one 16-bit timer The RAM space provides 32Kbytes and it was decided that this amount was adequate. The Ethernut documentation [9] suggests that most typical applications running TCP use between 5Kbytes and 10Kbytes of RAM.
4.2.5 Embedded MP3 decoding technology The available MP3 decoder technology seems rather simple compared to the networking solutions. Most of the chips available take data serially or through parallel and output the analogue audio. Some suitable chips for this project are as follows: MICRONAS [14] make the MAS 3587F MP3 decoder chip. MP3 data is provided to it through a serial or parallel interface to a micro-controller and then it decodes it into digital audio signals.
Figure 7 The MP3 decoder board [19] 19
Chapter 5 - Implementation of software Before a decision had been reached about the Ethernut an investigation was carried out for other embedded TCP/IP software development environments. Investigation examined the possibility of combining different solutions, for example the possibility of combining a hardware solution with a separate software solution. 5.
It appears that most of the TCP/IP software discussed on the internet seemed to use TCP/IP with an operating system due to ease of development and allows for more elegant solutions. The decision that resulted from this search was to use the Ethernut solution. This was because of the hardware features described in the hardware chapter and also the features the software provided. 5.
5.3 Software development 5.3.1 Accessing Windows file system from a remote embedded environment When this project was started it was comprehensible how to interface the microcontroller to the MP3 decoder and how to manage the buffering of data, but it was difficult to conceptualise how to interact with the Windows file system from the MP3 player. Not knowing how to transfer files, navigate or find the files, and then how to open them, all from the remote device.
Windows provide an application protocol above TCP/IP called Server Message Block (SMB) or common internet file sharing protocol (CIFS), as it is called now. 5.3.1.1 The server message block (SMB) Server message block is a protocol that allows other operating systems to interact with the Windows file system. It incorporates the same multi-user read-and-write operations, locking, and file-sharing semantics that are used as if you were operating with the files from the local machine.
The design question then turned from how to implement SMB on the player to how to implement it on the PC. How does one write a Windows program that can open files from its own local storage or remote location and in turn transfer the data into a buffer? Very little was known about Windows programming and obviously a lot had to be learnt. After reading MSDN [22] about how to use SMB in Windows programming, it was found that making direct calls to the SMB layer is not needed.
The player Computer MP3 decoder Server program Graphical User interface Buffer Networking Microsoft Windows Figure 9 Overall design revision two The player makes the request to the server for the file system information. The server retrieves the required information from Windows and sends it back to the player which in turn sends the information to the GUI. The user then use the GUI to navigate the file system and the nut sends this to the server. This continues until the play list is composed.
if they are called the thread will stop and will only continue once they are complete. The second way is to create what the Nut OS calls a SoStream (Socket Stream) which is a type of Nut Device (as described in section 5.2 of this Chapter). This creates a programming model similar to what is found in UNIX. In UNIX each device is considered a file and you simply read and write to the file and the operating system will handle the communication to the device.
5.3.3 Sending files from the PC to the Ethernut To test whether the Ethernut can transfer files successfully, the server program on the PC first has to open files in Windows, read from the file into a buffer and then transmit the buffer via TCP. 5.3.3.1 Sending files through a TCP connection The ReadFile function in Windows programming reads the requested length or less if it reaches the end of the file.
5.3.3.2 Sending files over TCP with the Ethernut To test whether the same could happen through the Ethernut, the Ethernut was added into the loop. The server program on the PC now sent the packet of file data to the Ethenut first and the Ethernut inturn sent the data to the other program on the PC that reconstructed the file. This process is shown in figure 8.
5.3.4.1 The buffer system A 20 Kbyte ring buffer was implemented on the Ethernut. This translates to about 1-2 seconds worth of audio. It doesn’t sound like much but not very long is required. Although the network speed will vary, it will not vary much in the local area network. A problem may arise when the data comes from the internet, because the reliability of the connection speed is greatly decreased.
Receive pointer * Transmit pointer * End Address of the buffer Memory addresses Starting Address of the buffer Figure 13 The ring buffer The buffer works by using two pointers a transmit pointer and a receive pointer. The receive pointer is passed to the function that reads the TCP data and stores the data from this pointer forward. When the read is finished, it is determined how many bytes were put into the buffer and the pointer is moved to this next position, ready to receive more data.
Ethernut Process 2 Open file Write data to sostream Create file Read file data Ring buffer Write data to file Send packet Read data from sostream Receive packet Process 1 Figure 14 TCP file transfer through the Ethernut with a ring buffer The Ethernut does not transfer the data to process 2 until the buffer gets full. At this stage it also stops reading data from process 1 until the buffer becomes empty again.
Before running the code, the piggy back board was connected to the Ethernut. The 12 connection are outlined in table 3. Piggy back Function Connection on Ethernut connection OUTR Audio right channel out. NC OUTL Audio left channel out NC DCLK Clock for the data line PORTB PIN 1 DREQ This goes high when the decoder is PORTE PIN 6 running out of data. BSYNC This line is used to synchronize PORTB PIN 5 between bytes transferred.
Select the command register Select the data register Disable the Atmel SPI module Enable the Atmel SPI module Write each bit to the data line and toggle the clock Figure 15 Writing data to the decoder command register To write to the configuration register, the hardware SPI module of the Atmel ATmega103 cannot be used because the configuration of the data is not a standard 8-bit packet.
To test the connections of the bus and the functionality of the decoder board, the decoder was sent the test sine wave. To do this the decoder had to first be initialized by calling the initialisation function, setting up the required pins as outputs or inputs and then enables the SPI function of the Atmel mega103.
5.3.6 Playing MP3 data To play the MP3 data that is stored in the buffer through the MP3 decoder, the transfer needs to be at one byte at a time. The data transfer function provided with the Nut OS was modified to take data from the buffer that was created. The following is a flow chart of how this function works.
This function is the interrupt service routine that is called when the DREQ line of the VLSI goes high. This pin goes high whenever the buffer in the decoder is getting low. The pin that this DREQ line is connected to is an interrupt on edge pin on the Atmel that is configured to interrupt of the rising signal edge. To operate interrupts in the Nut OS, the code must register the interrupt, first by identifying what the interrupt is, and then when the Interrupt Service Routine (ISR) is called.
Receive MP3 data from server Increment the receive pointer Reset the pointer if it is past the buffer boundary No Yes Is t he buffer set t o refill Is it full Yes No Calculate the buffer size Start the decoder If buffer is empty send indiactor to HyperTerminal Send indicator to HyperTerminal and wait for there to be room in the buffer for another receive. Yes No Is the buffer full Figure 18 First program to play MP3 data The program starts by receiving the data into the buffer.
the OS indicating which interrupt you want to trigger. Once the interrupt has been triggered, the transmission of data to the decoder will start. If the buffer becomes empty or full the test program outputs this through the serial port to indicate the buffer status. The results of this were that the buffer initially got to the 10Kbytes and then audio started to play. This means that the decoder was working and it understood the data in the buffer.
Receive MP3 data from server Increment the receive pointer Reset the pointer if it is past the buffer boundary No Yes Is the buffer set to refill Is it full Yes Start the decoder No Calculate the buffer size and send it to HyperTerminal If buffer is empty send indiactor to HyperTerminal and set the buffer to refill Send indicator to HyperTerminal and wait for there to be room in the buffer for another receive.
5.3.7.1 Speeding up the network transfer Small changes to try to optimize the code were made while monitoring the buffer size that was being output to HyperTerminal. Changing the TCP packet size and shortening the delays in the decoder interface made next to no difference to the speed; the buffer emptied. After spending time making minor design changes, it was decided that a major change was required.
recovered nearly immediately so it was close to being fast enough. At this stage it was decided to change from using a 128kbbs test file to a larger 192kbbs test file. This was so that if development time was being spent trying to get the network speed right then it has to be fast enough to play all bit rates. In experimenting to try to find a little bit more speed, it was found that changing the TCP packet size now had quite an effect on the speed of transfer.
long or intense, indicating that the clicks were definitely something to do with the pointers at the end of the allocated space. The second problem with the TCP receive function and the buffer was that the buffer should not be able to store data outside the space allocated to it. That space could be assigned to other variables or data, therefore the buffer could cause errors with other parts of the program or the program could corrupt the buffer.
was just a text file that listed each file to be open on separate lines with their path in front of the filename. For example: C:\mp3\The Whitlams\Eternal Nightcap\05 - Melbourne.mp3 C:\mp3\The Whitlams\Eternal Nightcap\06 - Where's the Enemy.mp3 The server program keeps a count of what song number it is up to and this song number is passed to a function called openMp3 that was created to search through the play list and retrieves the information required as shown in figure 20.
It reads this play list file counting the number of character returns. When the number of character returns is one less than the song number, it reads the next line in the play list as the song and its respective path. In this process, the string is also converted to the UNC. When it sees a ‘\’ the program adds another one in the next string position. This string is then passed to the create file function which opens the required file.
button was pressed. The tests showed that de-bounce and the interrupts worked but the buffer for the MP3 data kept emptying. This was surprising, because there was very little extra that appeared to be happening then before when it worked. Even without pressing the buttons the buffer kept emptying, but if the de-bounce thread was removed the player now played without a problem. It was discovered that when a thread is created, it is given a default priority of 64.
Server The player Main thread Main thread Play/Pause button ISR Receive MP3 data from server No Is send enabled Play No If play Yes Yes Send UNPAUSE to the server Pause Is the buffer set to refill Is it full Read file into buffer Toggle between play and pause states Reset the pointer if it is past the buffer boundary If at end of file call openMP3 to open the next song Decode the message If UNPAUSE Enable transfer to the decoder Send PAUSE to the server Software trigger the decoder ISR
failing. The pause button was then pressed again to undo pause and the player stated producing music from the point it left off. The first test proved to be more successful then those that followed. The problems was that the buffer would sometimes empty after play was resumed. This would cause a gap in the music and a blip could be heard most times when the play was resumed. The initial reason suspected for this was the transfer was stopped at an undetermined buffer size on the player.
number as previously. This resets the file handler back to the start of the file, ready to play from the start when play is pressed. The results of this test were very puzzling. Stop did indeed stop the music but when play was pressed the music began playing, from where it was stopped for about one second then the song would start from the beginning again. This meant that old data from before stop was pressed was still being stored somewhere.
to the upper limit that was passed to the function. It returned before the full 1.5Kbyte packet was received, normally at 536 bytes. This was while the player was actually playing a song. If the player was stopped and the play was pressed again, the first packet, and often the second packet would be 1.5Kbytes and the following packet would be closer to 536bytes. It would then receive 536 bytes from that point on.
Server The player Main thread Main thread Receive MP3 data from server Stop button ISR Yes If set to discard No Is GAP in buffer No Increment the receive pointer Yes Disable discard and set pointers to the data after GAP Disable button interrupt No Is send enabled Thread 2 Receive command from player Yes If GAP send has been selected send the GAP Decode the message Disable transfer to the decoder Read file into buffer Set buffer to discard Yes If UNPAUSE Enable send No Reset the pointer if
management. How was it possible that the GAP can be put into the middle of old data in the buffer? It did not make sense, for a long time the Nut OS was again suspected. 5.3.7.3 Next and previous Buttons. The design focus then changed as too much time was being spent on this problem, so next and previous buttons were implemented. They acted the same as stop, but on the server side the program opened song number plus one for next, and minus one for previous.
song from the play list without buttons being pressed. This would allow the buffer to be unaffected by the GAP and discarding of data algorithms. The result was that the player had just as much difficulty regardless of whether it went from low to high or high to low bit rates; the buffer quickly emptied. To solve this problem, it was necessary to reset the decoder every time the buffer emptied. It was decided to do it when the buffer emptied because this is the functional outcome of the change in bit rate.
The LCD has three control lines, Read/Write, enable and register select. It has a processor on board with two registers that are accessed via its bus. In this case a 4-bit bus was used due to a lack of I/O pins available. There are two registers to write too on the LCD; the first is the control register the second is the data register. These use the same bus but are selected by the register select control line.
Figure 25 The initialisation of the LCD [23] The values at (1), (2), (3), (4), and (5) are the configuration data written to the LCD module using the control function described above. At (1), the LCD is set in 4-bit bus mode, 2 lines of display and the characters are in 5x7 dot format. At (2), the memory mode is set to increment mode. This means that when a character is printed the cursor is moved across one position. At (3), the display is turned on and a cursor is shown.
Goto position 0,0 Clear the rest of the line or lines Yes String pointer = NULL No Exit Print pointer at cursor position on the LCD Increment pointer Yes Goto position 1,0 The next line Position of next character > LCD columns No Yes Yes Pointer = space Clear the rest of the line Calculate the number characters to the next space If greater than space left on line No No Figure 26 The print screen function This function was developed and tested initially without the Nut OS running and worked p
5.3.8.2 Extracting the song name To get the song name to the LCD from the filename, it was necessary to extract the required string from the songs entry in the play list and send this to the player, which would simply have to display it. Another socket was created just to send the song name. A thread was created on the player that prints anything (using the print screen function) that is received through this socket. On the server, the task is more complex.
There are two buffers represented in the flow chart. The path buffer is the buffer the openMP3 function creates to store the path and filename in and the song buffer is the buffer the function stores the song name in. It starts by adding the song number to the start of the buffer and then copying each character from the beginning of the path into the song buffer one character at a time.
Initial tests with the Ethernut showed that the web server functions that it provided were very useful. Commands could easily be passed to the web server through CGI scripts and the interface was easily developed via HTML.
would in turn talk to Windows to get the required information about files and then pass this on to the GUI. At the same time it was decided that the Ethernut should not communicate with Windows directly but instead, through a server. This meant that it would make more sense for the GUI to interact with Windows directly to form the play lists and then communicate with the server. Consequently, all the song selection and organization would be passed onto the server, reducing the overhead from the player.
#EXTINF:322,Jeff Buckley - Grace C:\mp3\Jeff Buckley\Grace\02 - Grace.mp3 #EXTINF:275,Jeff Buckley - Last Goodbye C:\mp3\Jeff Buckley\Grace\03 - Last Goodbye.mp3 It is the same structure as the file that was developed except that before each song the song ID tag information is put on the line preceding. If all the lines that are hashed defined were removed, then it is indeed the same. The server program was modified to ignore lines that begin with ‘#’. As a result any Winamp play list file worked perfectly.
Chapter 6 - Design evaluation The evaluation of the design for this player is unfortunately quite a subjective one. How does one assess whether they like a stereo, for example? Most of the evaluation is done on how the stereo sounds and operates. The same can be said for this player. To be able to judge the product for these qualities, the product must first be able to fulfil the basic requirements of a player.
The player can successfully play MP3s, up to and including 250kbbs, data rates with perfect audio quality. The test for this specification was to create MP3 data from a song multiple times and each time slightly increasing the bit rate. A play list was then created from the multiple copies of this same song in increasing bit rates and then the player was controlled to play each song.
quickly navigate the play list while the player is in playback mode. Playback does not resume until the user finally takes their finger off the button, otherwise the user will only hear about one tenth of a second of each song they are passing, which is more annoying than useful. This does not happen if the user toggles the button up and down. There is a repeat and a non-repeat function for the player.
6.2 Fulfilling the subtle requirements The finer details of an audio device are also worth mentioning. The user controls on the player are very easy to use. This might seem easy to accomplish, but it is often difficult to implement controls the human user enjoys. In so many MP3 players, there are often quite long delays before the action expected is actually performed. This is most commonly involved with the next and previous functions.
decisions that were significant for finishing the product on schedule. The decision to implement a server program on a PC, instead of implementing an SMB layer on the Ethernut, was very important for time management. It also made the development a lot easier. However, it was disappointing overall, because it meant that the player would no longer be as flexible due to the proximity to a PC that the user would need to be.
Chapter 7 - Future Improvements The previous chapters have outline how the idea for the thesis came about and how the idea was developed in to fully functional MP3 player. Not all aspects of the MP3 player were implemented in way that fulfilled the ideal specifications that were outlined in Chapter 3. Chapter 7 deals with the features that were not yet implemented and also the extra features that could be added to the design. 7.1 Improvements to the Current Design.
7.2 Extensions to the Current Design. After developing the user interface on the player itself there are a wide range of extra features that could be added. A remote control could be used to control all the features of the player including track control and play list functions. An infrared received would be developed on the player and the remote control could either be designed and constructed or a third party product could be used. Extra sound effects would be useful.
Chapter 8 - Conclusion The outcome of this thesis was to produce an MP3 player that solved the problem with the current MP3 player solutions. The problem was that current MP3 player requires the user to up load the MP3 data from a computer to the player before the player could be used. Current MP3 players use the small size of MP3 files to be able to store as many as possible on the local storage of the player. A better method would be to play the MP3s that are stored on the computer using a remote device.
also extracts the song name of the file selected to play and sends it the Ethernut which displays it on the LCD. The buttons connected to the Ethernut result in the sending of messages to the server program. The messages can invoke the actions of play, pause, stop, previous or next, allowing control of the current track or selection of new tracks. Concluding the thesis, the final product that was produced was a fully functional MP3 player.
References 1. Sutton, P. ‘COMS3200, Lecture 2’, University of Queensland, URL:http://www.itee.uq.edu.au/~coms3200/, (Sept, 2002) 2. ‘Music across Your Home Network? – AudioTron’, Tom’s hardware Guide, URL: http://www4.tomshardware.com/network/01q4/011220/index.html, (Sept, 2002) 3. AudioTron product page, URL:http://www.audiotron.net/audiotron/producthome.asp, (Sept, 2002) 4. SimpleFi Devices Home Page, URL:http://www.simpledevices.com/simplefi.shtml, (Sept, 2002) 5.
17. ‘MP3’, (1999), Webopedia, URL:http://www.webopedia.com/TERM/M/MP3.html, (Sept, 2002) 18. Nullsoft Winamp Home Page, http://www.winamp.com/, (Sept, 2002) 19. YAMPP Home Page,URL:www.yampp.com, (Sept, 2002) 20. E-mail correspondence from Harald Kipp, egnite Software GmbH. 21. ‘Software solutions for applications and appliances’, CodeFX Home Page, http://www.codefx.com/, (Sept, 2002) 22. ‘Windows Networking API/Redirector Overview’, msdn, URL:http://msdn.microsoft.com/library/default.
Appendices Appendix A – The Player Source code /* * MP3 player with network interface. * The Ethernut code * Engineering Thesis 2002 * by Michael Somersmith */ /* The MAC address of the realtek controler - dont loose */ #define MY_MAC {0x00,0x06,0x98,0x01,0x00,0x61} /* Buffer size all buffer management is based around this */ #define BUFFER_SIZE 16000 #include #include #include #include #include #include #include
static HANDLE q_stop = 0; static HANDLE q_next = 0; static HANDLE q_previous = 0; char button = 0; char first = 1; /* * Pause button ISR */ static void PauseButton(void *arg) { /*Diable the button interrupt*/ EIMSK = EIMSK & ~(0x01); /* Toggle the button state */ if(play){ pause = 0; } else { pause = 1; } if (pause){ /* change mode to play */ play = 1; /* Send command to the server */ NutTcpSend(command_sock, "UNPAUSE\n", 12); } else { /* Send command to the server */ NutTcpSend(command_sock, "PAUSE\n", 12)
/* Signal to debounce that a button has been pressed */ button = 1; /* change mode to stop */ play = 0; /* This will discard all transfer until it recieves GAP */ discard = 1; /* Return the handler - signals the interrupt is finished */ NutEventPostAsync(&q_stop); } /* * Next button ISR */ static void NextButton(void *arg) { /*Diable the button interrupt*/ EIMSK = EIMSK & ~(0x08); /* Disable the encoder access to the buffer */ tx_act = 0; /* Send command to the server */ NutTcpSend(command_sock, "NEXT\n", 1
/* * debounce thread */ THREAD(debounce, arg) { while(1){ /* Wait for button press */ NutSleep(100); if (button){ /* Re - enable buttons after 200ms */ NutSleep(200); EIMSK = EIMSK | 0x0F; button = 0; } } } /* * Command recieving thread */ THREAD(Command, arg) { char playlist_buff[50]; char size; /* Change thread priority to the lowest */ NutThreadSetPriority(200); while(1){ /* Recieve the song name from the server */ size = NutTcpReceive(playlist_sock, &playlist_buff[0], 50); /* Null terminate */ playlist_
* Nut/OS automatically calls this entry after initialization.
*/ NutRegisterDevice(&devEth0, 0x8300, 5); /* * Configure lan interface. * */ NutNetIfConfig("eth0", mac, inet_addr("192.168.0.100"), inet_addr("255.255.255.0")); for(;;) { /* * Create the three sockets. */ sock = NutTcpCreateSocket(); command_sock = NutTcpCreateSocket(); playlist_sock = NutTcpCreateSocket(); /* * Listen for connections on ports * 12345, 12222, 11111. If we return, * we got the server.
/* Reset the decoder */ VsReset(0); discard = 0; /* * Place the transmit pointer to * the byte after GAP */ tx_ptr = wr_ptr + buffer_shuffle + 4; /* * Place the recieve pointer to * the end of the data recieved */ wr_ptr = wr_ptr + bytes; mem_flip = mem_end; /* Tell it to refill the buffer */ first = 1; empty = 0; break; } } } buffer_count--; buffer_shuffle++; } /* If no GAP recieved clear the buffer */ if(buffer_count == 0){ tx_ptr = mem_start; wr_ptr = mem_start; mem_flip = mem_end; } } else { /* * If dis
/* If set the buffer is set to refill */ if(first){ /* If the buffer is full again */ if(buffer_in > (BUFFER_SIZE - 3000)){ /* Start the decoder receiving data again */ tx_act = 1; SIG_INTERRUPT6(); first = 0; } } /* The buffer has emptied */ if(empty){ empty = 0; tx_act = 0; /* Signal to refill buffer */ first = 1; /* Reset the decoder */ VsReset(0); } /* * Recieve buffer full wait for VSLI to take data */ while((BUFFER_SIZE - buffer_in) < 2000 ){ /* Calculate the buffer size */ if (wr_ptr >= tx_ptr){ buff
Appendix B – Server Source code /* * MP3 player with network interface. * Server software * Engineering Thesis 2002 * by Michael Somersmith */ #include #include #include #include #include #include
/* place the number in the string */ while(length>0){ tmp = length - 1; power = 1; while( tmp > 0 ){ power *= 10; tmp--; } *string = data/power + '0'; string++; length--; data -= (data/power)*power; } *string = '.
/* Add the song number to the start of the song name */ song_to_send_ptr += add_number_song_name(song_count, &song_name_to_send[0]);; } pointer_pos++; /* if at the end of the buffer set variable to exit */ if((song_path[pointer_pos] == '.
* and to write to */ song_name_ptr = &song_name[0]; songs_ptr = &songs[0]; ReadFile (PlayList, &songs[0], 100000, &bytes, NULL) ; songs[bytes] = NULL; /* extract the path and file name */ if(*songs_ptr == '#') crt_num--; //for winamp playlists /* loop til the end of the buffer */ while(*songs_ptr != NULL){ /* if the CRT count is one less than song number */ if(crt_num == song){ if(*songs_ptr == 0x5C){ net_address = 1; } else { net_address = 0; } /* Read the path out on the next line of the buffer */ while(*
} else { /* close old MP3 file */ CloseHandle(FileHandle); /* send the new song name to the ethernut */ send_song_name(song_no, &song_name[0]); /* open the new MP3 */ if ((FileHandle = CreateFile( &song_name[0], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) { printf("MP3 open failed with error %d\n", GetLastError()); } } } /* The message handler for commands from the ethernut */ DWORD WINAPI CommandThread(LPVOID lpParam) { SOCKET sock=(SOCKET)lpParam; char Buff[100]; w
song_no++; open_MP3(song_no); reopen = 1; if(stop == 0){ go = 1; } /* NEXT decrements the song count and opens the new file */ } else if ( strncmp("PREV", &Buff[0], 3) == 0){ play = 0; if(song_no != 1){ song_no--; } open_MP3(song_no); reopen = 1; if(stop == 0){ go = 1; } } } return 0; } /* The Main loop of the program */ int main(int argc, char **argv) { WSADATA wsd; SOCKET Server; SOCKET command_Server; int bytes_sent; HANDLE Thread; DWORD ThreadId; char buffer[3000]; unsigned long bytes_read = 1; struct
/* Set up the network information structures */ server.sin_family = AF_INET; server.sin_port = htons(iPort); server.sin_addr.s_addr = inet_addr("192.168.0.100"); command_server.sin_family = AF_INET; command_server.sin_port = htons(cPort); command_server.sin_addr.s_addr = inet_addr("192.168.0.100"); song_server.sin_family = AF_INET; song_server.sin_port = htons(sPort); song_server.sin_addr.s_addr = inet_addr("192.168.0.
printf("connecting\n"); if (connect(song_Server, (struct sockaddr *)&song_server, sizeof(song_server)) == SOCKET_ERROR) { printf("connect() failed: %d\n", WSAGetLastError()); return 1; } printf("connected\n"); /* Create the thread to receive the commands */ Thread = CreateThread(NULL, 0, CommandThread, (LPVOID)command_Server, 0, &ThreadId); if (Thread == NULL) { printf("CreateThread() failed: %d\n", GetLastError()); } /* stay in main loop until a key is hit */ while(!kbhit()) { /* delay for fast file skipin
printf("Done\r\n"); } /* Send the MP3 data to the ethernut */ bytes_sent = send(Server, &buffer[0], bytes_read, 0); Sleep(50); if (bytes_sent == SOCKET_ERROR) { printf("send() data failed:\n");// %d\n");//, WSAGetLastError()); break; } } /* Send disabled */ } else { Sleep(1); } } /* close everything and exit */ CloseHandle(Thread); CloseHandle(FileHandle); closesocket(Server); closesocket(command_Server); closesocket(song_Server); WSACleanup(); printf("Key pressed..
Appendix C – LCD interface source code /* * MP3 player with network interface.
} /* clear the rest of line 1 and line 2 */ if(line == 0){ while(counter < NO_COL){ counter++; putch(' '); } gotoxy(1,0); counter = 0; while(counter < NO_COL){ counter++; putch(' '); } } else { counter = counter - NO_COL; while(counter < NO_COL){ counter++; putch(' '); } } } /* prints the text at the co-ordinates x,y */ void printxy(unsigned char x, unsigned char y, char *text) { gotoxy(x,y); print(text); } /* usec delay */ void delay2(int clks) { clks = clks * 4; while ( clks ) { clks--; } } /* The initial
delay2 (40); control(0x02); control(0x08); delay2 (40); // 4 bit // 2 lines control(0x00); control(0x06); delay2 (40); //display on, cursor off, blink off control(0x00); //clear display control(0x0C); delay2 (40); control(0x00); control(0x01); NutDelay(2); //entry = increment + shift control(0x08); control(0x00); delay2 (40); } /* * goto the x y position on the LCD * 0,0 represents top left */ void gotoxy(unsigned char x, unsigned char y) { x = x * 0x04; NutDelay(5); control(0x08 + x); control(y); Nut
char temp; u_char priority; /* Set the thread priority to the highest */ priority = NutThreadSetPriority(10); LCD_RS(1); // select the data register LCD_E(1); /* Select the high 4 bits of the data */ temp = (PORTE & 0xF0)+((data & 0xF0)>>4); PORTE = temp; //load nibble for ( count = lcddelay; --count;) ; LCD_E(0); // strobe for ( count = lcddelay; --count;) ; LCD_E(1); data = (PORTE & 0xF0)+(data & 0x0F); //low nibble PORTE = data; //load nibble for ( count = lcddelay; --count;) ; LCD_E(0); // strobe NutThr
printxy(0,0," printxy(1,0," } "); "); 93
Appendix D – Decoder interface Source code /* * Copyright (C) 2001-2002 by egnite Software GmbH. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2.
* First pre-release with 2.4 stack * */ /* * MP3 player with network interface. * The Ethernut code * Engineering Thesis 2002 * Modified by Michael Somersmith * Added different buffer controls */ #include #include #include "vs1001k.
*/ if(tx_ptr == wr_ptr) { empty = 1; tx_act = 0; break; } /* * Stop transfer if the chip is filled up. */ if(bit_is_clear(VS_DREQ_PIN, VS_DREQ_BIT)) { break; } } /* * If the transfer stopped because the decoder * clears the DREQ signal, it still has room * for 32 bytes.
* Set data line. */ if(data & mask) sbi(VS_SI_PORT, VS_SI_BIT); else cbi(VS_SI_PORT, VS_SI_BIT); /* * Toggle clock and shift mask. */ sbi(VS_SCK_PORT, VS_SCK_BIT); mask >>= 1; cbi(VS_SCK_PORT, VS_SCK_BIT); } } /* * Write a word to the VS1001 command interface. */ static void VsWriteReg(u_char reg, u_short data) { /* * Disable interrupts and select chip.
{ /* * Set BSYNC high. */ sbi(VS_BSYNC_PORT, VS_BSYNC_BIT); outp(b, SPDR); asm volatile("nop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop"); /* * Set BSYNC back to low. */ cbi(VS_BSYNC_PORT, VS_BSYNC_BIT); loop_until_bit_is_set(SPSR, SPIF); } /* * VS1001 software reset. */ void VsReset(u_short mode) { int i; /* * Software reset. */ VsWriteReg(VS_MODE_REG, VS_SM_RESET); NutDelay(2); /* * Wait for DREQ.
u_char on[] = { 0x53, 0xEF, 0x6E, 56 }; u_char off[] = { 0x45, 0x78, 0x69, 0x74 }; on[3] += (fsin & 7) * 9; for(i = 0; i < sizeof(on); i++) VsSend(on[i]); for(i = 0; i < 8; i++) VsSend(0); NutDelay(ms); for(i = 0; i < sizeof(off); i++) VsSend(off[i]); for(i = 0; i < 8; i++) VsSend(0); } /* * Initialize the VS1001 hardware interface. */ void VsInit(u_char wDelay) { writeDelay = wDelay; /* * Toggle MP3 hardware reset output.
*/ sbi(VS_SI_DDR, VS_SI_BIT); /* * Set SS to output for SPI master mode. */ sbi(VS_SS_DDR, VS_SS_BIT); /* * Set MISO to input. */ cbi(VS_SO_DDR, VS_SO_BIT); /* * Set SCK output low.