Forum - MCS Electronics

 

FAQFAQ SearchSearch RegisterRegister Log inLog in

A simple WAV file player from an SD card w/double buffering
Goto page 1, 2, 3, 4  Next
 
Post new topic   Reply to topic    www.mcselec.com Forum Index -> Share your working BASCOM-AVR code here
View previous topic :: View next topic  
Author Message
glena

Bascom Member



Joined: 25 Jul 2007
Posts: 284
Location: PA near Philly

usa.gif
PostPosted: Tue Jun 01, 2010 1:27 am    Post subject: A simple WAV file player from an SD card w/double buffering Reply with quote

A while back I had seen some audio players that used 2 output pins (RC2) and some specially formatted audio files written to a flash chip and thought I might try to do something similar using PWM for the analog audio and double buffering to get passed the SD card read propagation delay.

After reading up on WAV file formats and doing a lot of testing I was able to create a workable WAV file player that is able to play mono or stereo WAV files (8bit only) up to 22KHz samples.

The output is on OC2A & OC2B and should have some low pass filtering applied to it but I was able to feed it directly into a cheap audio amplifier and it sounded almost as good as an FM radio! Smile



I think this might come in handy for users that want some more advance sounds, prerecorded speech or music in there applications.

Note, if you are using an MP3 as a source file, you will first need to convert it to a basic WAV file. Once converted to WAV format you can use the Windows XP Sound Player to than down sample it to 8bits & 22KHz.

Note you can also convert to Mono and other sample rates to save on file space if needed.

The program was written in two parts, a main file and a lib type file so its easier to put into your own app!

Let me know what you think and what projects you use it in.

Also, It would be realy nice if one of the assembler gurus out there could rewrite the ISR in assembly. This would make it much better and able to handle higher bit rates or at least leave more CPU for other uses while playing.

Have fun!



WavePlayer-v0.4.bas
Code:
'===============================================================================
' Project:  Wave Audio file player.  (only plays wave riff PCM format)
' Created:  2009-12-10
' Edited:   2009-12-15
' By:       Glen Aidukas
' Description:
' This program will read & play Wave format, RIFF, Mono, 8bit, PCM audio files.
'===============================================================================

'=====[ Compiler Directives ]===================================================
$regfile=   "m644pdef.dat"
$crystal=   18432000          ' 18.432 MHz
$baud=      38400
$hwstack=   64
$swstack=   64
$framesize= 64
'-------------------------------------------------------------------------------

'=====[ Includes ]==============================================================
$include "Config_MMC.bas"              ' Load SD/MMC code and config
$include "Config_AVR-DOS.BAS"          ' Load FAT File system code
$include "WavePlayerLib.bas"           ' PlayWaveFile code.
'-------------------------------------------------------------------------------

Dim Btemp1           as byte           ' used by filesystem
Dim Filename         as String * 12    ' Fat Filename

'=====[ Config misc stuff ]=====================================================
Config Serialout = Buffered , Size = 100        ' Serial Buffering
'-------------------------------------------------------------------------------

Enable Interrupts

print
print "Starting... V0.4"

waitms 100

'*******************************************************************************
'*****[ Start of main loop ]****************************************************
'*******************************************************************************

main:

   print "{027}[2J";             ' ansi clear screen...
   print "BahBots.com - Wave file player V0.4"
   print
   print "Commands: [N]extFile, [F]astForward, [R]ewind [S]tartOver"
   waitms 100

   Btemp1 = Initfilesystem(1)       ' Init Partition 1
   if Btemp1 <> 0 Then
      Print "{010}{013}Error: " ; Btemp1 ; " Initilizing file system!"
      waitms 2000
      end
   endif
   print

   do
      Filename= Dir("*.wav")
      do
         if FileName <> "" then

            GetWaveFileInfo FileName

               WP_lx= WP_FileSize / WP_ByteRate

               print "{027}[5;1H";     ' position cursor at top of page
               print "Playing File  : "; Filename                    ; "{027}[K"
               print                                                 ; "{027}[K"
               print "Wav file header info:"                         ; "{027}[K"
               Print "AudioFormat   : "; WP_AudioFormat              ; "{027}[K"
               Print "Channels      : "; WP_NumChannels              ; "{027}[K"
               Print "SampleRate    : "; WP_SampleRate               ; "{027}[K"
               Print "ByteRate      : "; WP_ByteRate                 ; "{027}[K"
               Print "BlockAlign    : "; WP_BlockAlign               ; "{027}[K"
               Print "BitsPerSample : "; WP_BitsPerSample            ; "{027}[K"
               Print "FileSize      : "; WP_FileSize                 ; "{027}[K"
               Print "Seconds long  : "; WP_lx                       ; "{027}[K"

            PlayWaveFile Filename

            if WP_Status <> 0 then
               print "{027}[17;1H"; "ERROR! File format not playable...{027}[K";
               waitms 3000
               print "{027}[17;1H{027}[K";
            endif
         endif
         Filename= Dir()
         waitms 500
      loop until Filename = ""

      print "{027}[16;1H"; "Done with list. Starting over..."  ; "{027}[K";
      waitms 2000
      print "{027}[16;1H{027}[K";      ' clear msg.

   loop

