Shop OBEX P1 Docs P2 Docs Learn Events
P2 Audio Out — Parallax Forums

P2 Audio Out

Has anybody sent audio out the stereo outputs of the P2 AV board? I'm interested in seeing some PASM code that does that.
«1

Comments

  • Ahle2Ahle2 Posts: 1,179
    Yes, Peter Jakacki has got both audio and video running from SD card. I have got SIDcog running at 250 kHz sample rate. My next P2 project will be an audio driver. And there are a few other examples as well. I will dig up the code to SIDcog tomorrow and strip it clean from the junk (that is SIDcog )😃
  • 250kHz SIDcog at what P2 frequency?
  • Not very many P2 audio projects yet that I can recall but external HyperRAM on a P2 should now allow some interesting wavetable type audio in time, or at a minimum simpler MOD file playback vs the P1 approach with a fresh SD card which I think Ariba did IIRC.

    I also wonder if a P2 could software decode MP3? I heard the Cortex M4 can do it.
  • The MUL(S) and hardware CORDIC will likely eat MP3 decoding for breakfast.
    Although I don't see a lot of MP3s around these days, the world has moved on (mostly to OPUS, as far as I can tell. And of course lossless formats).

    Also...

    OPLcog, anyone?

  • Cluso99Cluso99 Posts: 18,069
    MP3

    At work we broadcast a live 1 hour video every week, plus an additional one each month. We also produce audio versions of these programs in MP3 format and make video and audio podcasts of these too. We also do a short ~5 minute video and audio each week, again available in MP3.

    So I don't think MP3 will be replaced any time soon.
  • 90MHz will do MP3 on an arm920t. 45MHz won't. (my own testing with libmad) With 160 MIPS, the P1 should be able to do MP3 if it could be sufficiently parallelized. It might not have enough memory though.

    It would be interesting to throw these audio codecs at the existing C compilers for P2. We all like efficient code, but if a C version is fast enough... Based on the benchmarks I've seen, my first choice would be riscv-p2.

    Storage is much cheaper now than when MP3 became popular. It's possible that MP3 would have advantages of computational complexity or code size compared to newer codecs. And I think it's old enough now to be patent free.
  • potatoheadpotatohead Posts: 10,261
    edited 2020-01-01 06:47
    https://mp3-tech.org/programmer/sources/amp-0_7_6.tgz

    That one is lean, mean, accurate

    Could decode 256mbps on 30Mhz MIPS R4k

    Many existing players were derived from that software base. It's really pretty great.

    Years ago, I compiled it on irix 5.3. The sound quality was very impressive, and it would do that 256 megabits over an NFS share, meaning the network stack was also active, along with the indigo magic Desktop.

    Pity, I never did profile it memory use wise.

  • kwinnkwinn Posts: 8,697
    Wuerfel_21 wrote: »
    The MUL(S) and hardware CORDIC will likely eat MP3 decoding for breakfast.
    Although I don't see a lot of MP3s around these days, the world has moved on (mostly to OPUS, as far as I can tell. And of course lossless formats).

    Also...

    OPLcog, anyone?

    OPL?? That was a programming (Our Programming Language) language at a company I worked for eons ago. Pretty sure that is not what you are referring to so please enlighten me. Way too many search results to pick from.
  • Ahle2Ahle2 Posts: 1,179
    OPL is the sound chip used in many Soundblaster cards and variants from the golden days of DOS-gaming. I had an Amiga in those days so I grew to "hate" that sound. (anyone remember the "Intel Outside" meme from back then?) I do love the DX7 though, but that is a much more comptetent FM synth.
  • Ahle2Ahle2 Posts: 1,179
    edited 2020-01-01 10:10
    Dave,

    Here is the P2 specific audio code from SIDcog.

    First I setup some constants...
      L_PIN = 0
      R_PIN = 1
      SYSTEM_CLOCK = 250000000
      SAMPLE_RATE = 250000
    
      'DAC_MODE             = %00001_0 ' DAC 8-bit noise
      'DAC_MODE             = %00010_0 ' DAC 16-bit dither, noise
      DAC_MODE             = %00011_0 ' DAC 16-bit dither, PWM
      DIR_MODE             = %01      ' Output enabled, overrides DIR
      INIT_8BIT_DAC_VALUE  = 128
      'ANALOG_OUT_MODE      = %10100 ' 990 ohm, 3.3V DAC mode
      'ANALOG_OUT_MODE      = %10101 ' 600 ohm, 2.0V DAC mode
      'ANALOG_OUT_MODE      = %10110 ' 123 ohm, 3.3V DAC mode
      ANALOG_OUT_MODE      = %10111 '  75 ohm, 2.0V DAC mode
      SMARTPIN_DAC_MODE    = (ANALOG_OUT_MODE << 16) | (INIT_8BIT_DAC_VALUE << 8) | (DIR_MODE << 6) | DAC_MODE
      SAMPLE_PERIOD        = SYSTEM_CLOCK  / SAMPLE_RATE  'CPU cycles between sample updates
    

    Here is the PASM init routine...
                  wrpin   smartConfigAudioDAC, #L_PIN           ' Config smartpin DAC mode on "left" pin
                  wrpin   smartConfigAudioDAC, #R_PIN           ' Config smartpin DAC mode on "right" pin
                  wxpin   samplePeriod, #L_PIN                  ' Set sample period for left audio channel
                  wxpin   samplePeriod, #R_PIN                  ' Set sample period for right audio channel
                  dirh    #L_PIN                                ' Enable smartpin DAC mode on left pin
                  dirh    #R_PIN                                ' Enable smartpin DAC mode on right pin
                  setse1  #%001_000000 | L_PIN                  ' Event triggered every new sample period (when "left in pin rises")
    

    Note the "setse1" instruction, it is used to trigger an event everytime the "left" smartpin NCO wraps and the "left in pin rises". (@SAMPLE_RATE)
    the "001" is the event mode that selects that the event should trigger on a pin rising. (everytime the NCO wraps it sets the IN state high)

    Then for every new sample processed you have to send it to the pin(s) and wait the exact number of remaining CPU cycles before starting the processing of the next sample...
                  wypin    r1, #L_PIN                          ' Output sample on left channel
                  wypin    r1, #R_PIN                          ' Output sample on right channel
                  waitse1
    

    The "waitse1" waits for the selectable event 1 that occours @SAMPLE_RATE and acknowledges it.

  • Ahle2Ahle2 Posts: 1,179
    edited 2020-01-01 10:16
    Of course, the event can trigger an interrupt routine instead... That's a different story and you have to delve into the wonderful world of "indetermism".
    One more thing... The sample gets updated exactly when the NCO wraps and NOT when "wypin-ning".
  • Ahle2Ahle2 Posts: 1,179
    edited 2020-01-01 12:25
    Wuerfel_21 wrote: »
    250kHz SIDcog at what P2 frequency?

    I think I can go as low as 190 MHz?? Even when all bells and whistles are activated. It's 8 times the sample rate of SIDcog on the P1. The performance boost comes from 3 different things. 2x from faster instruction execution, 2x from faster clock rate and 2x from P2 specific optimizations such as HW multiplication, "bit twiddling" instruction, block moves. etc.

    It may even be possible to hit 500 kHz sample rate with moderate overclocking and more P2 specific optimizations.
  • Ahle2, Thanks for the code. I'll give it a try when I have a chance.
  • ReinhardReinhard Posts: 489
    edited 2020-01-02 20:12
    Hello, I have tried with the infos above to make a very simple audio out prog.
    I compile with p2asm and have questions.
    first smartConfigAudioDAC is not defined
    wrpin   smartConfigAudioDAC, #L_PIN
    

    Second Can I replace samplePeriod
    wxpin   samplePeriod, #L_PIN 
    
    with this SAMPLE_PERIOD
    SAMPLE_PERIOD        = SYSTEM_CLOCK  / SAMPLE_RATE
    
    Is that planned with the smartpins so that I touch the pin directly to the probe
    (e.g. #L_PIN) and need nothing else?
    It must not be event or interrupt triggered.
    Thank you for help.
  • Ahle2Ahle2 Posts: 1,179
    I'm sorry about that!

    smartConfigAudioDAC = SMARTPIN_DAC_MODE

    samplePeriod = SAMPLE_PERIOD

    You must register the event even if you dont want to do any interrupt handling!
  • const
      L_PIN = 0
      R_PIN = 1
      SYSTEM_CLOCK = 250000000
      SAMPLE_RATE = 250000
    
      'DAC_MODE             = %00001_0 ' DAC 8-bit noise
      'DAC_MODE             = %00010_0 ' DAC 16-bit dither, noise
      DAC_MODE             = %00011_0 ' DAC 16-bit dither, PWM
      DIR_MODE             = %01      ' Output enabled, overrides DIR
      INIT_8BIT_DAC_VALUE  = 128
      'ANALOG_OUT_MODE      = %10100 ' 990 ohm, 3.3V DAC mode
      'ANALOG_OUT_MODE      = %10101 ' 600 ohm, 2.0V DAC mode
      'ANALOG_OUT_MODE      = %10110 ' 123 ohm, 3.3V DAC mode
      ANALOG_OUT_MODE      = %10111 '  75 ohm, 2.0V DAC mode
      SMARTPIN_DAC_MODE    = (ANALOG_OUT_MODE << 16) | (INIT_8BIT_DAC_VALUE << 8) | (DIR_MODE << 6) | DAC_MODE
      SAMPLE_PERIOD        = SYSTEM_CLOCK  / SAMPLE_RATE  'CPU cycles between sample updates 
    
    ''Here is the PASM init routine...
    dat
    	org
    
        wrpin   SMARTPIN_DAC_MODE, #L_PIN           ' Config smartpin DAC mode on "left" pin
        wrpin   SMARTPIN_DAC_MODE, #R_PIN
        
        wxpin   SAMPLE_PERIOD, #L_PIN                  ' Set sample period for left audio channel
        wxpin   SAMPLE_PERIOD, #R_PIN                  ' Set sample period for right audio channel
        dirh    #L_PIN                                ' Enable smartpin DAC mode on left pin
        dirh    #R_PIN                                ' Enable smartpin DAC mode on right pin
        setse1  #%001_000000 | L_PIN                  ' Event triggered every new sample period (when "left in pin rises")
    
    	'mov		r1,#128
    loop    
        wypin    r1, #L_PIN                          ' Output sample on left channel
        wypin    r1, #R_PIN                          ' Output sample on right channel
        'waitse1  
        add		r1,#1
        jmp 	#loop
        
     r1		res		1  
    
    

    Now I coded this.
    then p2asm -v33 audio.spin2 ... ok
    loadp2 -p /dev/ttyUSB0 audio.bin ... ok (leds P60/P61 flash)
    The board is supplied with pc_usb.
    The Osci probe is at P0.
    but there is no signal.
    What am I doing wrong?
  • If I make a P1 like DIRA/OUTA or DRVNOT code I see the normal Pin toggle.
  • Reinhard given at this stage you just are looking to see DAC outputs, you can do this purely with wrpin and not worry about the smartpin (wxpin, wypin) for now

    I have posted proven DAC testing code from 13 months ago but need to find it. Or I can send it in an hour or two when I am at work

    I think you are close to a solution, hang in there!

  • Here's the working DAC test code that steps through pins
    Hopefully this can be cut back a bit for what you need
  • Reinhard, on your wrpin and wxpin instructions you need to put ## before SMARTPIN_DAC_MODE and SAMPLE_PERIOD since these are constants and not cog RAM addresses. There appears to be a bug in p2asm since it is not generating an error message on these instructions. I tried PNUT, and it does generate an error message.

    I also tried flexgui, and it generates a different error message saying that SMARTPIN_DAC_MODE, SAMPLE_PERIOD, L_PIN and R_PIN are unknown symbols. This is because you use "const" instead of "con" at the beginning of your program. The assembler assume "const" is just a label. PNUT and p2asm start out in the CON mode, so all of the constant definitions work OK in those assemblers. I'm not sure what flexgui does with the constant definitions, but it seems to be ignoring them.
  • @Dave, thank you for this information, now I see the sawtooth signal as expected.
    @Tubular, thank's also for the code, next I look what I can use for DAC.
  • Ahle2Ahle2 Posts: 1,179
    Glad you have got it working Reinhard. Just a reminder...Without the "waitse1", you have got a jittery saw tooth signal updating at the configured 250 kHz. The smart pin audio DAC mode on the P2 is not like the duty DAC on the P1 that could be updated when you feel like it. It needs to be synched to the NCO rollover. The value is sampled on the rollover, so the processing of the sample needs to be done in the window between sample updates. Think of it like a double buffered frame buffer.
  • I played around with the code that Ahle2 posted, and created a C program that plays a 1 KHz tone through the AV board's stereo outputs. The attached program uses FlexGUI to compile and load.
  • Ahle2Ahle2 Posts: 1,179
    edited 2020-01-04 12:36
    I'm glad I could help... :smiley:

    16 kHz sample rate is quite lofi, you could fit 5000 pasm instructions between sample updates @160 MHz. SIDcog takes up to 380 instructions to process each sample. The P2 really has the power to do quite advanced audio processing at high sample rates.

  • 16 kHz is sufficient for my purposes. I have plans to use the 5000 pasm instructions per sample. :smile:
  • @"Dave Hein"
    Thank you for the audio_out demo in C.
    -- The xor in void audio_out(int value) is very clever ! --
    -- I forgot all this tricks. --

    I see the first P2 based synthis on the sky;




  • I modified the audio_out demo a little. I encountered a problem with getcnt (). warning signed / unsigned, if I compare getcnt() with unsigned int.
    With inline assembler I was able to do a workaround.
    c
    c
    4K
  • ReinhardReinhard Posts: 489
    edited 2020-01-05 22:30
    here are more details over the problem.
    maybe I do a wrong use of getcnt()
    //while(getcnt() < stop)//pluck.c:87: warning: signed/unsigned comparison may not work properly
    //while(getcnt() < (int)stop)//no warning, but duration is sometimes not ok 
    while( tick < stop) //ok,tick come from asm
    
    fastspin -2b pluck.c
    Propeller Spin/PASM Compiler 'FastSpin' (c) 2011-2019 Total Spectrum Software Inc.
    Version 4.1.0-beta- Compiled on: Jan  5 2020
    pluck.c
    pluck.p2asm
    Done.
    Program size is 2400 bytes
    
    
  • You have to handle the case where the counter wraps back to zero. It's better to subtract the starting cycle count from the current count, and compare it to the total number of cycles, such as:
                    int duration_cycles = 160000 * duration_ms;
                    int start = getcnt();
    		while( getcnt() - start < duration_cycles)
    
    I played with your code, and added a decay to the notes. You can adjust the decay by changing the value of DECAY_RATE.
    c
    c
  • roglohrogloh Posts: 5,787
    edited 2020-01-06 00:12
    I tried this pluck1.c example. However only one audio channel output seems to be active at a time. Is it meant to be stereo or just mono?

    I can swap the L&R pin definitions in the code and the mono sound channel moves to the other speaker, so I think that means my HW+amp path is working for both channels. The left speaker is working when L=pin6 R=pin7, and the right one is quiet. This swaps when these pins get reversed in the code.


    Roger.

    EDIT: just looked at the PASM2 in the code in more detail, it was coded for left channel only by the looks of it which explains things.

    To get stereo just add this line to the inline asm in both while loops...

    wypin sample, #R_PIN
Sign In or Register to comment.