Shop OBEX P1 Docs P2 Docs Learn Events
Looking for an Audio Output (PWM?) Example — Parallax Forums

Looking for an Audio Output (PWM?) Example

David BetzDavid Betz Posts: 14,511
edited 2020-05-21 17:57 in Propeller 2
Is there an example showing how to configure a smart pin for PWM output?
«134

Comments

  • I may have asked the wrong question. What I want to do is write some audio code and output through a DAC using the A/V accessory board. What mode should I use for that?
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2020-05-21 00:39
    There's Rayman's wave player code you could look at and I also have TAQOZ code for a wave player although I will probably check to see if I need to bring that up to date for revB silicon since there wasn't any info around at the time I did this. I am just using my DAC function to setup the pin and writing samples to WYPIN.
    pub DAC ( bits -- )		%101_000_0000000_01_00011 WRFNC |< WXPIN ;
    ( usage: 50 PIN 12 DAC )
    
    WRFNC just left shifts the function code and combines it with any other drive modes (normally 0) before it does a WRPIN.
    |< converts the bits parameter to a mask before writing to WXPIN.
    Then the audio samples are written to WYPIN.

    While I get mine to work, and it works well, I'm sure it could make better use of the DAC modes when I get around to looking at it again.

  • I found the @Rayman wrote for the WAV player but I don't understand what this is supposed to be doing:
                rdword  right,pData
                add     right,##$8000 
                and     right,##$FFFF
    
  • ElectrodudeElectrodude Posts: 1,621
    edited 2020-05-21 01:39
    The last two instructions convert it from a signed 16-bit value (-$8000 to $7FFF) to an offset unsigned 16-bit value ($0000-$FFFF). It should be possible to replace the last two instructions with "bitnot right, #15". This optimization won't ever cause overflow, since rdword only gets you a 16 bit value.
  • The last two instructions convert it from a signed 16-bit value (-$8000 to $7FFF) to an offset unsigned 16-bit value ($0000-$FFFF). It should be possible to replace the last two instructions with "bitnot right, #15". This optimization won't ever cause overflow, since rdword only gets you a 16 bit value.
    Thanks for the explanation!
  • evanhevanh Posts: 15,169
    David Betz wrote: »
    I may have asked the wrong question. What I want to do is write some audio code and output through a DAC using the A/V accessory board. What mode should I use for that?
    There's a few ways: You can set the DAC level as a low-level pin setting with a WRPIN. Or use one of a couple of smartpin modes which both perform a dither to extend beyond 8-bit effective. Or SETDACS can be used. Or a streamer for up to four 8-bit DACs on a single hubRAM DMA channel.

    In all cases the low-level DAC has to be setup in the P field of a WRPIN.

  • Ahle2Ahle2 Posts: 1,178
    What exactly do you want to do? You can have a look at reSound if you want something quite usable! If you want to do it yourself, there are numerous threads discussion audio stuff. I can go through my discussion history and post some links if you want to.

    /Johannes
  • AribaAriba Posts: 2,682
    Here is a Spin2 example to output a sine wave:
    http://forums.parallax.com/discussion/comment/1496551/#Comment_1496551
  • Ariba wrote: »
    Thanks for the Spin2 example.

  • Ahle2 wrote: »
    What exactly do you want to do? You can have a look at reSound if you want something quite usable! If you want to do it yourself, there are numerous threads discussion audio stuff. I can go through my discussion history and post some links if you want to.

    /Johannes
    I want to port a program called Auduino to the P2 (and maybe to the P1 as well). It's a simple granular synthesizer.

    https://code.google.com/archive/p/tinkerit/wikis/Auduino.wiki

  • Ahle2Ahle2 Posts: 1,178
    edited 2020-05-21 17:46
    I see...
    A grain is basically a soundbuffer of a very small size that you apply realtime frequency control, volume control and the ability to change the data ptr on the fly. Having a lot of grains mixed in realtime into two stereo channels is the basis of granular synthesis. Sounds almost like a perfect match for reSound. Up to 64 grains could be mixed in realtime with independant frequency, volume, panning and grain size. The driver supports everything, the API just needs a method to set the grain parameters. It was actually on my todo list + a very basic granular synth examples. It wouldn't take much effort to implement it since it basically is just setting some parameters in hub that reSound already reads in at the mixing rate.
  • JonnyMacJonnyMac Posts: 8,918
    edited 2020-05-21 17:48
    I wrote a pwm object for an upcoming N&V article on the P2. The servo version uses the same smartpin mode, just in a specialized way.

    You may find these very simple -- they're intended to be beginner-friendly.

    Edit: Whoops, I posted very late and in response to your first quote -- these may not be helpful.
  • JonnyMac wrote: »
    I wrote a pwm object for an upcoming N&V article on the P2. The servo version uses the same smartpin mode, just in a specialized way.

    You may find these very simple -- they're intended to be beginner-friendly.

    Edit: Whoops, I posted very late and in response to your first quote -- these may not be helpful.
    Thanks, Jon. I updated the thread title to indicate the shift in topic. Isn't DAC output pretty similar to PWM anyway?

  • ElectrodudeElectrodude Posts: 1,621
    edited 2020-05-22 17:58
    David Betz wrote: »
    Isn't DAC output pretty similar to PWM anyway?

    The P2 has real 8-bit R2R DACs; it's not just PWM. Smartpins can be used to do dithering to achieve 16 effective bits, which is effectively using PWM to append 8 more bits to an 8 bit value. Using the DACs should sound a lot better, i.e. have a lot less noise, than just using plain PWM.

    EDIT: It's apparently a resistor-string DAC, not an R2R DAC. See Ariba's post below if you're reading this in the future.
  • I use this method for putting a frequency on a pin. Using P_NCO_MODE, the output duty cycle will always be 50%.
    pub nco_freq(pin, fr01)
    
    '' Output frequency on pin
    '' -- fr01 is in 0.1Hz units
    ''    * set to 0 to stop output
    
      if (fr01 > 0)
        pinstart(pin, P_NCO_FREQ | P_OE, 10, fr01 frac CLK_FREQ)
      else
        pinclear(pin)   
        pinf(pin)
    
  • So I guess I have an idea of how to use DAC mode. What about ADC mode? This synth takes input in the form of a bunch of pots connected to analog pins.
  • The P2 has real 8-bit R2R DACs; it's not just PWM. Smartpins can be used to do dithering to achieve 16 effective bits, which is effectively using PWM to append 8 more bits to an 8 bit value. Using the DACs should sound a lot better, i.e. have a lot less noise, than just using plain PWM.

    Depends... R2R DACs have some non-linearity, i.e. it could happen that each of the 256 steps are not exactly the same size if the Rs are not perfectly matched. So DAC mode has less noise (if you only use simple RC filtering) but more inter-modulation and distortion.

    For speech distortion doesn't matter much. There are even cases where you want distortion. E-guitars and old synthesizers (C64...) sound better if you have a little bit inter-modulation. But if you want to play high quality music I'd go for PWM with good filtering. A 3rd order sallen-key filter can be built with only one OP amp.

  • Ahle2Ahle2 Posts: 1,178
    David,

    Here's a very basic stereo audio example code in pasm...
    CON
      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 
    
    DAT
        org
    
        wrpin   ##SMARTPIN_DAC_MODE, #L_PIN   ' Config smartpin DAC mode on left pin
        wrpin   ##SMARTPIN_DAC_MODE, #R_PIN   ' Config smartpin DAC mode on right 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")
    
    loop
        wypin    sawWave, #L_PIN              ' Output sample on left channel
        wypin    sawWave, #R_PIN              ' Output sample on right channel
        waitse1                               ' Wait for smart pin NCO wrap, new period
        add      sawWave, #1
        jmp      #loop
    
    sawWave	 res      1
    
  • Ahle2Ahle2 Posts: 1,178
    edited 2020-05-22 12:21
    @ManAtWork
    But if you want to play high quality music I'd go for PWM with good filtering.
    The only problem is that you would need 2^16 * 44100 = 2.8 GHz system clock to even achive CD quality output, it's not feasible. We can achive a little bit better than 12 bits of resolution for 44100 samples/sec using an overclocked P2. There are noise shaping techniques that could be applied to get better result, but I doubt the end result will be better than what we already have with chips 8bit-PWM. The perceived inter-modulation and distortion is quite low on the P2 from what I've heard (using two sine functions at different frequencies), but I will make some real measurements to confirm this. It should differ a little between pins and chips, but doing the measurement on more than one pin/chip will give us a clue.
  • Ahle2Ahle2 Posts: 1,178
    One more thing regarding my example above...
    You can't update the sample more often than what the sample period dictates because the smart pin will only update the value on each NCO rollover. Therefore it's necessary to have that "waitse1" to sync everything.
  • AribaAriba Posts: 2,682
    ManAtWork wrote: »
    The P2 has real 8-bit R2R DACs; it's not just PWM. Smartpins can be used to do dithering to achieve 16 effective bits, which is effectively using PWM to append 8 more bits to an 8 bit value. Using the DACs should sound a lot better, i.e. have a lot less noise, than just using plain PWM.

    Depends... R2R DACs have some non-linearity, i.e. it could happen that each of the 256 steps are not exactly the same size if the Rs are not perfectly matched. So DAC mode has less noise (if you only use simple RC filtering) but more inter-modulation and distortion.

    For speech distortion doesn't matter much. There are even cases where you want distortion. E-guitars and old synthesizers (C64...) sound better if you have a little bit inter-modulation. But if you want to play high quality music I'd go for PWM with good filtering. A 3rd order sallen-key filter can be built with only one OP amp.

    Chip said, it's a resistor string DAC, so there are 255 equal resistors in series and a 256 to 1 multiplexer that selects the output voltage.
    This should work much better than a R2R DAC for 16 bit dithering.

    Andy
  • I did run a test on the dacs to check their linearity. Its graphed vs output code, the linearity deviated around 0.2 lsb from ideal. There would be ways to straighten that out if needed.

  • Heres the post with the graphed dac linearity data
    https://forums.parallax.com/discussion/comment/1452997/#Comment_1452997
  • Ahle2Ahle2 Posts: 1,178
    edited 2020-05-22 14:35
    And I forgot one more thing regarding my example above...
    If you choose to use the PWM mode, it's important to have a sample period that is a multiple of 256 to get the best results; In other words the 8 LSB's needs to be zero. The reason is that the PWM has 8bit = 256 CPU cycles of resolution. Not having a multiple of 256 will possible give you audible jitter/distortion. If it actually is audible depends on the ratio of the period to 256 and the sample rate. A sample period with bit 7 set will not be audible if the sample rate is 88200 Hz, because the jitter will be above the upper limit of the human hearing. (Dogs may complain though! ;) )

  • David BetzDavid Betz Posts: 14,511
    edited 2020-05-22 15:04
    (deleted stupid question)
  • Do I need to do anything other than this to get the DAC running?
    #define AV_BasePin  8
    #define LeftPin     (AV_BasePin+6)
    #define RightPin    (AV_BasePin+7)
    
    #define SAMPLE_RATE 32000
    
    void setup()
    {
        _pinstart(LeftPin, p_dac_dither_rnd | p_dac_600r_2v | p_oe, _CLOCKFREQ / SAMPLE_RATE, 0);
        _pinstart(RightPin, p_dac_dither_rnd | p_dac_600r_2v | p_oe, _CLOCKFREQ / SAMPLE_RATE, 0);
        _pinh(LED_PIN);
    }
    
    After that I just call _wypin(LeftPin, x) or _wypin(RightPin, x) to update the value being output. Should that work?
  • Ariba wrote: »
    Chip said, it's a resistor string DAC, so there are 255 equal resistors in series and a 256 to 1 multiplexer that selects the output voltage.
    This should work much better than a R2R DAC for 16 bit dithering.
    Tubular wrote: »
    Heres the post with the graphed dac linearity data
    https://forums.parallax.com/discussion/comment/1452997/#Comment_1452997

    Ok, it really looks like it's a 256:1 mux instead of a R-2R ladder. If it was R-2R there should be larger steps at transitions where many bits change, especially from $7F to $80. So please forget my 1st post, DAC mode seems to be the better way compared to PWM.
  • Where can I find Spin2 documentation? I need an explanation of what the FRAC operator does. I looked at the fastspin docs but they only described the differences between Fastspin's P2 support and PNut. Is there a Spin2 language description somewhere?
  • Cluso99Cluso99 Posts: 18,069
    Here is a link to the pnut thread, first post. There is a link there.
    forums.parallax.com/discussion/171196/pnut-spin2-latest-version-v34s
  • evanhevanh Posts: 15,169
    It's the QX half of result of QFRAC or QDIV cordic instructions. Both do a 64-bit / 32-bit -> 32-bit(QX) r 32-bit(QY).

    QFRAC x,y effectively has an implicit 32-bit left shift of x. So that'll be the one used there. The actual difference is QDIV gets the dividend high 32 bits from the preceeding SETQ, or zero, and the low 32 bits from D, whereas QFRAC gets the dividend low 32 bits from the preceeding SETQ, or zero, and the high 32 bits from D.

Sign In or Register to comment.