End 'end program

'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


WavePlayerLib.bas
Code:
'===============================================================================
' Project:  Wave Audio file player Lib.  (plays wave riff PCM formated files)
' Created:  2009-12-10
' Edited:   2009-12-15
' By:       Glen Aidukas
' Description:
' This program will read & play Wave format, RIFF, PCM audio file.
' NOTE: Timer1 & Timer2 are used and can not be used for other purposes.
'       Timer0 is available though.
'===============================================================================

Const WP_BufSize=             250      ' Size of each (A&B) buffers

Const WP_StatusNoErrors       = 0      ' No errors
Const WP_StatusBadFile        = 1      ' Not a wave file!
Const WP_StatusBadWavFormat   = 2      ' Bad format (Bits|SampleRate|Other)

Const WP_BufA                 = 0      ' Buffer A
Const WP_BufB                 = 1      ' Buffer B
Const WP_BufEmpty             = 0      ' Buffer status Empty
Const WP_BufFull              = 1      ' Buffer status Full

Dim WP_Filename      as String * 14    ' Fat Filename
Dim WP_buf           as String *  4    ' misc. text

Dim WP_Buf_A(WP_BufSize)   as byte     ' buffer A for double buffering
Dim WP_Buf_B(WP_BufSize)   as byte     ' buffer B for double buffering
Dim WP_A_Buf_Status        as byte     ' buffer A full (1) or empty (0)
Dim WP_B_Buf_Status        as byte     ' buffer B full (1) or empty (0)
dim WP_ActiveBuffer        as byte     ' Actively playing buffer [0|1]
Dim WP_BufPos              as word     ' Position in the active buffer
Dim WP_Status              as byte     ' return status

Dim WP_AudioFormat         as word     ' ??
Dim WP_NumChannels         as word     ' 1 or 2 channels
Dim WP_SampleRate          as long     ' Sample rate (EX: 8k|11K|22k|other)
Dim WP_Byterate            as long     ' Read Bytes per second
Dim WP_Blockalign          as word     '
Dim WP_BitsPerSample       as word     ' 8 or 16 bits (only 8 at the moment)
DIM WP_FileSize            as long     ' Size of wave file in bytes

' misc. vars
dim WP_ch                  as byte     '
Dim WP_lx                  as long     '
dim WP_SkipRate            as long     ' used for fast forward & reverse
dim WP_Seconds             as word     ' possition of seconds into a file
dim WP_Seconds_last        as word     ' last second. used for screen updates

