Thứ Năm, 13 tháng 8, 2009

WAV Player [MMC and SD memory card]


Here is a simple project to create a Wav file player, with files being read from a Secure Digital or Multi Media Card (SD or MMC). The files are read using the SDFileSystem library and the bytes are stored in a circular buffer. A timer interrupt reads bytes from the buffer and sends them to a R-2R digital to analogue converter (DAC) on Port D. A basic filter is used to remove the high frequency noise from the output. In the prototype version of this project, the output is then fed to an external amplifier, although a simple op-amp amplifier could be constructed as part of the project.

The files are read sequentially in the order that they appear on the card. After the last file has been played, the code stops. The header of each file is checked to ensure that it is a Wav file of the correct format - only 8-bit, mono files of 16kHz sampling rate can be played with the attached code, although it would be easy to adapt the code to play files of a lower sampling rate. With the PIC running at 32MHz, it is not possible to go to higher sampling rates using this code.

An alphanumeric LCD is used to display the name of the file being played. It also shows a progress bar indicating the fraction of the file that has been played. There is much scope for adding to the sophistication of the code in this area.

Two LEDS on Port E are also used in the code - that on PORTE.0 indicates that a buffer under-run has occurred whilst reading a file, whilst that on PORTE.1 indicates that the buffer is full. The circular buffer is used to prevent glitches in the output whilst the SD card accesses a new cluster.

During testing, the sound quality was initially quite poor. However, by adjusting the resistors in the R-2R ladder carefully, the sound quality was much improved.

Extensions to the project might include:

  • the ability to select the file to play using buttons
  • the inclusion of play, pause and stop buttons
  • the option to play files in a random order

All of the above would be relatively straightforward. There is ample code space still available, since on a PIC18F452 the code only occupies 6677 bytes of the 32768 available.

I hope that this simple project gives you some ideas

Project Code


{

***************************************************************************
* Name : WavPlayer.BAS *
* Author : hungkyo *
* Notice : Copyright (c) 2006 hungkyo *
* : All Rights Reserved *
* Date : 8/1/2009 *
* Version : 0.8 *
* Notes : It's a example. May not work well. *
* : *
***************************************************************************
}

Device = 18F452
Clock = 32
Config OSC = HSPLL

#option SD_SPI = MSSP
#option SD_SUPPORT_SUB_DIRECTORIES = True
#option LCD_DATA = PORTB.4
#option LCD_RS = PORTB.2
#option LCD_EN = PORTB.3
#option LCD_INIT_DELAY = 300
Include "SDFileSystem.bas"
Include "LCD.bas"

Const BufferSize = 500 // buffer to avoid glitches
Const TimerReload = 65036 // alter for different sample rates

Dim Timer As TMR1L.AsWord // alias to Timer1
Dim TimerOn As T1CON.Booleans(0) // start and stop
Dim TimerEnabled As PIE1.Booleans(0) // enable interrupts
Dim TimerInterrupt As PIR1.Booleans(0) // interrupt flag

Dim File As String(13)
Dim WritePointer, ReadPointer As Word // read/write pointers in
Dim Buffer(BufferSize) As Byte // circular buffer
Dim DataSize As LongWord // size of WAV data in file
Dim Progress As LongWord // increment progress bar when zero
Dim ProgressIncrement As LongWord // progress bar step

{
********************************************************************************
* Name : OnTimer (Interupt) *
* Purpose : Sends bytes to R-2R DAC on PORT D to play file *
********************************************************************************
}

Interrupt OnTimer() // ISR
Timer = TimerReload
Save(0) // context save
If ReadPointer = WritePointer Then // buffer empty
PORTE.0 = 1 // set buffer empty indicator
Else
Inc(ReadPointer) // increment read pointer
If ReadPointer = BufferSize Then // if end of buffer then...
ReadPointer = 0 // move back to beginning of buffer
EndIf
PORTD = Buffer(ReadPointer) // move byte to PORTD (R-2R ladder DAC)
EndIf
TimerInterrupt = False // clear interrupt flag
Restore
End Interrupt
{
********************************************************************************
* Name : ReadFmt *
* Purpose : Reads header of WAV file and checks compatibility *
********************************************************************************
}

