View previous topic :: View next topic |
Author |
Message |
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Tue Jun 01, 2010 1:27 am Post subject: A simple WAV file player from an SD card w/double buffering |
|
|
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!
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!
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 |
|
|
bzijlstra
Joined: 30 Dec 2004 Posts: 1179 Location: Tilburg - Netherlands
|
Posted: Tue Jun 01, 2010 7:40 am Post subject: Thanks |
|
|
Very impressive. Thanks for sharing.
Ben Zijlstra |
|
Back to top |
|
|
Joy
Joined: 28 Feb 2009 Posts: 23
|
Posted: Wed Nov 24, 2010 6:48 am Post subject: |
|
|
please provide schematic for the project .. what should i do for 16bit pwm?
how is the quality with 8bit pwm? |
|
Back to top |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Fri Nov 26, 2010 3:45 pm Post subject: |
|
|
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 |
|
|
Evert :-)
Joined: 18 Feb 2005 Posts: 2156
|
Posted: Fri Nov 26, 2010 7:03 pm Post subject: |
|
|
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 |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
|
Back to top |
|
|
Evert :-)
Joined: 18 Feb 2005 Posts: 2156
|
|
Back to top |
|
|
albertsm
Joined: 09 Apr 2004 Posts: 5913 Location: Holland
|
Posted: Sun Nov 28, 2010 12:53 pm Post subject: |
|
|
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 |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Mon Nov 29, 2010 3:11 pm Post subject: |
|
|
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 |
|
|
Dave
Joined: 05 Feb 2005 Posts: 314 Location: OR
|
Posted: Fri Dec 03, 2010 3:06 am Post subject: |
|
|
Great demo. I will definitely be taking a much closer look at this work. Thanks! |
|
Back to top |
|
|
tarokh
Joined: 21 Feb 2007 Posts: 1
|
Posted: Sat Jun 04, 2011 9:01 am Post subject: |
|
|
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 |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Mon Jun 06, 2011 9:24 pm Post subject: |
|
|
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 |
|
|
amirf
Joined: 25 Aug 2009 Posts: 154
|
Posted: Wed Mar 06, 2013 4:45 am Post subject: |
|
|
is it possible to use lower crystal such as 16 Mhz , instead of 18.432 Mhz? thanks |
|
Back to top |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Wed Mar 06, 2013 4:50 am Post subject: |
|
|
Yes. It should work fine.
Glen _________________ http://bahbots.com |
|
Back to top |
|
|
pinkfloyd11
Joined: 02 Jul 2007 Posts: 247
|
Posted: Wed Mar 06, 2013 11:26 am Post subject: |
|
|
Hi glena
I would try your player since ISD25xx are expensive.
Could you post the schematic?
Thanks in advice |
|
Back to top |
|
|
|