Shop OBEX P1 Docs P2 Docs Learn Events
ASM WAV Player How can I modify to juke box — Parallax Forums

ASM WAV Player How can I modify to juke box

bradleyenoybradleyenoy Posts: 8
edited 2009-09-15 05:16 in Propeller 1
I have added the keyboard at the start of the code and I can select my file from the SD card
but how can I replay a wave file after it plays?

It is like the code was written only to play a wave file once..

Please help as losing my hair..

or change this code so I can retrigger from an input pin a wave file to play.

Bradley E Noy

Comments

  • VIRANDVIRAND Posts: 656
    edited 2009-09-11 11:54
    Can you play a different song but not the same one?

    If so then you have to stop reading the file at the end, what might be called closing the file.
    Then you have to reopen the file to reload it again at the beginning.
    If it's songs, there isn't enough RAM to hold a whole song so the file has to reload directly into the player.
  • bradleyenoybradleyenoy Posts: 8
    edited 2009-09-11 11:58
    No you just open one file and play it
    I can't replay any thing else or find out how to end or close or track the buffer
  • bradleyenoybradleyenoy Posts: 8
    edited 2009-09-11 12:03
    this was the last time I try but have a look at playwave_16bitstereo file

    ASM WAV Player Ver. 1b (Plays only stereo, 16-bit PCM WAV files from SD card)
    ' Copyright 2007 Raymond Allen See end of file for terms of use.
    ' Settings for Demo Board Audio Output: Right Pin# = 10, Left Pin# = 11 , VGA base=Pin16, TV base=Pin12
    ' Rev.B: 21Dec07 Fixed pin assignment bug.
    CON _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000 '80 MHz

    buffSize = 100

    VAR long parameter1 'to pass @buff1 to ASM
    long parameter2 'to pass @buff2 to ASM
    long parameter3 'to pass sample rate to ASM
    long parameter4 'to pass #samples to ASM
    long buff1[noparse][[/noparse]buffSize]
    long buff2[noparse][[/noparse]buffSize]
    byte Header[noparse][[/noparse]44]
    long mem
    byte mem1
    byte soundname
    byte keymem
    long m

    OBJ
    SD : "FSRW"
    text : "vga_text" 'For NTSC TV Video: Comment out this line...
    'text : "tv_text" 'and un-comment this line (need to change pin parameter for text.start(pin) command below too).
    kb : "keyboard"

    PUB Main|n,i,j, SampleRate,Samples
    'Play a WAV File
    'Start up the status display...
    text.start(16) 'Start the VGA/TV text driver (uses another cog)
    'The parameter (16) is the base pin used by demo and proto boards for VGA output
    'Change (16) to (12) when using "tv_text" driver with demo board as it uses pin#12 for video
    text.str(STRING("Starting Up",13))

    'open the WAV file (NOTE: Only plays stereo, 16-bit PCM WAV Files !!!!!!!!!!!!!)
    'access SD card
    soundname:=sd.mount(0)
    if (i<>0)
    repeat

    text.str(STRING("SD Card Mounted",13))

    'start the keyboard
    kb.start(26, 27)
    text.str(STRING("Enter sound number",13))

    'i:=sd.popen(string("test2.wav"), "r") ' <
    Change .wav filename here !!!!!!!!!!!!!!
    if (soundname<>0)
    text.str(STRING(" no file found",13))
    repeat

    'ignoring much file header (so you better have the format right!) stereo, 16-bit PCM WAV Files !!!!!!!!!!!!!!!!
    'See here for header format: http://ccrma.stanford.edu/CCRMA/Courses/422/projects/WaveFormat/
    soundname:=sd.pread(@Header, 44) 'read data words to input stereo buffer
    'Get sample rate from header
    SampleRate:=Header[noparse][[/noparse]27]<<24+Header[noparse][[/noparse]26]<<16+Header[noparse][[/noparse]25]<<8+Header[noparse][[/noparse]24]
    text.dec(SampleRate)
    text.out(13)
    'get # samples from header
    Samples:=Header[noparse][[/noparse]43]<<24+Header[noparse][[/noparse]42]<<16+Header[noparse][[/noparse]41]<<8+Header[noparse][[/noparse]40]
    Samples:=Samples>>2
    text.dec(Samples)
    text.out(13)


    'Start ASM player in a new cog
    text.str(STRING("Running ASM Player",13))
    parameter1:=@buff1[noparse][[/noparse]0]
    parameter2:=@buff2[noparse][[/noparse]0]
    parameter3:=CLKFREQ/SampleRate '#clocks between samples'1814'for 44100ksps, 5000 'for 16ksps
    parameter4:=Samples
    COGNEW(@ASMWAV,@parameter1)

    'Keep filling buffers until end of file
    ' note: using alternating buffers to keep data always at the ready...
    n:=buffSize-1
    j:=buffsize*4 'number of bytes to read
    text.str(STRING("n = "))
    text.dec(n)
    text.out(13)
    text.str(STRING("j = "))
    text.dec(j)
    text.out(13)
    m := 0
    repeat
    'while (j==buffsize*4) 'repeat until end of file
    ' text.out(@nSamples)
    if (buff1[noparse][[/noparse]n]==0)
    j:=sd.pread(@buff1, buffSize*4) 'read data words to input stereo buffer
    ' text.str(STRING("buff1",13))

    if (buff2[noparse][[/noparse]n]==0)
    j:=sd.pread(@buff2, buffSize*4) 'read data words to input stereo buffer

    DAT
    ORG 0
    ASMWAV
    'load input parameters from hub to cog given address in par
    'now stop
    ' COGID thisCog
    ' COGSTOP thisCog
    'JMP #ASMWAV
    movd tongue.gifar,#pData1
    mov x,par
    mov y,#4 'input 4 parameters
    tongue.gifar rdlong 0,x
    add tongue.gifar,dlsb
    add x,#4
    djnz y,#tongue.gifar

    setup
    'setup output pins
    MOV DMaskR,#1
    ROL DMaskR,OPinR
    OR DIRA, DMaskR
    MOV DMaskL,#1
    ROL DMaskL,OPinL
    OR DIRA, DMaskL
    'setup counters
    OR CountModeR,OPinR
    MOV CTRA,CountModeR
    OR CountModeL,OPinL
    MOV CTRB,CountModeL
    'Wait for SPIN to fill table
    MOV WaitCount, CNT
    ADD WaitCount,BigWait
    WAITCNT WaitCount,#0
    'setup loop table
    MOV LoopCount,SizeBuff
    'ROR LoopCount,#1 'for stereo
    MOV pData,pData1
    MOV nTable,#1
    'setup loop counter
    MOV WaitCount, CNT
    ADD WaitCount,dRate


    MainLoop
    SUB nSamples,#1
    CMP nSamples,#0 wz
    IF_Z JMP #Done
    waitcnt WaitCount,dRate

    RDLONG Right,pData
    ADD Right,twos 'Going to cheat a bit with the LSBs here... Probably shoud fix this!
    MOV FRQA,Right
    ROL Right,#16 '16 LSBs are left channel...
    MOV FRQB,Right
    WRLONG Zero,pData
    ADD pData,#4

    'loop
    DJNZ LoopCount,#MainLoop

    MOV LoopCount,SizeBuff
    'switch table ?
    CMP nTable,#1 wz
    IF_Z JMP #SwitchToTable2
    SwitchToTable1
    MOV nTable,#1
    MOV pData,pData1
    JMP #MainLoop
    SwitchToTable2
    MOV nTable,#2
    MOV pData,pData2
    JMP #MainLoop


    Done
    'now stop
    COGID thisCog
    COGSTOP thisCog
    'JMP #ASMWAV

    'Working variables
    thisCog long 0
    x long 0
    y long 0
    dlsb long 1 << 9
    BigWait long 100000
    twos long $8000_8000

    'Loop parameters
    nTable long 0
    WaitCount long 0
    pData long 0
    LoopCount long 0
    SizeBuff long buffsize
    'Left long 0
    Right long 0
    Zero long 0

    'setup parameters
    DMaskR long 0 'right output mask
    OPinR long 10 'right channel output pin # ' <
    Change Right pin# here !!!!!!!!!!!!!!
    DMaskL long 0 'left output mask
    OPinL long 11 'left channel output pin # ' <
    Change Left pin# here !!!!!!!!!!!!!!
    CountModeR long %00011000_00000000_00000000_00000000
    CountModeL long %00011000_00000000_00000000_00000000


    'input parameters
    pData1 long 0 'Address of first data table
    pData2 long 0 'Address of second data table
    dRate long 5000 'clocks between samples
    nSamples long 2000
  • RaymanRayman Posts: 14,849
    edited 2009-09-11 12:48
    Please put your code in as an attachment, the forum software doesn't show it right when you paste it in...

    The code was only made to play a single file, but it should be easy to modify to play more than one...

    FYI: I've recently posted a revised version to the original forum thread... You might want to try that one, but it too is only coded for a single file.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    My Prop Info&Apps: ·http://www.rayslogic.com/propeller/propeller.htm
  • VIRANDVIRAND Posts: 656
    edited 2009-09-11 12:59
    It does look like it can only run once,
    but maybe you can add a REBOOT command if this program is in eeprom,
    unless you probably have it on SD card with femtobasic or propDOS.
    Also, keyboard looks initialized too late, and missing parts to type in filename string or use any keys.
    It looks like it doesn't work now and I can't fix it but it shouldn't be too hard with an original working version,
    except I don't have an SD slot on my protoboard now to test with.
    I think if things are initialized without stopping after playing you run out of cogs, but maybe REBOOT can deal with that mess.
  • bradleyenoybradleyenoy Posts: 8
    edited 2009-09-11 13:54
    ok how can I make a wave player like a juke box player I can do the keyboard ok and find the sd name of the wave but I cant get my head around the wave player
    I want to make the player fast like play small files and need the speed
    I don't like the reboot
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2009-09-11 14:32
    Brad,

    Just a few snippets from some code I did a couple of months ago for a WAV player accessed from a numeric keypad. It features instant play and repeat modes as well as jump to track immediate modes etc. I elected to have the code that was feeding the 16-bit signed PCM samples to the sound object as a separate task and simply ask that task to do something different. So the response from the keypad is as I said instant.

    The code in PLAY sits in a repeat loop and for my project I get it to increment a track number to go to the next track but all you need to do is not increment it if you just want it to do a track loop. PLAYPAUSE is called from the keypad such as when you hit ENTER.

    Make of this what you will, I know it's not everything but should help you along. I'm turning this into a commercial product soon as it has been going really well and I may release the code in a more general form later on. The display is one of my own serial 4 line LCD modules with big digits for the track number. I also read the headers and extract from the chunks the sample rate, bits, size, title etc.

    *Peter*

    pri PLAYPAUSE
      case mode
        0     : if digits~
                  track := num~
                  title~
                  lcd.tx(_cld)
                REQUEST
        _play : if digits~
                  STOP
                  track := num~     ' change track to the number that has been entered (and clear)
                  REQUEST           ' make a request to the play task
                else
                  mode := _pause
                  BACKLIGHT(_dim)
        _pause :  CONTINUE
    
    
    pri REQUEST
      coginit(0,PLAYTASK,@STACK0)
      CONTINUE
      ResetClock
      if PauseFirst
        mode := _pause
        BACKLIGHT(_dim)
    
    pri CONTINUE
      mode := _play
      lcd.tx(_show)
      BACKLIGHT(_bright)
      ready(1)
      ResetClock
    
    



    Play task section

    pub PLAYTASK
      PLAY(track)
    
    
    PUB PLAY(tracknum)
      tracknum //= 1000
      bytemove(@fname,string("SONG"),4)
      bytemove(@fname[noparse][[/noparse]7],string(".WAV"),5)
      repeat
        track := tracknum
        pos~
        spinner~
        fdec(tracknum)
        com.str(string(cr,lf))
        com.str(@fname)
        if OpenWav
          trackflag~~
          PLAYTRACK
        else
    '      cont~                    ' stop continuous play when a track is not found
        ms(gap)
        tracknum++
        tracknum //= 1000
      until not cont
      STOP
    
    pub PLAYTRACK  | ch,aj
      delta~
      ready(1)
      busy(1)
      aj := buffer_siz * WORD_SIZ
      sound.start(audio, sample_rate, @buffer1, @buffer2, buffer_siz)
    ' KEEP SUPPLYING DATA USING pcm BUFFER
      repeat while (aj == buffer_siz * WORD_SIZ)  'repeat until end of file
        aj:= sdfile.pread(@pcm, buffer_siz * WORD_SIZ)
        pos += buffer_siz
        busy(2)
        sound.write(@pcm)  ' waits to fill another buffer
     ' PAUSING
        repeat while mode == _pause
          busy(0)
          ready(2)                  ' flash ready led when in pause
          ms(100)
    ' FINISHED
      sound.stop
    ' timer[noparse][[/noparse]_standby] := 30
    
    pub STOP
      ResetClock      '
      sdfile.pclose
      sound.stop
      mode := _stop
      title~
      busy(0)
      ready(1)
      sound.stop
      cogstop(0)
    
    
    pri CheckInserted
      dira[noparse][[/noparse]card]~
      if ina[noparse][[/noparse]card]
        ms(10)
        if ina[noparse][[/noparse]card]
          ready(0)
          busy(1)
          cogstop(0)                '12345678901234567890
          lcd.str(string(_show,_cld,"Insert Songbox card!"))
          ms(100)
          repeat while ina[noparse][[/noparse]card]
            busy(2)
            ms(100)
          reboot
    
    



    P.S. Here's a photo of the prototype

    Post Edited (Peter Jakacki) : 9/11/2009 2:45:36 PM GMT
    640 x 389 - 154K
  • DelusDelus Posts: 79
    edited 2009-09-11 14:33
    I made a wave player last spring that will play more than one file (I had it cycling through wav versions of the RA3 sound track at one point).· it's still using older sd routines and hides the sd object if you wanted to use it for anything else.· I will be making a more robust version eventually but i think this will work with a few modifications.
    David

    Post Edited (Delus) : 9/11/2009 7:10:56 PM GMT
  • bradleyenoybradleyenoy Posts: 8
    edited 2009-09-11 16:33
    Thanks I will have a look at this code
  • bradleyenoybradleyenoy Posts: 8
    edited 2009-09-11 16:49
    just want to load sd wave and play tracks like sound fx 1 to 10 eg play track 1 then 3 then 1 then 1 then 6 then 2,3,4
  • bradleyenoybradleyenoy Posts: 8
    edited 2009-09-11 16:50
    are you using PCM object
  • bradleyenoybradleyenoy Posts: 8
    edited 2009-09-11 17:01
    where is this file - object
    AWG : "Audio Out Gen"
    ABG : "Audio Buffer to gen"
  • DelusDelus Posts: 79
    edited 2009-09-11 19:01
    sorry about that i missed a couple of files this morning here's what should have been sent.· This version only works with stereo 16-bit pcm wave files.

    David
  • DelusDelus Posts: 79
    edited 2009-09-14 22:46
    I'm not sure if you've got everything figured out already but here's the latest version of my wav player just finnished. It plays mono, stereo, 8 and 16-bit PCM wav files and allows access to sd routines while a wav isn't playing. Hope someone finds it useful.

    David

    Pretty sure all the necessary files are there this time.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2009-09-14 23:15
    Hi David,

    Looks like you put some work into that especially in processing the headers. You could probably do with some string matching though. This code snippet suited my particular implementation and is by no means exhaustive but does illustrate the use of strings.

    Maybe we can get a more general method worked out to cover the different formats.

    *Peter*

    pri ReadChunks |ptr
      ptr := @header+_chunks
      repeat until match(ptr,string("INAM")) OR match(data,string("data")) or ptr > @header+512
        ptr += 2
      if match(ptr,string("INAM"))
        bytemove(@title,ptr+8,getlong(ptr+4))
        com.tx(" ")
        com.str(@title)
        MakeBanner
      else
        PresetBanner
    
      data := @header+_chunks               ' offset to 1st chunk
      repeat until match(data,string("data")) or ptr > @header+512
        data += 2
      if match(data,string("data"))
        wavsize := getlong(data+4)                          'statistics
        playtime := wavsize/(sample_rate*2)
        data += 8
        data := data-@header
      else
        wavsize := getlong(@header+4)                       'statistics
        playtime := wavsize/(sample_rate*2)
        data := 256                                         ' assume header is 256 bytes
    
    
    
    pub getlong(addr)                                       ' get one LONG from the buffer, assume non-aligned
      result := byte[noparse][[/noparse]addr++]
      result += byte[noparse][[/noparse]addr++] << 8
      result += byte[noparse][[/noparse]addr++] << 16
      result += byte[noparse][[/noparse]addr] << 24
    
    pub match(addr,str) | i
      result~~
      repeat i from 0 to strsize(str)-1
        if byte[noparse][[/noparse]addr][i] <> byte[noparse][[/noparse]str][i]
          result~
    [/i][/i]
    
  • DelusDelus Posts: 79
    edited 2009-09-15 00:52
    Hello Peter,

    What different formats do you have in mind? My understanding is pretty limited to pcm wav files at this moment. Have been doing quite a bit of work with them in C# and this post was just what I needed to update the wav player i made back in the spring. the wav_decoder was an attempt to generalize but in the end it just ended up stripping the important bits out of the file and leaving you with the data remaining (yes string comparisons would clean that up a bit). It would be nice to have some way of passing the sd object to the wav_decoder as in real oo languages to allow for more seemless integration with other objects.

    David

    Added: slightly more readable wav_decoder object

    Post Edited (Delus) : 9/15/2009 1:41:32 AM GMT
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2009-09-15 03:20
    Dave,

    Here's a typical header produced by Audacity. You can see the various tags in ASCII plus the modes and sample-rates etc as per WAV file format specification. I'm using an adapted PCM audio driver for mono 16-bit signed PCM.

    0000: 52 49 46 46 24 00 00 00 57 41 56 45 66 6D 74 20  RIFF$...WAVEfmt 
    0010: 10 00 00 00 01 00 01 00 44 AC 00 00 88 58 01 00  ........D....X..
    0020: 02 00 10 00 4C 49 53 54 5A 00 00 00 49 4E 46 4F  ....LISTZ...INFO
    0030: 49 4E 41 4D 0E 00 00 00 47 6F 6F 64 62 79 65 20  INAM....Goodbye 
    0040: 47 69 72 6C 00 00 49 41 52 54 0C 00 00 00 44 61  Girl..IART....Da
    0050: 76 69 64 20 47 61 74 65 73 00 49 43 4E 54 16 00  vid Gates.ICMT..
    0060: 00 00 43 6F 6C 64 20 43 61 73 65 20 73 6F 75 6E  ..Cold Case soun
    0070: 64 74 72 61 63 6B 00 00 49 43 52 44 06 00 00 00  dtrack..ICRD....
    0080: 31 39 37 38 00 00 64 61 74 61 00 00 00 00 00 00  1978..data....
    
    



    I think MACs put their tag info at the end of the file so we need a generalized method for handling most of the common stuff. I imagine that having a method which could be called with a descriptor and return with the data or pointer will be helpful, a bit like wav.readtag(string("INAM")) or sample_rate := wav.sample-rate etc. When I have time to play with it again I will try to clean these methods up a bit instead of the usual Q&Ds smile.gif

    *Peter*
  • DelusDelus Posts: 79
    edited 2009-09-15 03:46
    Peter,

    Do you have a description of the wav specifications? The specs I have been using may be incomplete as I have no INFO, INAM sections in my spec. I've been lucky enough so far not to run into any wave files with anything between the WAVE chunk and the data chunk which doesn't have its id immediately followed by its length which is then ignored. But I think I’ve had enough for one night.

    David
Sign In or Register to comment.