mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-04 01:28:45 -04:00
style: add comments to the public methods and members in the root package.
This commit is contained in:
parent
60e4518230
commit
a9b90c4db8
8
audio.go
8
audio.go
@ -1,10 +1,18 @@
|
|||||||
package sointu
|
package sointu
|
||||||
|
|
||||||
|
// AudioSink represents something where we can send audio e.g. audio output.
|
||||||
|
// WriteAudio should block if not ready to accept audio e.g. buffer full.
|
||||||
type AudioSink interface {
|
type AudioSink interface {
|
||||||
WriteAudio(buffer []float32) error
|
WriteAudio(buffer []float32) error
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AudioContext represents the low-level audio drivers. There should be at most
|
||||||
|
// one AudioContext at a time. The interface is implemented at least by
|
||||||
|
// oto.OtoContext, but in future we could also mock it.
|
||||||
|
//
|
||||||
|
// AudioContext is used to create one or more AudioSinks with Output(); each can
|
||||||
|
// be used to output separate sound & closed when done.
|
||||||
type AudioContext interface {
|
type AudioContext interface {
|
||||||
Output() AudioSink
|
Output() AudioSink
|
||||||
Close() error
|
Close() error
|
||||||
|
@ -7,6 +7,11 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Wav converts a stereo signal of 32-bit floats (L R L R..., length should be
|
||||||
|
// divisible by 2) into a valid WAV-file, returned as a []byte array.
|
||||||
|
//
|
||||||
|
// If pcm16 is set to true, the samples in the WAV-file will be 16-bit signed
|
||||||
|
// integers; otherwise the samples will be 32-bit floats
|
||||||
func Wav(buffer []float32, pcm16 bool) ([]byte, error) {
|
func Wav(buffer []float32, pcm16 bool) ([]byte, error) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
wavHeader(len(buffer), pcm16, buf)
|
wavHeader(len(buffer), pcm16, buf)
|
||||||
@ -17,6 +22,11 @@ func Wav(buffer []float32, pcm16 bool) ([]byte, error) {
|
|||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raw converts a stereo signal of 32-bit floats (L R L R..., length should be
|
||||||
|
// divisible by 2) into a raw audio file, returned as a []byte array.
|
||||||
|
//
|
||||||
|
// If pcm16 is set to true, the samples will be 16-bit signed integers;
|
||||||
|
// otherwise the samples will be 32-bit floats
|
||||||
func Raw(buffer []float32, pcm16 bool) ([]byte, error) {
|
func Raw(buffer []float32, pcm16 bool) ([]byte, error) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
err := rawToBuffer(buffer, pcm16, buf)
|
err := rawToBuffer(buffer, pcm16, buf)
|
||||||
|
@ -8,6 +8,7 @@ type Instrument struct {
|
|||||||
Units []Unit
|
Units []Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of an Instrument
|
||||||
func (instr *Instrument) Copy() Instrument {
|
func (instr *Instrument) Copy() Instrument {
|
||||||
units := make([]Unit, len(instr.Units))
|
units := make([]Unit, len(instr.Units))
|
||||||
for i, u := range instr.Units {
|
for i, u := range instr.Units {
|
||||||
|
2
order.go
2
order.go
@ -6,6 +6,7 @@ package sointu
|
|||||||
// necessary amount when a new item is added, filling the unused slots with -1s.
|
// necessary amount when a new item is added, filling the unused slots with -1s.
|
||||||
type Order []int
|
type Order []int
|
||||||
|
|
||||||
|
// Get returns the value at index; or -1 is the index is out of range
|
||||||
func (s Order) Get(index int) int {
|
func (s Order) Get(index int) int {
|
||||||
if index < 0 || index >= len(s) {
|
if index < 0 || index >= len(s) {
|
||||||
return -1
|
return -1
|
||||||
@ -13,6 +14,7 @@ func (s Order) Get(index int) int {
|
|||||||
return s[index]
|
return s[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set sets the value at index; appending -1s until the slice is long enough.
|
||||||
func (s *Order) Set(index, value int) {
|
func (s *Order) Set(index, value int) {
|
||||||
for len(*s) <= index {
|
for len(*s) <= index {
|
||||||
*s = append(*s, -1)
|
*s = append(*s, -1)
|
||||||
|
23
patch.go
23
patch.go
@ -9,6 +9,7 @@ import (
|
|||||||
// Patch is simply a list of instruments used in a song
|
// Patch is simply a list of instruments used in a song
|
||||||
type Patch []Instrument
|
type Patch []Instrument
|
||||||
|
|
||||||
|
// Copy makes a deep copy of a Patch.
|
||||||
func (p Patch) Copy() Patch {
|
func (p Patch) Copy() Patch {
|
||||||
instruments := make([]Instrument, len(p))
|
instruments := make([]Instrument, len(p))
|
||||||
for i, instr := range p {
|
for i, instr := range p {
|
||||||
@ -17,6 +18,8 @@ func (p Patch) Copy() Patch {
|
|||||||
return instruments
|
return instruments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumVoices returns the total number of voices used in the patch; summing the
|
||||||
|
// voices of every instrument
|
||||||
func (p Patch) NumVoices() int {
|
func (p Patch) NumVoices() int {
|
||||||
ret := 0
|
ret := 0
|
||||||
for _, i := range p {
|
for _, i := range p {
|
||||||
@ -25,6 +28,8 @@ func (p Patch) NumVoices() int {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumDelayLines return the total number of delay lines used in the patch;
|
||||||
|
// summing the number of delay lines of every delay unit in every instrument
|
||||||
func (p Patch) NumDelayLines() int {
|
func (p Patch) NumDelayLines() int {
|
||||||
total := 0
|
total := 0
|
||||||
for _, instr := range p {
|
for _, instr := range p {
|
||||||
@ -37,6 +42,8 @@ func (p Patch) NumDelayLines() int {
|
|||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumSyns return the total number of sync outputs used in the patch; summing
|
||||||
|
// the number of sync outputs of every sync unit in every instrument
|
||||||
func (p Patch) NumSyncs() int {
|
func (p Patch) NumSyncs() int {
|
||||||
total := 0
|
total := 0
|
||||||
for _, instr := range p {
|
for _, instr := range p {
|
||||||
@ -49,6 +56,11 @@ func (p Patch) NumSyncs() int {
|
|||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FirstVoiceForInstrument returns the index of the first voice of given
|
||||||
|
// instrument. For example, if the Patch has three instruments (0, 1 and 2),
|
||||||
|
// with 1, 3, 2 voices, respectively, then FirstVoiceForInstrument(0) returns 0,
|
||||||
|
// FirstVoiceForInstrument(1) returns 1 and FirstVoiceForInstrument(2) returns
|
||||||
|
// 4. Essentially computes just the cumulative sum.
|
||||||
func (p Patch) FirstVoiceForInstrument(instrIndex int) int {
|
func (p Patch) FirstVoiceForInstrument(instrIndex int) int {
|
||||||
ret := 0
|
ret := 0
|
||||||
for _, t := range p[:instrIndex] {
|
for _, t := range p[:instrIndex] {
|
||||||
@ -57,6 +69,10 @@ func (p Patch) FirstVoiceForInstrument(instrIndex int) int {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InstrumentForVoice returns the instrument number for the given voice index.
|
||||||
|
// For example, if the Patch has three instruments (0, 1 and 2), with 1, 3, 2
|
||||||
|
// voices, respectively, then InstrumentForVoice(0) returns 0,
|
||||||
|
// InstrumentForVoice(1) returns 1 and InstrumentForVoice(3) returns 1.
|
||||||
func (p Patch) InstrumentForVoice(voice int) (int, error) {
|
func (p Patch) InstrumentForVoice(voice int) (int, error) {
|
||||||
if voice < 0 {
|
if voice < 0 {
|
||||||
return 0, errors.New("voice cannot be negative")
|
return 0, errors.New("voice cannot be negative")
|
||||||
@ -70,6 +86,11 @@ func (p Patch) InstrumentForVoice(voice int) (int, error) {
|
|||||||
return 0, errors.New("voice number is beyond the total voices of an instrument")
|
return 0, errors.New("voice number is beyond the total voices of an instrument")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindSendTarget searches the instrument number and unit index for a unit with
|
||||||
|
// the given id. Two units should never have the same id, but if they do, then
|
||||||
|
// the first match is returned. Id 0 is interpreted as "no id", thus searching
|
||||||
|
// for id 0 returns an error. Error is also returned if the searched id is not
|
||||||
|
// found.
|
||||||
func (p Patch) FindSendTarget(id int) (int, int, error) {
|
func (p Patch) FindSendTarget(id int) (int, int, error) {
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
return 0, 0, errors.New("send targets unit id 0")
|
return 0, 0, errors.New("send targets unit id 0")
|
||||||
@ -84,6 +105,8 @@ func (p Patch) FindSendTarget(id int) (int, int, error) {
|
|||||||
return 0, 0, fmt.Errorf("send targets an unit with id %v, could not find a unit with such an ID in the patch", id)
|
return 0, 0, fmt.Errorf("send targets an unit with id %v, could not find a unit with such an ID in the patch", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParamHintString returns a human readable string representing the current
|
||||||
|
// value of a given unit parameter.
|
||||||
func (p Patch) ParamHintString(instrIndex, unitIndex int, param string) string {
|
func (p Patch) ParamHintString(instrIndex, unitIndex int, param string) string {
|
||||||
if instrIndex < 0 || instrIndex >= len(p) {
|
if instrIndex < 0 || instrIndex >= len(p) {
|
||||||
return ""
|
return ""
|
||||||
|
@ -6,6 +6,7 @@ package sointu
|
|||||||
// necessary amount when a new item is added, filling the unused slots with 1s.
|
// necessary amount when a new item is added, filling the unused slots with 1s.
|
||||||
type Pattern []byte
|
type Pattern []byte
|
||||||
|
|
||||||
|
// Get returns the value at index; or 1 is the index is out of range
|
||||||
func (s Pattern) Get(index int) byte {
|
func (s Pattern) Get(index int) byte {
|
||||||
if index < 0 || index >= len(s) {
|
if index < 0 || index >= len(s) {
|
||||||
return 1
|
return 1
|
||||||
@ -13,6 +14,7 @@ func (s Pattern) Get(index int) byte {
|
|||||||
return s[index]
|
return s[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set sets the value at index; appending 1s until the slice is long enough.
|
||||||
func (s *Pattern) Set(index int, value byte) {
|
func (s *Pattern) Set(index int, value byte) {
|
||||||
for len(*s) <= index {
|
for len(*s) <= index {
|
||||||
*s = append(*s, 1)
|
*s = append(*s, 1)
|
||||||
|
16
score.go
16
score.go
@ -1,11 +1,16 @@
|
|||||||
package sointu
|
package sointu
|
||||||
|
|
||||||
|
// Score represents the arrangement of notes in a song; just a list of tracks
|
||||||
|
// and RowsPerPattern and Length (in patterns) to know the desired length of a
|
||||||
|
// song in rows. If any of the tracks is too short, all the notes outside the
|
||||||
|
// range should be just considered as holding the last note.
|
||||||
type Score struct {
|
type Score struct {
|
||||||
Tracks []Track
|
Tracks []Track
|
||||||
RowsPerPattern int
|
RowsPerPattern int // number of rows in each pattern
|
||||||
Length int // length of the song, in number of patterns
|
Length int // length of the song, in number of patterns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of a Score.
|
||||||
func (l Score) Copy() Score {
|
func (l Score) Copy() Score {
|
||||||
tracks := make([]Track, len(l.Tracks))
|
tracks := make([]Track, len(l.Tracks))
|
||||||
for i, t := range l.Tracks {
|
for i, t := range l.Tracks {
|
||||||
@ -14,6 +19,8 @@ func (l Score) Copy() Score {
|
|||||||
return Score{Tracks: tracks, RowsPerPattern: l.RowsPerPattern, Length: l.Length}
|
return Score{Tracks: tracks, RowsPerPattern: l.RowsPerPattern, Length: l.Length}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumVoices returns the total number of voices used in the Score; summing the
|
||||||
|
// voices of every track
|
||||||
func (l Score) NumVoices() int {
|
func (l Score) NumVoices() int {
|
||||||
ret := 0
|
ret := 0
|
||||||
for _, t := range l.Tracks {
|
for _, t := range l.Tracks {
|
||||||
@ -22,6 +29,11 @@ func (l Score) NumVoices() int {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FirstVoiceForTrack returns the index of the first voice of given track. For
|
||||||
|
// example, if the Score has three tracks (0, 1 and 2), with 1, 3, 2 voices,
|
||||||
|
// respectively, then FirstVoiceForTrack(0) returns 0, FirstVoiceForTrack(1)
|
||||||
|
// returns 1 and FirstVoiceForTrack(2) returns 4. Essentially computes just the
|
||||||
|
// cumulative sum.
|
||||||
func (l Score) FirstVoiceForTrack(track int) int {
|
func (l Score) FirstVoiceForTrack(track int) int {
|
||||||
ret := 0
|
ret := 0
|
||||||
for _, t := range l.Tracks[:track] {
|
for _, t := range l.Tracks[:track] {
|
||||||
@ -30,6 +42,8 @@ func (l Score) FirstVoiceForTrack(track int) int {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LengthInRows returns just RowsPerPattern * Length, as the length is the
|
||||||
|
// length in the number of patterns.
|
||||||
func (l Score) LengthInRows() int {
|
func (l Score) LengthInRows() int {
|
||||||
return l.RowsPerPattern * l.Length
|
return l.RowsPerPattern * l.Length
|
||||||
}
|
}
|
||||||
|
14
song.go
14
song.go
@ -4,6 +4,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Song includes a Score(the arrangement of notes in the song in one or more
|
||||||
|
// tracks) and a Patch (the list of one or more instruments). Additionally, BPM
|
||||||
|
// and RowsPerBeat fields set how fast the song should be played. Currently, BPM
|
||||||
|
// is an integer as it offers already quite much granularity for controlling the
|
||||||
|
// playback speed, but this could be changed to a floating point in future if
|
||||||
|
// finer adjustments are necessary.
|
||||||
type Song struct {
|
type Song struct {
|
||||||
BPM int
|
BPM int
|
||||||
RowsPerBeat int
|
RowsPerBeat int
|
||||||
@ -11,16 +17,20 @@ type Song struct {
|
|||||||
Patch Patch
|
Patch Patch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of a Score.
|
||||||
func (s *Song) Copy() Song {
|
func (s *Song) Copy() Song {
|
||||||
return Song{BPM: s.BPM, RowsPerBeat: s.RowsPerBeat, Score: s.Score.Copy(), Patch: s.Patch.Copy()}
|
return Song{BPM: s.BPM, RowsPerBeat: s.RowsPerBeat, Score: s.Score.Copy(), Patch: s.Patch.Copy()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assuming 44100 Hz playback speed, return the number of samples of each row of
|
||||||
|
// the song.
|
||||||
func (s *Song) SamplesPerRow() int {
|
func (s *Song) SamplesPerRow() int {
|
||||||
return 44100 * 60 / (s.BPM * s.RowsPerBeat)
|
return 44100 * 60 / (s.BPM * s.RowsPerBeat)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TBD: Where shall we put methods that work on pure domain types and have no dependencies
|
// Validate checks if the Song looks like a valid song: BPM > 0, one or more
|
||||||
// e.g. Validate here
|
// tracks, score uses less than or equal number of voices than patch. Not used
|
||||||
|
// much so we could probably get rid of this function.
|
||||||
func (s *Song) Validate() error {
|
func (s *Song) Validate() error {
|
||||||
if s.BPM < 1 {
|
if s.BPM < 1 {
|
||||||
return errors.New("BPM should be > 0")
|
return errors.New("BPM should be > 0")
|
||||||
|
28
synth.go
28
synth.go
@ -6,17 +6,41 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Synth represents a state of a synthesizer, compiled from a Patch.
|
||||||
type Synth interface {
|
type Synth interface {
|
||||||
|
// Render tries to fill a stereo signal buffer with sound from the
|
||||||
|
// synthesizer, until either the buffer is full or a given number of
|
||||||
|
// timesteps is advanced. In the process, it also fills the syncbuffer with
|
||||||
|
// the values output by sync units. Normally, 1 sample = 1 unit of time, but
|
||||||
|
// speed modulations may change this. It returns the number of samples
|
||||||
|
// filled (! in stereo samples, so the buffer will have 2 * sample floats),
|
||||||
|
// the number of sync outputs written, the number of time steps time
|
||||||
|
// advanced, and a possible error.
|
||||||
Render(buffer []float32, syncBuffer []float32, maxtime int) (sample int, syncs int, time int, err error)
|
Render(buffer []float32, syncBuffer []float32, maxtime int) (sample int, syncs int, time int, err error)
|
||||||
|
|
||||||
|
// Update recompiles a patch, but should maintain as much as possible of its
|
||||||
|
// state as reasonable. For example, filters should keep their state and
|
||||||
|
// delaylines should keep their content. Every change in the Patch triggers
|
||||||
|
// an Update and if the Patch would be started fresh every time, it would
|
||||||
|
// lead to very choppy audio.
|
||||||
Update(patch Patch) error
|
Update(patch Patch) error
|
||||||
|
|
||||||
|
// Trigger triggers a note for a given voice. Called between synth.Renders.
|
||||||
Trigger(voice int, note byte)
|
Trigger(voice int, note byte)
|
||||||
|
|
||||||
|
// Release releases the currently playing note for a given voice. Called
|
||||||
|
// between synth.Renders.
|
||||||
Release(voice int)
|
Release(voice int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SynthService compiles a given Patch into a Synth, throwing errors if the
|
||||||
|
// Patch is malformed.
|
||||||
type SynthService interface {
|
type SynthService interface {
|
||||||
Compile(patch Patch) (Synth, error)
|
Compile(patch Patch) (Synth, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render fills an stereo audio buffer using a Synth, disregarding all syncs and
|
||||||
|
// time limits.
|
||||||
func Render(synth Synth, buffer []float32) error {
|
func Render(synth Synth, buffer []float32) error {
|
||||||
s, _, _, err := synth.Render(buffer, nil, math.MaxInt32)
|
s, _, _, err := synth.Render(buffer, nil, math.MaxInt32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -28,6 +52,10 @@ func Render(synth Synth, buffer []float32) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Play plays the Song using a given Synth, returning the stereo audio buffer
|
||||||
|
// and the sync buffer as a result (and possible errors). This is a bit
|
||||||
|
// illogical as the Song contains already the Patch; this could be probably
|
||||||
|
// refactored to just accept a SynthService and a Song.
|
||||||
func Play(synth Synth, song Song) ([]float32, []float32, error) {
|
func Play(synth Synth, song Song) ([]float32, []float32, error) {
|
||||||
err := song.Validate()
|
err := song.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
19
track.go
19
track.go
@ -1,12 +1,31 @@
|
|||||||
package sointu
|
package sointu
|
||||||
|
|
||||||
|
// Track represents the patterns and orderlist for each track. Note that each
|
||||||
|
// track has its own patterns, so one track cannot use another tracks patterns.
|
||||||
|
// This makes the data more intuitive to humans, as the reusing of patterns over
|
||||||
|
// tracks is a rather rare occurence. However, the compiler will put all the
|
||||||
|
// patterns in one global table (identical patterns only appearing once), to
|
||||||
|
// optimize the runtime code.
|
||||||
type Track struct {
|
type Track struct {
|
||||||
|
// NumVoices is the number of voices this track triggers, cycling through
|
||||||
|
// the voices. When this track triggers a new voice, the previous should be
|
||||||
|
// released.
|
||||||
NumVoices int
|
NumVoices int
|
||||||
|
|
||||||
|
// Effect hints the GUI if this is more of an effect track than a note
|
||||||
|
// track: if true, e.g. the GUI can display the values as hexadecimals
|
||||||
|
// instead of note values.
|
||||||
Effect bool `yaml:",omitempty"`
|
Effect bool `yaml:",omitempty"`
|
||||||
|
|
||||||
|
// Order is a list telling which pattern comes in which order in the song in
|
||||||
|
// this track.
|
||||||
Order Order `yaml:",flow"`
|
Order Order `yaml:",flow"`
|
||||||
|
|
||||||
|
// Patterns is a list of Patterns for this track.
|
||||||
Patterns []Pattern `yaml:",flow"`
|
Patterns []Pattern `yaml:",flow"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of a Track.
|
||||||
func (t *Track) Copy() Track {
|
func (t *Track) Copy() Track {
|
||||||
order := make([]int, len(t.Order))
|
order := make([]int, len(t.Order))
|
||||||
copy(order, t.Order)
|
copy(order, t.Order)
|
||||||
|
32
unit.go
32
unit.go
@ -2,12 +2,34 @@ package sointu
|
|||||||
|
|
||||||
// Unit is e.g. a filter, oscillator, envelope and its parameters
|
// Unit is e.g. a filter, oscillator, envelope and its parameters
|
||||||
type Unit struct {
|
type Unit struct {
|
||||||
|
// Type is the type of the unit, e.g. "add","oscillator" or "envelope".
|
||||||
|
// Always in lowercase. "" type should be ignored, no invalid types should
|
||||||
|
// be used.
|
||||||
Type string `yaml:",omitempty"`
|
Type string `yaml:",omitempty"`
|
||||||
|
|
||||||
|
// ID should be a unique ID for this unit, used by SEND units to target
|
||||||
|
// specific units. ID = 0 means that no ID has been given to a unit and thus
|
||||||
|
// cannot be targeted by SENDs. When possible, units that are not targeted
|
||||||
|
// by any SENDs should be cleaned from having IDs, e.g. to keep the exported
|
||||||
|
// data clean.
|
||||||
ID int `yaml:",omitempty"`
|
ID int `yaml:",omitempty"`
|
||||||
|
|
||||||
|
// Parameters is a map[string]int of parameters of a unit. For example, for
|
||||||
|
// an oscillator, unit.Type == "oscillator" and unit.Parameters["attack"]
|
||||||
|
// could be 64. Most parameters are either limites to 0 and 1 (e.g. stereo
|
||||||
|
// parameters) or between 0 and 128, inclusive.
|
||||||
Parameters map[string]int `yaml:",flow"`
|
Parameters map[string]int `yaml:",flow"`
|
||||||
|
|
||||||
|
// VarArgs is a list containing the variable number arguments that some
|
||||||
|
// units require, most notably the DELAY units. For example, for a DELAY
|
||||||
|
// unit, VarArgs is the delaytimes, in samples, of the different delaylines
|
||||||
|
// in the unit.
|
||||||
VarArgs []int `yaml:",flow,omitempty"`
|
VarArgs []int `yaml:",flow,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When unit.Type = "oscillator", its unit.Parameter["Type"] tells the type of
|
||||||
|
// the oscillator. There is five different oscillator types, so these consts
|
||||||
|
// just enumerate them.
|
||||||
const (
|
const (
|
||||||
Sine = iota
|
Sine = iota
|
||||||
Trisaw = iota
|
Trisaw = iota
|
||||||
@ -16,6 +38,7 @@ const (
|
|||||||
Sample = iota
|
Sample = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Copy makes a deep copy of a unit.
|
||||||
func (u *Unit) Copy() Unit {
|
func (u *Unit) Copy() Unit {
|
||||||
parameters := make(map[string]int)
|
parameters := make(map[string]int)
|
||||||
for k, v := range u.Parameters {
|
for k, v := range u.Parameters {
|
||||||
@ -26,6 +49,12 @@ func (u *Unit) Copy() Unit {
|
|||||||
return Unit{Type: u.Type, Parameters: parameters, VarArgs: varArgs, ID: u.ID}
|
return Unit{Type: u.Type, Parameters: parameters, VarArgs: varArgs, ID: u.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StackChange returns how this unit will affect the signal stack. "pop" and
|
||||||
|
// "addp" and such will consume the topmost signal, and thus return -1 (or -2,
|
||||||
|
// if the unit is a stereo unit). On the other hand, "oscillator" and "envelope"
|
||||||
|
// will produce a signal, and thus return 1 (or 2, if the unit is a stereo
|
||||||
|
// unit). Effects that just change the topmost signal and will not change the
|
||||||
|
// number of signals on the stack and thus return 0.
|
||||||
func (u *Unit) StackChange() int {
|
func (u *Unit) StackChange() int {
|
||||||
switch u.Type {
|
switch u.Type {
|
||||||
case "addp", "mulp", "pop", "out", "outaux", "aux":
|
case "addp", "mulp", "pop", "out", "outaux", "aux":
|
||||||
@ -42,6 +71,9 @@ func (u *Unit) StackChange() int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StackNeed returns the number of signals that should be on the stack before
|
||||||
|
// this unit is executed. Used to prevent stack underflow. Units producing
|
||||||
|
// signals do not care what is on the stack before and will return 0.
|
||||||
func (u *Unit) StackNeed() int {
|
func (u *Unit) StackNeed() int {
|
||||||
switch u.Type {
|
switch u.Type {
|
||||||
case "", "envelope", "oscillator", "noise", "receive", "loadnote", "loadval", "in":
|
case "", "envelope", "oscillator", "noise", "receive", "loadnote", "loadval", "in":
|
||||||
|
@ -134,6 +134,10 @@ var UnitTypes = map[string]([]UnitParameter){
|
|||||||
"sync": []UnitParameter{},
|
"sync": []UnitParameter{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ports is static map allowing quickly finding the parameters of a unit that
|
||||||
|
// can be modulated. This is populated based on the UnitTypes list during
|
||||||
|
// init(). Thus, should be immutable, but Go not supporting that, then this will
|
||||||
|
// have to suffice: DO NOT EVER CHANGE THIS MAP.
|
||||||
var Ports = make(map[string]([]string))
|
var Ports = make(map[string]([]string))
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user