''' Sub Declarations...
Declare Sub GetWaveFileInfo(WP_FileName as string)
Declare Sub PlayWaveFile(WP_FileName as string)

''' skip passed the subs so that this file can be included at the beginning of
''' the calling main program file
Goto WP_SkipSubs

' =====[ GetWaveFileInfo ]======================================================
' This sub opens the filename passed to it and reads its WAVE headers.
' This is called by ether the user to get info like Channels and BitRate.
' It is also called by PlaYWaveFile for the needed header info.
Sub GetWaveFileInfo(WP_FileName as string)
   WP_SampleRate=0 : WP_NumChannels=0 : WP_BitsPerSample=0  ' clear old values
   WP_buf= ""

   open WP_Filename For Binary As #10

   ''' Read file format
   get #10, WP_buf , 9, 4           ' should be "WAVE"
   if WP_buf <> "WAVE" then
      close #10
      WP_Status= WP_StatusBadFile
      return
   endif

   get #10, WP_AudioFormat,   21    ' Read Audio format
   get #10, WP_NumChannels,   23    ' Read number of channels (1|2, Mono|Stereo)
   get #10, WP_SampleRate,    25    ' Read Sample rate (EX: 8k|11K|22k|other)
   get #10, WP_ByteRate,      29    ' Read Bytes per second
   get #10, WP_BlockAlign,    33    ' BlockAlignment
   get #10, WP_BitsPerSample, 35    ' Read bitsper sample (8|16)
   WP_FileSize= Lof(#10)            ' Get file size

   close #10
End Sub


' =====[ PlayWaveFile ]=========================================================
' This sub will call GetWaveFileInfo and then play the wave file passed to it.
Sub PlayWaveFile(WP_FileName as string)

   ''' Read file format
   GetWaveFileInfo WP_FileName

   if WP_Status = WP_StatusBadFile then return   ' bad file!

   open WP_Filename For Binary As #10

   ''' Make sure its something we know how to play...
   if WP_NumChannels <> 1 and WP_NumChannels <> 2 then       ' Unknown format!
      WP_Status= WP_StatusBadWavFormat    ' Bad Format
      close #10
      return
   endif
   if WP_BitsPerSample <> 8 then  ' Unknown format!
      WP_Status= WP_StatusBadWavFormat
      close #10
      return
   endif

   '=====[ Config Timer2 for PWM audio output ]=================================
   ' This is done by using the PWM out puts on Timer2 (OC2A and OC2B)
   Config Timer2=Pwm, Compare A Pwm= Clear Down, Compare B Pwm= Clear Down, Prescale = 1
   stop timer2

   '=====[ Setup Timer1 for sample pushing isr ]================================
   ' Timer1 is used for setting the rate for pushing the samples to timer2 (PWM)
   Config Timer1= Timer, Prescale= 1, Compare A= Disconnect, Clear Timer= 1
   stop timer1
   On Compare1a WP_Play_isr   ' set up interrupt vector

   ''' setup compare1a reload value to match the SampleRate
   WP_lx= _XTAL / WP_SampleRate        ' CPU Speed / SampleRate of wave file
   Compare1a = WP_lx

   WP_lx= 45 : seek #10, WP_lx         ' seek to start of data at location 45

   WP_SkipRate= WP_Byterate / 5        ' setup fastforward & rewind rate

   ''' start loading buffers
   get #10, WP_Buf_A(1), , WP_BufSize  ' preload buffer A
   WP_A_Buf_Status= WP_BufFull         ' mark buffer A as full
   get #10, WP_Buf_B(1), , WP_bufSize  ' preload buffer B
   WP_B_Buf_Status= WP_BufFull         ' mark buffer B as full
   WP_ActiveBuffer= WP_BufA            ' start with buf 0
   WP_BufPos= 0                        ' Reset the buffer possition

   '======[ start playing file!!! ]============================
   Start Timer1 : Start Timer2
   Enable Compare1a

   ''' Watch the two buffers and fill them up when ether is emty
   while Eof(#10) = 0
      if WP_A_Buf_Status = WP_BufEmpty then
         get #10, WP_Buf_A(1), , WP_BufSize
         WP_A_Buf_Status= WP_BufFull
      elseif WP_B_Buf_Status = WP_BufEmpty then
         get #10, WP_Buf_B(1), , WP_bufSize
         WP_B_Buf_Status= WP_BufFull
      else
         ' Both buffers are full here so this would be a good place to
         ' do some other stuff if needed.  DON'T TAKE TOO LONG THOUGH! Wink
         WP_ch= inkey()
         if WP_ch <> 0 then
            WP_ch= lcase(WP_ch)
            select case WP_ch
               case "n" :                       ' Next file
                  WP_lx= LOF(#10)
                  seek #10, WP_lx
               case "f" :                       ' Fast forward
                  WP_lx= seek(#10) + WP_SkipRate
                  seek #10, WP_lx
               case "r" :                       ' Rewind
                  WP_lx= seek(#10) - WP_SkipRate
                  if WP_lx < 45 then WP_lx= 45
                  seek #10, WP_lx
               case "s" :                       ' StartOver
                  WP_lx= 45
                  seek #10, WP_lx
            end select
         else
            WP_lx= seek(#10) / WP_ByteRate
            WP_Seconds = WP_lx
            if WP_Seconds <> WP_Seconds_last then
               print "{013}Seconds Pos   : "; WP_Seconds; "{027}[K";
               WP_Seconds_last= WP_Seconds
            endif
         endif
      endif
   wend
   Disable Compare1a
   Stop Timer1 : Stop Timer2
   close #10

   WP_Status= WP_StatusNoErrors

End Sub

'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


'=====[ WP_Play ISR routine ]===================================================
' while Timer1 is active, cycle between buffer A and B and write its data
WP_Play_isr:
   if WP_ActiveBuffer = WP_BufA then               ' buffer A
      incr WP_BufPos                               ' incrment to next byte
      Pwm2a= WP_Buf_A(WP_BufPos)                   ' load sample into pwm2a
      if WP_NumChannels = 2 then incr WP_BufPos    ' if stereo then...
      Pwm2b= WP_Buf_A(WP_BufPos)                   ' load same sample if mono
      if WP_BufPos = WP_BufSize then               ' hit end of buffer A
         WP_BufPos= 0                              ' reset buffer pos to 0
         WP_ActiveBuffer= WP_BufB                  ' Set active buffer to B
         WP_A_Buf_Status= WP_BufEmpty              ' set buffer status empty
      endif
   else                                            ' buffer B
      incr WP_BufPos                               ' incrment to next byte
      Pwm2a= WP_Buf_B(WP_BufPos)                   ' load sample into pwm2a
      if WP_NumChannels = 2 then incr WP_BufPos    ' if stereo then...
      Pwm2b= WP_Buf_B(WP_BufPos)                   ' load same sample if mono
      if WP_BufPos = WP_BufSize then               ' hit end of buffer A
         WP_BufPos= 0                              ' reset buffer pos to 0
         WP_ActiveBuffer= WP_BufA                  ' Set active buffer to B
         WP_B_Buf_Status= WP_BufEmpty              ' set buffer status empty
      endif
   endif
Return
'-------------------------------------------------------------------------------

WP_SkipSubs:      ' this is just used to skip passed the beguning of this code

_________________
http://bahbots.com


Last edited by glena on Wed Jun 02, 2010 4:20 pm; edited 1 time in total
Back to top
View user's profile AIM Address
bzijlstra

Bascom Ambassador



Joined: 30 Dec 2004
Posts: 1179
Location: Tilburg - Netherlands

netherlands.gif
PostPosted: Tue Jun 01, 2010 7:40 am    Post subject: Thanks Reply with quote

Very impressive. Thanks for sharing.



Ben Zijlstra
Back to top
View user's profile Visit poster's website
Joy

Bascom Member



Joined: 28 Feb 2009
Posts: 23

india.gif
PostPosted: Wed Nov 24, 2010 6:48 am    Post subject: Reply with quote

please provide schematic for the project .. what should i do for 16bit pwm?

how is the quality with 8bit pwm?
Back to top
View user's profile Yahoo Messenger
glena

Bascom Member



Joined: 25 Jul 2007
Posts: 284
Location: PA near Philly

usa.gif
PostPosted: Fri Nov 26, 2010 3:45 pm    Post subject: Reply with quote

Joy,

Depending on the chip used, you need to find the pins (OC2A and OC2B) which are the PWM outputs for Timer2. You can then make it as simple as a 2.2k resister and a 0.1uf cap (one for each channel) like in the simple schematic at the top of this article where the triangle is the amplifier.

If your using a cheap little amplifier it will probably have a basic lowpass filter in it and you might not even need the resister/cap but I would maybe use a 0.1uf cap in series. I used a small amp that was meant for an MP3 play and it worked really well. For a more expensive higher power amp I would take more care to filter out high frequency above 20KHz.

-Glen

_________________
http://bahbots.com
Back to top
View user's profile AIM Address
Evert :-)

Bascom Expert



Joined: 18 Feb 2005
Posts: 2126

netherlands.gif
PostPosted: Fri Nov 26, 2010 7:03 pm    Post subject: Reply with quote

Hi Glena,

Thanks for sharing your work.
Can you post a Youtube movie or maybe a audio file so we can hear the quality of the audio.

_________________
www.evertdekker.com Bascom code vault
Back to top
View user's profile Visit poster's website
glena

Bascom Member



Joined: 25 Jul 2007
Posts: 284
Location: PA near Philly

usa.gif
PostPosted: Sat Nov 27, 2010 3:41 am    Post subject: Reply with quote

Evert Smile

As per your request: http://letsmakerobots.com/node/23622

-Glen

_________________
http://bahbots.com


Last edited by glena on Sat Nov 27, 2010 11:05 pm; edited 1 time in total
Back to top
View user's profile AIM Address
Evert :-)

Bascom Expert



Joined: 18 Feb 2005
Posts: 2126

netherlands.gif
PostPosted: Sat Nov 27, 2010 10:15 am    Post subject: Reply with quote

Very nice!, good sound quality.
I love Sade Very Happy

_________________
www.evertdekker.com Bascom code vault
Back to top
View user's profile Visit poster's website
albertsm

Administrator



Joined: 09 Apr 2004
Posts: 5171
Location: Holland

blank.gif
PostPosted: Sun Nov 28, 2010 12:53 pm    Post subject: Reply with quote

Wow, did not knew this was even possible with that quality. When you load the data into an eeprom it makes a cheap speech synthesizer.
Thanks for the youtube video, it 's great.

_________________
Mark
Back to top
View user's profile Visit poster's website
glena

Bascom Member



Joined: 25 Jul 2007
Posts: 284
Location: PA near Philly

usa.gif
PostPosted: Mon Nov 29, 2010 3:11 pm    Post subject: Reply with quote

Mark,

I think its also a testament to how good the code is that the compiler makes. Thanks for making such a good compiler!

-Glen

_________________
http://bahbots.com
Back to top
View user's profile AIM Address
Dave

Bascom Member



Joined: 05 Feb 2005
Posts: 270
Location: McMinnville, OR

usa.gif
PostPosted: Fri Dec 03, 2010 3:06 am    Post subject: Reply with quote

Great demo. I will definitely be taking a much closer look at this work. Thanks!
Back to top
View user's profile
tarokh

Bascom Member



Joined: 21 Feb 2007
Posts: 1

blank.gif
PostPosted: Sat Jun 04, 2011 9:01 am    Post subject: Reply with quote

Hi Glena
Thanks for your generosity
i work at a project that send a smal sound massage via phone line. it don't request high quality and my project is based on mega32. whether have your code ability of reduce quality ? if yes what change dose it have? thanks for your help
Back to top
View user's profile
glena

Bascom Member



Joined: 25 Jul 2007
Posts: 284
Location: PA near Philly

usa.gif
PostPosted: Mon Jun 06, 2011 9:24 pm    Post subject: Reply with quote

tarokh,

I'm not sure what your asking... Are you asking can it play lower quality sound? If so then yes. Just create a wav file with a lower bit rate like 8k samples per second mono.

If this is not what you mean then please explain with a little more detail.

-Glen

_________________
http://bahbots.com
Back to top
View user's profile AIM Address
amirf

Bascom Member



Joined: 25 Aug 2009
Posts: 154

indonesia.gif
PostPosted: Wed Mar 06, 2013 4:45 am    Post subject: Reply with quote

is it possible to use lower crystal such as 16 Mhz , instead of 18.432 Mhz? thanks
Back to top
View user's profile
glena

Bascom Member



Joined: 25 Jul 2007
Posts: 284
Location: PA near Philly

usa.gif
PostPosted: Wed Mar 06, 2013 4:50 am    Post subject: Reply with quote

Yes. It should work fine.

Glen

_________________
http://bahbots.com
Back to top
View user's profile AIM Address
pinkfloyd11

Bascom Member



Joined: 02 Jul 2007
Posts: 248

italy.gif
PostPosted: Wed Mar 06, 2013 11:26 am    Post subject: Reply with quote

Hi glena

I would try your player since ISD25xx are expensive.
Could you post the schematic?

Thanks in advice
Back to top
View user's profile
Display posts from previous:   
Post new topic   Reply to topic    www.mcselec.com Forum Index -> Share your working BASCOM-AVR code here All times are GMT + 1 Hour
Goto page 1, 2, 3, 4  Next
Page 1 of 4

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum