tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/tone/tone.go (about)

     1  package tone
     2  
     3  import (
     4  	"machine"
     5  )
     6  
     7  // PWM is the interface necessary for controlling a speaker.
     8  type PWM interface {
     9  	Configure(config machine.PWMConfig) error
    10  	Channel(pin machine.Pin) (channel uint8, err error)
    11  	Top() uint32
    12  	Set(channel uint8, value uint32)
    13  	SetPeriod(period uint64) error
    14  }
    15  
    16  // Speaker is a configured audio output channel based on a PWM.
    17  type Speaker struct {
    18  	pwm PWM
    19  	ch  uint8
    20  }
    21  
    22  // New returns a new Speaker instance readily configured for the given PWM and
    23  // pin combination. The lowest frequency possible is 27.5Hz, or A0. The audio
    24  // output uses a PWM so the audio will form a square wave, a sound that
    25  // generally sounds rather harsh.
    26  func New(pwm PWM, pin machine.Pin) (Speaker, error) {
    27  	err := pwm.Configure(machine.PWMConfig{
    28  		Period: uint64(1e9) / 55 / 2,
    29  	})
    30  	if err != nil {
    31  		return Speaker{}, err
    32  	}
    33  	ch, err := pwm.Channel(pin)
    34  	if err != nil {
    35  		return Speaker{}, err
    36  	}
    37  	return Speaker{pwm, ch}, nil
    38  }
    39  
    40  // Stop disables the speaker, setting the output to low continuously.
    41  func (s Speaker) Stop() {
    42  	s.pwm.Set(s.ch, 0)
    43  }
    44  
    45  // SetPeriod sets the period for the signal in nanoseconds. Use the following
    46  // formula to convert frequency to period:
    47  //
    48  //	period = 1e9 / frequency
    49  //
    50  // You can also use s.SetNote() instead for MIDI note numbers.
    51  func (s Speaker) SetPeriod(period uint64) {
    52  	// Disable output.
    53  	s.Stop()
    54  
    55  	if period == 0 {
    56  		// Assume a period of 0 is intended as "no output".
    57  		return
    58  	}
    59  
    60  	// Reconfigure period.
    61  	s.pwm.SetPeriod(period)
    62  
    63  	// Make this a square wave by setting the channel position to half the
    64  	// period.
    65  	s.pwm.Set(s.ch, s.pwm.Top()/2)
    66  }
    67  
    68  // SetNote starts playing the given note. For example, s.SetNote(C4) will
    69  // produce a 440Hz square wave tone.
    70  func (s Speaker) SetNote(note Note) {
    71  	period := note.Period()
    72  	s.SetPeriod(period)
    73  }