Documentation ¶
Overview ¶
Package signal provides primitives for digital signal manipulations. Floating-point, fixed signed and unsigned signal types are supported. This package focuses on allocation optimisations as it's one of the most important aspects for DSP applications.
Basics ¶
Digital signal is a representation of a physical signal that is a sampled and quantized. It is discrete in time and amplitude. When analog signal is converted to digital, it goes through two steps: discretization and quantization. Discretization means that the signal is divided into equal intervals of time, and each interval is represented by a single measurement of amplitude. Quantization means each amplitude measurement is approximated by a value from a finite set.
The finite set of quantization values determines the digital signal representation. It differs from type to type:
floating: [-1, 1] signed [-2^(bitDepth-1), 2^(bitDepth-1)-1] unsigned [0, 2^bitDepth-1]
Floating-point signal can exceed this range without loosing signal data, but fixed-point signals will be clipped and meaningful signal values will be lost.
Signal buffer types ¶
In order to allocate any of signal buffers, an allocator should be used. Allocator defines what number of channels and capacity per channel allocated buffers will have:
alloc := signal.Allocator{Channels: 2, Capacity: 512}
This package offers types that represent floating-point and both signed/unsigned fixed-point signal buffers. They implement Floating, Signed and Unsigned interfaces respectively. Internally, signal buffers use a slice of built-in type to hold the data.
Fixed-point buffers require a bit depth to be provided at allocation time. It allows to use the same type to hold values of various bit depths:
alloc := signal.Allocator{Channels: 2, Capacity: 512} _ = alloc.Int64(signal.BitDepth32) // int-64 buffer with 32-bits per sample _ = alloc.Uint32(signal.BitDepth24) // uint-32 buffer with 24-bits per sample _ = alloc.Float64() // float-64 buffer
Signal buffers have semantics of golang-slices - they can be sliced or apended one to another. All operations respect number of channels within buffer, so slicing and appending always happens for all channels.
Write/Read values
There are multiple ways to write/read values from the signal buffers.
WriteT/ReadT functions allows to write or read the data in the format of single slice, where samples for different channels are interleaved:
[]T{R, L, R, L}
WriteStripedT/ReadStripedT functions, on the other hand, can use slice of slices, where each nested slice represents a single channel of signal:
[][]T{{R, R}, {L, L}}
It's possible to append samples to the buffers using AppendSample fucntion. However, in order to have more control over allocations, this function won't let the buffer grow beyond it's initial capacity. To achieve this, another buffer needs to be explicitly allocated.
The one can also iterate over signal buffers. Please, refer to examples for more details.
Pooling ¶
This package also provides a pool-backed allocator. It contains a sync.Pool per signal type. It serves the purpose of decreasing a GC during the runtime when many buffers are allocated. The pool operates with the interface types (Signed, Unsigned, Floating) for convenience and alignment with other functions of this package. It is safe for concurrent use by multiple goroutines.
Example (Iterate) ¶
This example demonstrates how to iterate over the buffer.
package main import ( "fmt" "pipelined.dev/signal" ) func main() { // allocate int64 buffer with 2 channels and capacity of 8 samples per channel buf := signal.Allocator{ Channels: 2, Capacity: 8, Length: 4, }.Int64(signal.BitDepth64) // write striped data signal.WriteStripedInt8([][]int8{{1, 1, 1, 1}, {2, 2, 2, 2}}, buf) // iterate over buffer interleaved data for i := 0; i < buf.Len(); i++ { fmt.Printf("%d", buf.Sample(i)) } for c := 0; c < buf.Channels(); c++ { fmt.Println() for i := 0; i < buf.Length(); i++ { fmt.Printf("%d", buf.Sample(buf.BufferIndex(c, i))) } } }
Output: 12121212 1111 2222
Example (Pool) ¶
This example demonstrates how to use pool to allocate buffers.
package main import ( "fmt" "pipelined.dev/signal" ) func main() { pool := signal.GetPoolAllocator(2, 0, 512) // producer allocates new buffers produceFunc := func(allocs int, p *signal.PoolAllocator, c chan<- signal.Floating) { for i := 0; i < allocs; i++ { buf := p.Float64() buf.AppendSample(1.0) c <- buf } close(c) } // consumer processes buffers and puts them back to the pool consumeFunc := func(p *signal.PoolAllocator, c <-chan signal.Floating, done chan struct{}) { for s := range c { fmt.Printf("Length: %d Capacity: %d\n", s.Length(), s.Capacity()) } close(done) } c := make(chan signal.Floating) done := make(chan struct{}) go produceFunc(10, pool, c) go consumeFunc(pool, c, done) <-done }
Output: Length: 1 Capacity: 512 Length: 1 Capacity: 512 Length: 1 Capacity: 512 Length: 1 Capacity: 512 Length: 1 Capacity: 512 Length: 1 Capacity: 512 Length: 1 Capacity: 512 Length: 1 Capacity: 512 Length: 1 Capacity: 512 Length: 1 Capacity: 512
Example (ReadWrite) ¶
This example demonstrates how read and write data to the buffer.
package main import ( "fmt" "pipelined.dev/signal" ) func main() { var output []int // allocate int64 buffer with 2 channels and capacity of 8 samples per channel buf := signal.Allocator{ Channels: 2, Capacity: 8, Length: 8, }.Int64(signal.BitDepth64) // write striped data signal.WriteStripedInt8([][]int8{{1, 1, 1, 1}, {2, 2, 2, 2}}, buf.Slice(0, 4)) // write interleaved data signal.WriteInt16([]int16{11, 22, 11, 22, 11, 22, 11, 22}, buf.Slice(4, 8)) output = make([]int, 16) // reset output signal.ReadInt(buf, output) // read data into output fmt.Println(output) output = make([]int, 16) // reset output signal.ReadInt(buf.Slice(0, 0), output) // reset buffer length to 0 and read data into output fmt.Println(output) }
Output: [1 2 1 2 1 2 1 2 11 22 11 22 11 22 11 22] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
Index ¶
- func AsFloating(s Signal, dst Floating) (read int)
- func AsSigned(s Signal, dst Signed) (read int)
- func AsUnsigned(s Signal, dst Unsigned) (read int)
- func ChannelLength(sliceLen, channels int) int
- func ClearPoolAllocatorCache()
- func FloatingAsFloating(src, dst Floating) int
- func FloatingAsSigned(src Floating, dst Signed) int
- func FloatingAsUnsigned(src Floating, dst Unsigned) int
- func ReadFloat32(src Floating, dst []float32) int
- func ReadFloat64(src Floating, dst []float64) int
- func ReadInt(src Signed, dst []int) int
- func ReadInt16(src Signed, dst []int16) int
- func ReadInt32(src Signed, dst []int32) int
- func ReadInt64(src Signed, dst []int64) int
- func ReadInt8(src Signed, dst []int8) int
- func ReadStripedFloat32(src Floating, dst [][]float32) (read int)
- func ReadStripedFloat64(src Floating, dst [][]float64) (read int)
- func ReadStripedInt(src Signed, dst [][]int) (read int)
- func ReadStripedInt16(src Signed, dst [][]int16) (read int)
- func ReadStripedInt32(src Signed, dst [][]int32) (read int)
- func ReadStripedInt64(src Signed, dst [][]int64) (read int)
- func ReadStripedInt8(src Signed, dst [][]int8) (read int)
- func ReadStripedUint(src Unsigned, dst [][]uint) (read int)
- func ReadStripedUint16(src Unsigned, dst [][]uint16) (read int)
- func ReadStripedUint32(src Unsigned, dst [][]uint32) (read int)
- func ReadStripedUint64(src Unsigned, dst [][]uint64) (read int)
- func ReadStripedUint8(src Unsigned, dst [][]uint8) (read int)
- func ReadUint(src Unsigned, dst []uint) int
- func ReadUint16(src Unsigned, dst []uint16) int
- func ReadUint32(src Unsigned, dst []uint32) int
- func ReadUint64(src Unsigned, dst []uint64) int
- func ReadUint8(src Unsigned, dst []uint8) int
- func Scale(high, low BitDepth) int64
- func SignedAsFloating(src Signed, dst Floating) int
- func SignedAsSigned(src, dst Signed) int
- func SignedAsUnsigned(src Signed, dst Unsigned) int
- func UnsignedAsFloating(src Unsigned, dst Floating) int
- func UnsignedAsSigned(src Unsigned, dst Signed) int
- func UnsignedAsUnsigned(src, dst Unsigned) int
- func WriteFloat32(src []float32, dst Floating) int
- func WriteFloat64(src []float64, dst Floating) int
- func WriteInt(src []int, dst Signed) int
- func WriteInt16(src []int16, dst Signed) int
- func WriteInt32(src []int32, dst Signed) int
- func WriteInt64(src []int64, dst Signed) int
- func WriteInt8(src []int8, dst Signed) int
- func WriteStripedFloat32(src [][]float32, dst Floating) (written int)
- func WriteStripedFloat64(src [][]float64, dst Floating) (written int)
- func WriteStripedInt(src [][]int, dst Signed) (written int)
- func WriteStripedInt16(src [][]int16, dst Signed) (written int)
- func WriteStripedInt32(src [][]int32, dst Signed) (written int)
- func WriteStripedInt64(src [][]int64, dst Signed) (written int)
- func WriteStripedInt8(src [][]int8, dst Signed) (written int)
- func WriteStripedUint(src [][]uint, dst Unsigned) (written int)
- func WriteStripedUint16(src [][]uint16, dst Unsigned) (written int)
- func WriteStripedUint32(src [][]uint32, dst Unsigned) (written int)
- func WriteStripedUint64(src [][]uint64, dst Unsigned) (written int)
- func WriteStripedUint8(src [][]uint8, dst Unsigned) (written int)
- func WriteUint(src []uint, dst Unsigned) int
- func WriteUint16(src []uint16, dst Unsigned) int
- func WriteUint32(src []uint32, dst Unsigned) int
- func WriteUint64(src []uint64, dst Unsigned) int
- func WriteUint8(src []uint8, dst Unsigned) int
- type Allocator
- func (a Allocator) Float32() Floating
- func (a Allocator) Float64() Floating
- func (a Allocator) Int16(bd BitDepth) Signed
- func (a Allocator) Int32(bd BitDepth) Signed
- func (a Allocator) Int64(bd BitDepth) Signed
- func (a Allocator) Int8(bd BitDepth) Signed
- func (a Allocator) Uint16(bd BitDepth) Unsigned
- func (a Allocator) Uint32(bd BitDepth) Unsigned
- func (a Allocator) Uint64(bd BitDepth) Unsigned
- func (a Allocator) Uint8(bd BitDepth) Unsigned
- type BitDepth
- type Fixed
- type Floating
- type Frequency
- type PoolAllocator
- func (p *PoolAllocator) Float32() Floating
- func (p *PoolAllocator) Float64() Floating
- func (p *PoolAllocator) Int16(bd BitDepth) Signed
- func (p *PoolAllocator) Int32(bd BitDepth) Signed
- func (p *PoolAllocator) Int64(bd BitDepth) Signed
- func (p *PoolAllocator) Int8(bd BitDepth) Signed
- func (p *PoolAllocator) Uint16(bd BitDepth) Unsigned
- func (p *PoolAllocator) Uint32(bd BitDepth) Unsigned
- func (p *PoolAllocator) Uint64(bd BitDepth) Unsigned
- func (p *PoolAllocator) Uint8(bd BitDepth) Unsigned
- type Signal
- type Signed
- type Unsigned
Examples ¶
- Package (Iterate)
- Package (Pool)
- Package (ReadWrite)
- BitDepth.MaxSignedValue
- BitDepth.MaxUnsignedValue
- BitDepth.MinSignedValue
- BitDepth.SignedValue
- BitDepth.UnsignedValue
- FloatingAsFloating
- FloatingAsSigned
- FloatingAsUnsigned
- Frequency.Duration
- Frequency.Events
- SignedAsFloating
- SignedAsSigned
- SignedAsUnsigned
- UnsignedAsFloating
- UnsignedAsSigned
- UnsignedAsUnsigned
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AsFloating ¶ added in v0.8.0
AsFloating allows to convert arbitrary signal type to floating.
func AsUnsigned ¶ added in v0.8.0
AsUnsigned allows to convert arbitrary signal type to unsigned.
func ChannelLength ¶ added in v0.7.0
ChannelLength calculates a channel length for provided buffer length and number of channels.
func ClearPoolAllocatorCache ¶ added in v0.9.0
func ClearPoolAllocatorCache()
ClearPoolAllocatorCache resets internal cache of pools and makes existing pools available for GC. One good use case might be when the application changes global buffer size.
func FloatingAsFloating ¶ added in v0.6.0
FloatingAsFloating appends floating-point samples to the floating-point destination buffer. Both buffers must have the same number of channels, otherwise function will panic. Returns a number of samples written per channel.
Example ¶
package main import ( "fmt" "pipelined.dev/signal" ) func main() { values := 5 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } // 64-bit floating to 32-bit floating signal f64, f32 := alloc.Float64(), alloc.Float32() // write float32 values to input signal.WriteFloat64( []float64{ 1.5, 1, 0, -1, -1.5, }, f64, ) // convert float32 input to float64 output signal.FloatingAsFloating(f64, f32) result := make([]float32, values) // read result signal.ReadFloat32(f32, result) fmt.Println(result) }
Output: [1.5 1 0 -1 -1.5]
func FloatingAsSigned ¶ added in v0.6.0
FloatingAsSigned converts floating-point samples into signed fixed-point and appends them to the destination buffer. The floating sample range [-1,1] is mapped to signed [-2^(bitDepth-1), 2^(bitDepth-1)-1]. Floating values beyond the range will be clipped. Buffers must have the same number of channels, otherwise function will panic. Returns a number of samples written per channel.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { values := 7 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } // 64-bit floating to 64-bit signed signal f64, i64 := alloc.Float64(), alloc.Int64(signal.BitDepth64) // write float64 values to input signal.WriteFloat64( []float64{ math.Nextafter(1, 2), 1, math.Nextafter(1, 0), 0, math.Nextafter(-1, 0), -1, math.Nextafter(-1, -2), }, f64, ) // convert floating input to signed output signal.FloatingAsSigned(f64, i64) result := make([]int64, values) // read result signal.ReadInt64(i64, result) fmt.Println(result) }
Output: [9223372036854775807 9223372036854775807 9223372036854774784 0 -9223372036854774784 -9223372036854775808 -9223372036854775808]
func FloatingAsUnsigned ¶ added in v0.6.0
FloatingAsUnsigned converts floating-point samples into unsigned fixed-point and appends them to the destination buffer. The floating sample range [-1,1] is mapped to unsigned [0, 2^bitDepth-1]. Floating values beyond the range will be clipped. Buffers must have the same number of channels, otherwise function will panic.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { values := 7 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } // 64-bit floating to 64-bit unsigned signal f64, u64 := alloc.Float64(), alloc.Uint64(signal.BitDepth64) // write float64 values to input signal.WriteFloat64( []float64{ math.Nextafter(1, 2), 1, math.Nextafter(1, 0), 0, math.Nextafter(-1, 0), -1, math.Nextafter(-1, -2), }, f64, ) // convert floating input to unsigned output signal.FloatingAsUnsigned(f64, u64) result := make([]uint64, values) // read result signal.ReadUint64(u64, result) fmt.Println(result) }
Output: [18446744073709551615 18446744073709551615 18446744073709550592 9223372036854775808 1024 0 0]
func ReadFloat32 ¶ added in v0.6.0
ReadFloat32 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadFloat64 ¶ added in v0.6.0
ReadFloat64 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadInt ¶ added in v0.6.0
ReadInt reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadInt16 ¶ added in v0.6.0
ReadInt16 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadInt32 ¶ added in v0.6.0
ReadInt32 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadInt64 ¶ added in v0.6.0
ReadInt64 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadInt8 ¶ added in v0.6.0
ReadInt8 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadStripedFloat32 ¶ added in v0.6.0
ReadStripedFloat32 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedFloat64 ¶ added in v0.6.0
ReadStripedFloat64 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedInt ¶ added in v0.6.0
ReadStripedInt reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedInt16 ¶ added in v0.6.0
ReadStripedInt16 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedInt32 ¶ added in v0.6.0
ReadStripedInt32 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedInt64 ¶ added in v0.6.0
ReadStripedInt64 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedInt8 ¶ added in v0.6.0
ReadStripedInt8 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedUint ¶ added in v0.6.0
ReadStripedUint reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedUint16 ¶ added in v0.6.0
ReadStripedUint16 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedUint32 ¶ added in v0.6.0
ReadStripedUint32 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedUint64 ¶ added in v0.6.0
ReadStripedUint64 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadStripedUint8 ¶ added in v0.6.0
ReadStripedUint8 reads values from the buffer into provided slice. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, no values for that channel will be read. Returns a number of samples read for the longest channel.
func ReadUint16 ¶ added in v0.6.0
ReadUint16 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadUint32 ¶ added in v0.6.0
ReadUint32 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadUint64 ¶ added in v0.6.0
ReadUint64 reads values from the buffer into provided slice. Returns number of samples read per channel.
func ReadUint8 ¶ added in v0.6.0
ReadUint8 reads values from the buffer into provided slice. Returns number of samples read per channel.
func SignedAsFloating ¶ added in v0.6.0
SignedAsFloating converts signed fixed-point samples into floating-point and appends them to the destination buffer. The signed sample range [-2^(bitDepth-1), 2^(bitDepth-1)-1] is mapped to floating [-1,1]. Buffers must have the same number of channels, otherwise function will panic.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { values := 5 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } // 8-bit signed to 64-bit floating signal i8, f64 := alloc.Int8(signal.BitDepth8), alloc.Float64() // write int8 values to input signal.WriteInt8( []int8{ math.MaxInt8, math.MaxInt8 - 1, 0, math.MinInt8 + 1, math.MinInt8, }, i8, ) // convert signed input to signed output signal.SignedAsFloating(i8, f64) result := make([]float64, values) // read output to the result signal.ReadFloat64(f64, result) fmt.Println(result) }
Output: [1 0.9921259842519685 0 -0.9921875 -1]
func SignedAsSigned ¶ added in v0.6.0
SignedAsSigned appends signed fixed-point samples to the signed fixed-point destination buffer. The samples are quantized to the destination bit depth. Buffers must have the same number of channels, otherwise function will panic.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { values := 5 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } result := make([]int64, values) // downscale 64-bit signed to 8-bit signed { i64, i8 := alloc.Int64(signal.BitDepth64), alloc.Int8(signal.BitDepth8) // write int64 values to input signal.WriteInt64( []int64{ math.MaxInt64, math.MaxInt64 / 2, 0, math.MinInt64 / 2, math.MinInt64, }, i64, ) // convert signed input to signed output signal.SignedAsSigned(i64, i8) // read output to the result signal.ReadInt64(i8, result) fmt.Println(result) } // upscale signed 8-bit to signed 16-bit { i8, i64 := alloc.Int8(signal.BitDepth8), alloc.Int64(signal.BitDepth16) // write int8 values to input signal.WriteInt8([]int8{math.MaxInt8, math.MaxInt8 / 2, 0, math.MinInt8 / 2, math.MinInt8}, i8) // convert signed input to signed output signal.SignedAsSigned(i8, i64) // read output to the result signal.ReadInt64(i64, result) fmt.Println(result) } }
Output: [127 63 0 -64 -128] [32767 16383 0 -16384 -32768]
func SignedAsUnsigned ¶ added in v0.6.0
SignedAsUnsigned converts signed fixed-point samples into unsigned fixed-point and appends them to the destination buffer. The samples are quantized to the destination bit depth. The signed sample range [-2^(bitDepth-1), 2^(bitDepth-1)-1] is mapped to unsigned [0, 2^bitDepth-1]. Buffers must have the same number of channels, otherwise function will panic.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { values := 5 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } result := make([]uint64, values) // downscale 64-bit signed to 8-bit unsigned { i64, u64 := alloc.Int64(signal.BitDepth64), alloc.Uint64(signal.BitDepth8) // write int values to input signal.WriteInt64( []int64{ math.MaxInt64, math.MaxInt64 / 2, 0, math.MinInt64 / 2, math.MinInt64, }, i64, ) // convert signed input to unsigned output signal.SignedAsUnsigned(i64, u64) // read output to the result signal.ReadUint64(u64, result) fmt.Println(result) } // upscale 8-bit signed to 16-bit unsigned { i64, u64 := alloc.Int64(signal.BitDepth8), alloc.Uint64(signal.BitDepth16) // write int values to input signal.WriteInt64( []int64{ math.MaxInt8, math.MaxInt8 / 2, 0, math.MinInt8 / 2, math.MinInt8, }, i64, ) // convert signed input to unsigned output signal.SignedAsUnsigned(i64, u64) // read output to the result signal.ReadUint64(u64, result) fmt.Println(result) } }
Output: [255 191 128 64 0] [65535 49151 32768 16384 0]
func UnsignedAsFloating ¶ added in v0.6.0
UnsignedAsFloating converts unsigned fixed-point samples into floating-point and appends them to the destination buffer. The unsigned sample range [0, 2^bitDepth-1] is mapped to floating [-1,1]. Buffers must have the same number of channels, otherwise function will panic.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { values := 5 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } result := make([]float64, values) // unsigned 8-bit to 64-bit floating signal u64, f64 := alloc.Uint64(signal.BitDepth8), alloc.Float64() // write uint values to input signal.WriteUint64( []uint64{ math.MaxUint8, math.MaxUint8 - (math.MaxInt8+1)/2, math.MaxInt8 + 1, (math.MaxInt8 + 1) / 2, 0, }, u64, ) // convert unsigned input to floating output signal.UnsignedAsFloating(u64, f64) // read output to the result signal.ReadFloat64(f64, result) fmt.Println(result) }
Output: [1 0.49606299212598426 0 -0.5039370078740157 -1]
func UnsignedAsSigned ¶ added in v0.6.0
UnsignedAsSigned converts unsigned fixed-point samples into signed fixed-point and appends them to the destination buffer. The samples are quantized to the destination bit depth. The unsigned sample range [0, 2^bitDepth-1] is mapped to signed [-2^(bitDepth-1), 2^(bitDepth-1)-1]. Buffers must have the same number of channels, otherwise function will panic.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { values := 5 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } result := make([]int64, values) // downscale 64-bit unsigned to 8-bit signed { u64, i64 := alloc.Uint64(signal.BitDepth64), alloc.Int64(signal.BitDepth8) // write uint values to input signal.WriteUint64( []uint64{ math.MaxUint64, math.MaxUint64 - (math.MaxInt64+1)/2, math.MaxInt64 + 1, (math.MaxInt64 + 1) / 2, 0, }, u64, ) // convert unsigned input to signed output signal.UnsignedAsSigned(u64, i64) // read output to the result signal.ReadInt64(i64, result) fmt.Println(result) } // upscale unsigned 8-bit to signed 16-bit { u64, i64 := alloc.Uint64(signal.BitDepth8), alloc.Int64(signal.BitDepth16) // write uint values to input signal.WriteUint64( []uint64{ math.MaxUint8, math.MaxUint8 - (math.MaxInt8+1)/2, math.MaxInt8 + 1, (math.MaxInt8 + 1) / 2, 0, }, u64, ) // convert unsigned input to signed output signal.UnsignedAsSigned(u64, i64) // read output to the result signal.ReadInt64(i64, result) fmt.Println(result) } }
Output: [127 63 0 -64 -128] [32767 16383 0 -16384 -32768]
func UnsignedAsUnsigned ¶ added in v0.6.0
UnsignedAsUnsigned appends unsigned fixed-point samples to the unsigned fixed-point destination buffer. The samples are quantized to the destination bit depth. Buffers must have the same number of channels, otherwise function will panic.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { values := 5 alloc := signal.Allocator{ Channels: 1, Capacity: values, Length: values, } result := make([]uint64, values) // downscale 64-bit unsigned to 8-bit unsigned { u64, u8 := alloc.Uint64(signal.BitDepth64), alloc.Uint8(signal.BitDepth8) // write uint values to input signal.WriteUint64( []uint64{ math.MaxUint64, math.MaxUint64 - (math.MaxInt64+1)/2, math.MaxInt64 + 1, (math.MaxInt64 + 1) / 2, 0, }, u64, ) // convert unsigned input to unsigned output signal.UnsignedAsUnsigned(u64, u8) // read output to the result signal.ReadUint64(u8, result) fmt.Println(result) } // upscale 8-bit unsigned to 16-bit unsigned { u8, u16 := alloc.Uint8(signal.BitDepth8), alloc.Uint16(signal.BitDepth16) // write uint values to input signal.WriteUint64( []uint64{ math.MaxUint8, math.MaxUint8 - (math.MaxInt8+1)/2, math.MaxInt8 + 1, (math.MaxInt8 + 1) / 2, 0, }, u8, ) // convert unsigned input to unsigned output signal.UnsignedAsUnsigned(u8, u16) // read output to the result signal.ReadUint64(u16, result) fmt.Println(result) } }
Output: [255 191 128 64 0] [65535 49151 32768 16384 0]
func WriteFloat32 ¶ added in v0.6.0
WriteFloat32 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteFloat64 ¶ added in v0.6.0
WriteFloat64 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteInt ¶ added in v0.6.0
WriteInt writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteInt16 ¶ added in v0.6.0
WriteInt16 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteInt32 ¶ added in v0.6.0
WriteInt32 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteInt64 ¶ added in v0.6.0
WriteInt64 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteInt8 ¶ added in v0.6.0
WriteInt8 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteStripedFloat32 ¶ added in v0.6.0
WriteStripedFloat32 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedFloat64 ¶ added in v0.6.0
WriteStripedFloat64 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedInt ¶ added in v0.6.0
WriteStripedInt writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedInt16 ¶ added in v0.6.0
WriteStripedInt16 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedInt32 ¶ added in v0.6.0
WriteStripedInt32 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedInt64 ¶ added in v0.6.0
WriteStripedInt64 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedInt8 ¶ added in v0.6.0
WriteStripedInt8 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedUint ¶ added in v0.6.0
WriteStripedUint writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedUint16 ¶ added in v0.6.0
WriteStripedUint16 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedUint32 ¶ added in v0.6.0
WriteStripedUint32 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedUint64 ¶ added in v0.6.0
WriteStripedUint64 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteStripedUint8 ¶ added in v0.6.0
WriteStripedUint8 writes values from provided slice into the buffer. The length of provided slice must be equal to the number of channels, otherwise function will panic. Nested slices can be nil, zero values for that channel will be written. Returns a number of samples written for the longest channel.
func WriteUint ¶ added in v0.6.0
WriteUint writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteUint16 ¶ added in v0.6.0
WriteUint16 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteUint32 ¶ added in v0.6.0
WriteUint32 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteUint64 ¶ added in v0.6.0
WriteUint64 writes values from provided slice into the buffer. Returns a number of samples written per channel.
func WriteUint8 ¶ added in v0.6.0
WriteUint8 writes values from provided slice into the buffer. Returns a number of samples written per channel.
Types ¶
type Allocator ¶ added in v0.6.0
Allocator provides allocation of various signal buffers.
func (Allocator) Float32 ¶ added in v0.6.0
Float32 allocates a new sequential float32 signal buffer.
func (Allocator) Float64 ¶ added in v0.6.0
Float64 allocates a new sequential float64 signal buffer.
type BitDepth ¶
type BitDepth uint8
BitDepth is the number of bits of information in each sample.
const ( // BitDepth4 is 4 bit depth. BitDepth4 BitDepth = 1 << (iota + 2) // BitDepth8 is 8 bit depth. BitDepth8 // BitDepth16 is 16 bit depth. BitDepth16 // BitDepth32 is 32 bit depth. BitDepth32 // BitDepth64 is 64 bit depth. BitDepth64 // BitDepth24 is 24 bit depth. BitDepth24 BitDepth = 24 // MaxBitDepth is a maximum supported bit depth. MaxBitDepth BitDepth = BitDepth64 )
func (BitDepth) MaxSignedValue ¶ added in v0.6.0
MaxSignedValue returns the maximum signed value for the bit depth.
Example ¶
package main import ( "fmt" "pipelined.dev/signal" ) func main() { fmt.Println(signal.BitDepth8.MaxSignedValue()) }
Output: 127
func (BitDepth) MaxUnsignedValue ¶ added in v0.6.0
MaxUnsignedValue returns the maximum unsigned value for the bit depth.
Example ¶
package main import ( "fmt" "pipelined.dev/signal" ) func main() { fmt.Println(signal.BitDepth8.MaxUnsignedValue()) }
Output: 255
func (BitDepth) MinSignedValue ¶ added in v0.6.0
MinSignedValue returns the minimum signed value for the bit depth.
Example ¶
package main import ( "fmt" "pipelined.dev/signal" ) func main() { fmt.Println(signal.BitDepth8.MinSignedValue()) }
Output: -128
func (BitDepth) SignedValue ¶ added in v0.6.0
SignedValue clips the signed signal value to the given bit depth range.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { fmt.Println(signal.BitDepth8.SignedValue(math.MaxInt64)) }
Output: 127
func (BitDepth) UnsignedValue ¶ added in v0.6.0
UnsignedValue clips the unsigned signal value to the given bit depth range.
Example ¶
package main import ( "fmt" "math" "pipelined.dev/signal" ) func main() { fmt.Println(signal.BitDepth8.UnsignedValue(math.MaxUint64)) }
Output: 255
type Floating ¶ added in v0.6.0
type Floating interface { Signal Slice(start int, end int) Floating Channel(channel int) Floating Append(Floating) AppendSample(value float64) Sample(index int) float64 SetSample(index int, value float64) }
Floating is a digital signal represented with floating-point values.
type Frequency ¶ added in v0.9.0
type Frequency float64
Frequency in Hertz is the number of occurrences of a repeating event per second. It might represent sample rate or pitch.
func (Frequency) Duration ¶ added in v0.9.0
Duration returns a total time duration for a number events at this frequency.
Example ¶
package main import ( "fmt" "pipelined.dev/signal" ) func main() { fmt.Println(signal.Frequency(44100).Duration(88200)) }
Output: 2s
type PoolAllocator ¶ added in v0.9.0
type PoolAllocator struct { Channels int Capacity int Length int // contains filtered or unexported fields }
PoolAllocator allows to decrease a number of allocations at runtime. Internally it relies on sync.PoolAllocator to manage objects in memory. It contains a pool per signal buffer type.
func GetPoolAllocator ¶ added in v0.9.0
func GetPoolAllocator(channels, length, capacity int) *PoolAllocator
GetPoolAllocator returns pool for provided buffer dimensions - number of channels and capacity. The length is not taken into account, it's only used to provide desired size when once new buffer is requested. Pools are cached internally, so multiple calls with same dimentions will return the same pool instance.
func (*PoolAllocator) Float32 ¶ added in v0.10.0
func (p *PoolAllocator) Float32() Floating
Float32 selects a new sequential float32 signal buffer. from the pool.
func (*PoolAllocator) Float64 ¶ added in v0.10.0
func (p *PoolAllocator) Float64() Floating
Float64 selects a new sequential float64 signal buffer. from the pool.
func (*PoolAllocator) Int16 ¶ added in v0.10.0
func (p *PoolAllocator) Int16(bd BitDepth) Signed
Int16 selects a new sequential int16 signal buffer. from the pool.
func (*PoolAllocator) Int32 ¶ added in v0.10.0
func (p *PoolAllocator) Int32(bd BitDepth) Signed
Int32 selects a new sequential int32 signal buffer. from the pool.
func (*PoolAllocator) Int64 ¶ added in v0.10.0
func (p *PoolAllocator) Int64(bd BitDepth) Signed
Int64 selects a new sequential int64 signal buffer. from the pool.
func (*PoolAllocator) Int8 ¶ added in v0.10.0
func (p *PoolAllocator) Int8(bd BitDepth) Signed
Int8 selects a new sequential int8 signal buffer. from the pool.
func (*PoolAllocator) Uint16 ¶ added in v0.10.0
func (p *PoolAllocator) Uint16(bd BitDepth) Unsigned
Uint16 selects a new sequential uint16 signal buffer. from the pool.
func (*PoolAllocator) Uint32 ¶ added in v0.10.0
func (p *PoolAllocator) Uint32(bd BitDepth) Unsigned
Uint32 selects a new sequential uint32 signal buffer. from the pool.
func (*PoolAllocator) Uint64 ¶ added in v0.10.0
func (p *PoolAllocator) Uint64(bd BitDepth) Unsigned
Uint64 selects a new sequential uint64 signal buffer. from the pool.
func (*PoolAllocator) Uint8 ¶ added in v0.10.0
func (p *PoolAllocator) Uint8(bd BitDepth) Unsigned
Uint8 selects a new sequential uint8 signal buffer. from the pool.
type Signal ¶ added in v0.6.0
type Signal interface { Capacity() int Channels() int Length() int Len() int Cap() int BufferIndex(channel int, index int) int Free(*PoolAllocator) }
Signal is a buffer that contains a digital representation of a physical signal that is a sampled and quantized. Signal types have semantics of go slices. They can be sliced and appended to each other.
type Signed ¶ added in v0.6.0
type Signed interface { Fixed Slice(start int, end int) Signed Channel(channel int) Signed Append(Signed) AppendSample(value int64) Sample(index int) int64 SetSample(index int, value int64) }
Signed is a digital signal represented with signed fixed-point values.
type Unsigned ¶ added in v0.6.0
type Unsigned interface { Fixed Slice(start int, end int) Unsigned Channel(channel int) Unsigned Append(Unsigned) AppendSample(value uint64) Sample(index int) uint64 SetSample(index int, value uint64) }
Unsigned is a digital signal represented with unsigned fixed-point values.