Function ReadFmt() As Boolean // read fmt block of WAV file
Dim Index As Byte
ReadFmt = True
Index = 0
Repeat // skip over RIFF header
SD.ReadLongWord
Inc(Index)
Until Index = 3
If SD.ReadChar <> "f" Then // check in fmt block
ReadFmt = False
EndIf
If SD.ReadChar <> "m" Then
ReadFmt = False
EndIf
If SD.ReadChar <> "t" Then
ReadFmt = False
EndIf
If SD.ReadChar <> " " Then
ReadFmt = False
EndIf
SD.ReadLongWord // skip over fmt size
If SD.ReadWord <> 1 Then // check PCM (un-compressed) file
ReadFmt = False
EndIf
If SD.ReadWord <> 1 Then // check 1 channel (mono) file
ReadFmt = False
EndIf
If SD.ReadLongWord <> 16000 Then // check sample rate (16kHz)
ReadFmt = False
EndIf
Index = 0
Repeat // skip to data size
SD.ReadByte
Inc(Index)
Until Index = 12
DataSize = SD.ReadLongWord
SD.ReadLongWord // skip to data section
ProgressIncrement = DataSize / 16
End Function
{
********************************************************************************
* Name : ProgressBar *
* Purpose : Updates progress bar during playback *
********************************************************************************
}

Sub ProgressBar()
Dec(Progress)
If Progress = 0 Then
Progress = ProgressIncrement
LCD.Write("=")
EndIf
End Sub
{
********************************************************************************
* Name : SoftStart *
* Purpose : Ramps DAC output to new value to avoid clicks *
********************************************************************************
}

Sub SoftStart(pNewValue As Byte)
Dim Value As Byte
Value = PORTD
Repeat
If Value < class="category_1">Then
Inc(Value)
ElseIf Value > pNewValue Then
Dec(Value)
EndIf
PORTD = Value
DelayUS(20)
Until Value = pNewValue
End Sub
{
********************************************************************************
* Name : PlayFile *
* Purpose : Plays file *
********************************************************************************
}

Sub PlayFile(pFile As String)
Dim TempWritePointer As Word
LCD.Cls
LCD.Writeat(1, 1, "{ ", pFile, " }")
LCD.WriteAt(2, 1, " OPENING FILE ")
SD.OpenFile(pFile)
Timer = TimerReload // set timer one
ReadPointer = 0 // initialise pointers
WritePointer = 0
If Not(ReadFmt()) Then // incompatible file format
LCD.WriteAt(2, 1, "WRONG FILE TYPE!")
Else
LCD.WriteAt(2, 1, "----------------")
Progress = ProgressIncrement
LCD.MoveCursor(2,1)
Repeat // fill buffer initially
Inc(WritePointer)
Buffer(WritePointer) = SD.ReadByte
Dec(DataSize)
ProgressBar()
Until (WritePointer = BufferSize - 1) Or SD.EOF
SoftStart(Buffer(1)) // ramp up to first byte to play
TimerOn = True // start timer - play starts
Repeat
TempWritePointer = WritePointer // use a temp pointer in order
Inc(TempWritePointer) // to prevent interrupt
If TempWritePointer = BufferSize Then // detecting a buffer underun
TempWritePointer = 0 // when buffer is full
EndIf
While TempWritePointer = ReadPointer
PORTE.1 = 1
Wend
PORTE.1 = 0
WritePointer = TempWritePointer
Buffer(WritePointer) = SD.ReadByte // write next byte to buffer
Dec(DataSize)
ProgressBar()
Until SD.EOF Or (DataSize = 0)
EndIf
SD.CloseFile
Repeat // wait until buffer is empty
Until ReadPointer = WritePointer
TimerOn = False // stop timer
PORTE = 0
End Sub


// Main program...
TRISD = 0
TRISE = 0
PORTE = 0
SoftStart(127)
TimerOn = False // stop timer initially
Enable(OnTimer) // assign the interrupt handler
TimerEnabled = True // enable timer interrupts

LCD.Cls
LCD.WriteAt(1, 1, "PLEASE INSERT SD")
Repeat
Until SD.Init(spiOscDiv4)

Repeat
File = SD.Dir(dirNext, sdFile) // search for next file on SD card
If File <> Null Then // file found
PlayFile(File)
EndIf
Until File = Null

LCD.Cls
LCD.WriteAt(1, 5, "FINISHED")


1 nhận xét:

  1. So happy the shop is open!! Got some lovely things that sound great-thanks so much!

    Trả lờiXóa