Skip to content
This repository was archived by the owner on Mar 18, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 6 additions & 22 deletions mixing/mixbuffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package mixing

import (
"bytes"
"encoding/binary"
"time"

"github.com/gotracker/gomixing/sampling"
Expand Down Expand Up @@ -63,15 +62,14 @@ func (m *MixBuffer) Add(pos int, rhs *MixBuffer, volMtx volume.Matrix) {

// ToRenderData converts a mixbuffer into a byte stream intended to be
// output to the output sound device
func (m *MixBuffer) ToRenderData(samples int, bitsPerSample int, channels int, mixerVolume volume.Volume) []byte {
func (m *MixBuffer) ToRenderData(samples int, channels int, mixerVolume volume.Volume, formatter sampling.Formatter) []byte {
writer := &bytes.Buffer{}
writer.Grow(samples * ((bitsPerSample + 7) / 8) * channels)
writer.Grow(samples * ((formatter.Size() + 7) / 8) * channels)
for _, samp := range *m {
buf := samp.Apply(mixerVolume)
d := buf.ToChannels(channels)
for i := 0; i < channels; i++ {
val := d.StaticMatrix[i].ToSample(bitsPerSample)
_ = binary.Write(writer, binary.LittleEndian, val) // lint
_ = formatter.Write(writer, d.StaticMatrix[i]) // lint
}
}
return writer.Bytes()
Expand All @@ -96,7 +94,7 @@ func (m *MixBuffer) ToIntStream(outputChannels int, samples int, bitsPerSample i

// ToRenderDataWithBufs converts a mixbuffer into a byte stream intended to be
// output to the output sound device
func (m *MixBuffer) ToRenderDataWithBufs(outBuffers [][]byte, samples int, bitsPerSample int, mixerVolume volume.Volume) {
func (m *MixBuffer) ToRenderDataWithBufs(outBuffers [][]byte, samples int, mixerVolume volume.Volume, formatter sampling.Formatter) {
pos := 0
onum := 0
out := outBuffers[onum]
Expand All @@ -111,22 +109,8 @@ func (m *MixBuffer) ToRenderDataWithBufs(outBuffers [][]byte, samples int, bitsP
out = outBuffers[onum]
pos = 0
}
val := buf.StaticMatrix[c].ToSample(bitsPerSample)
switch d := val.(type) {
case int8:
out[pos] = uint8(d)
pos++
case int16:
binary.LittleEndian.PutUint16(out[pos:], uint16(d))
pos += 2
case int32:
binary.LittleEndian.PutUint32(out[pos:], uint32(d))
pos += 4
default:
writer := &bytes.Buffer{}
_ = binary.Write(writer, binary.LittleEndian, val) // lint
pos += copy(out[pos:], writer.Bytes())
}
_ = formatter.WriteAt(out, int64(pos), buf.StaticMatrix[c]) // lint
pos += formatter.Size()
}
}
}
19 changes: 12 additions & 7 deletions mixing/mixer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package mixing

import "github.com/gotracker/gomixing/volume"
import (
"github.com/gotracker/gomixing/sampling"
"github.com/gotracker/gomixing/volume"
)

// Mixer is a manager for mixing multiple single- and multi-channel samples into a single multi-channel output stream
type Mixer struct {
Expand All @@ -10,7 +13,7 @@ type Mixer struct {

// NewMixBuffer returns a mixer buffer with a number of channels
// of preallocated sample data
func (m *Mixer) NewMixBuffer(samples int) MixBuffer {
func (m Mixer) NewMixBuffer(samples int) MixBuffer {
return make(MixBuffer, samples)
}

Expand All @@ -21,8 +24,9 @@ func GetDefaultMixerVolume(numMixedChannels int) volume.Volume {
}

// Flatten will to a final saturation mix of all the row's channel data into a single output buffer
func (m *Mixer) Flatten(panmixer PanMixer, samplesLen int, row []ChannelData, mixerVolume volume.Volume) []byte {
func (m Mixer) Flatten(panmixer PanMixer, samplesLen int, row []ChannelData, mixerVolume volume.Volume, sampleFormat sampling.Format) []byte {
data := m.NewMixBuffer(samplesLen)
formatter := sampling.GetFormatter(sampleFormat)
for _, rdata := range row {
for _, cdata := range rdata {
if cdata.Flush != nil {
Expand All @@ -34,12 +38,12 @@ func (m *Mixer) Flatten(panmixer PanMixer, samplesLen int, row []ChannelData, mi
}
}
}
return data.ToRenderData(samplesLen, m.BitsPerSample, m.Channels, mixerVolume)
return data.ToRenderData(samplesLen, m.Channels, mixerVolume, formatter)
}

// FlattenToInts runs a flatten on the channel data into separate channel data of int32 variety
// these int32s still respect the BitsPerSample size
func (m *Mixer) FlattenToInts(panmixer PanMixer, samplesLen int, row []ChannelData, mixerVolume volume.Volume) [][]int32 {
func (m Mixer) FlattenToInts(panmixer PanMixer, samplesLen int, row []ChannelData, mixerVolume volume.Volume) [][]int32 {
data := m.NewMixBuffer(samplesLen)
for _, rdata := range row {
for _, cdata := range rdata {
Expand All @@ -56,8 +60,9 @@ func (m *Mixer) FlattenToInts(panmixer PanMixer, samplesLen int, row []ChannelDa
}

// FlattenTo will to a final saturation mix of all the row's channel data into a single output buffer
func (m *Mixer) FlattenTo(resultBuffers [][]byte, panmixer PanMixer, samplesLen int, row []ChannelData, mixerVolume volume.Volume) {
func (m Mixer) FlattenTo(resultBuffers [][]byte, panmixer PanMixer, samplesLen int, row []ChannelData, mixerVolume volume.Volume, sampleFormat sampling.Format) {
data := m.NewMixBuffer(samplesLen)
formatter := sampling.GetFormatter(sampleFormat)
for _, rdata := range row {
for _, cdata := range rdata {
if cdata.Flush != nil {
Expand All @@ -69,5 +74,5 @@ func (m *Mixer) FlattenTo(resultBuffers [][]byte, panmixer PanMixer, samplesLen
}
}
}
data.ToRenderDataWithBufs(resultBuffers, samplesLen, m.BitsPerSample, mixerVolume)
data.ToRenderDataWithBufs(resultBuffers, samplesLen, mixerVolume, formatter)
}
27 changes: 27 additions & 0 deletions sampling/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package sampling

// Format is the format of the sample data
type Format uint8

const (
// Format8BitUnsigned is for unsigned 8-bit data
Format8BitUnsigned = Format(iota)
// Format8BitSigned is for signed 8-bit data
Format8BitSigned
// Format16BitLEUnsigned is for unsigned, little-endian, 16-bit data
Format16BitLEUnsigned
// Format16BitLESigned is for signed, little-endian, 16-bit data
Format16BitLESigned
// Format16BitBEUnsigned is for unsigned, big-endian, 16-bit data
Format16BitBEUnsigned
// Format16BitBESigned is for signed, big-endian, 16-bit data
Format16BitBESigned
// Format32BitLEFloat is for little-endian, 32-bit floating-point data
Format32BitLEFloat
// Format32BitBEFloat is for big-endian, 32-bit floating-point data
Format32BitBEFloat
// Format64BitLEFloat is for little-endian, 64-bit floating-point data
Format64BitLEFloat
// Format64BitBEFloat is for big-endian, 64-bit floating-point data
Format64BitBEFloat
)
116 changes: 116 additions & 0 deletions sampling/format_bit16.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package sampling

import (
"encoding/binary"
"io"

"github.com/gotracker/gomixing/volume"
)

const (
cSample16BitDataCoeff = 0x8000
cSample16BitVolumeCoeff = volume.Volume(1) / cSample16BitDataCoeff
cSample16BitBytes = 2
)

// Sample16BitSigned is a signed 16-bit sample
type Sample16BitSigned struct {
byteOrder binary.ByteOrder
}

// Volume returns the volume value for the sample
func (Sample16BitSigned) volume(v int16) volume.Volume {
return volume.Volume(v) * cSample16BitVolumeCoeff
}

// fromVolume returns the volume value for the sample
func (Sample16BitSigned) fromVolume(v volume.Volume) int16 {
return int16(v.ToIntSample(16))
}

// Size returns the size of the sample in bytes
func (Sample16BitSigned) Size() int {
return cSample16BitBytes
}

// ReadAt reads a value from the reader provided in the byte order provided
func (s Sample16BitSigned) ReadAt(data []byte, ofs int64) (volume.Volume, error) {
if len(data) <= int(ofs)+(cSample16BitBytes-1) {
return 0, io.EOF
}
if ofs < 0 {
ofs = 0
}

v := int16(s.byteOrder.Uint16(data[ofs:]))
return s.volume(v), nil
}

// WriteAt writes a value to the slice provided in the byte order provided
func (s Sample16BitSigned) WriteAt(data []byte, ofs int64, v volume.Volume) error {
if len(data) <= int(ofs) {
return io.EOF
}
if ofs < 0 {
ofs = 0
}

s.byteOrder.PutUint16(data[ofs:], uint16(s.fromVolume(v)))
return nil
}

// Write writes a value to the Writer provided in the byte order provided
func (s Sample16BitSigned) Write(out io.Writer, v volume.Volume) error {
return binary.Write(out, s.byteOrder, s.fromVolume(v))
}

// Sample16BitUnsigned is an unsigned 16-bit sample
type Sample16BitUnsigned struct {
byteOrder binary.ByteOrder
}

// Volume returns the volume value for the sample
func (Sample16BitUnsigned) volume(v uint16) volume.Volume {
return volume.Volume(int16(v-cSample16BitDataCoeff)) * cSample16BitVolumeCoeff
}

// fromVolume returns the volume value for the sample
func (Sample16BitUnsigned) fromVolume(v volume.Volume) uint16 {
return uint16(v.ToUintSample(16))
}

// Size returns the size of the sample in bytes
func (Sample16BitUnsigned) Size() int {
return cSample16BitBytes
}

// ReadAt reads a value from the reader provided in the byte order provided
func (s Sample16BitUnsigned) ReadAt(data []byte, ofs int64) (volume.Volume, error) {
if len(data) <= int(ofs)+(cSample16BitBytes-1) {
return 0, io.EOF
}
if ofs < 0 {
ofs = 0
}

v := uint16(s.byteOrder.Uint16(data[ofs:]))
return s.volume(v), nil
}

// WriteAt writes a value to the slice provided in the byte order provided
func (s Sample16BitUnsigned) WriteAt(data []byte, ofs int64, v volume.Volume) error {
if len(data) <= int(ofs) {
return io.EOF
}
if ofs < 0 {
ofs = 0
}

s.byteOrder.PutUint16(data[ofs:], s.fromVolume(v))
return nil
}

// Write writes a value to the Writer provided in the byte order provided
func (s Sample16BitUnsigned) Write(out io.Writer, v volume.Volume) error {
return binary.Write(out, s.byteOrder, s.fromVolume(v))
}
55 changes: 55 additions & 0 deletions sampling/format_bit32float.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package sampling

import (
"encoding/binary"
"io"
"math"

"github.com/gotracker/gomixing/volume"
)

const (
//cSample32BitFloatVolumeCoeff = volume.Volume(1)
cSample32BitFloatBytes = 4
)

// Sample32BitFloat is a 32-bit floating-point sample
type Sample32BitFloat struct {
byteOrder binary.ByteOrder
}

// Size returns the size of the sample in bytes
func (Sample32BitFloat) Size() int {
return cSample32BitFloatBytes
}

// ReadAt reads a value from the reader provided in the byte order provided
func (s Sample32BitFloat) ReadAt(data []byte, ofs int64) (volume.Volume, error) {
if len(data) <= int(ofs)+(cSample32BitFloatBytes-1) {
return 0, io.EOF
}
if ofs < 0 {
ofs = 0
}

v := math.Float32frombits(s.byteOrder.Uint32(data[ofs:]))
return volume.Volume(v), nil
}

// WriteAt writes a value to the slice provided in the byte order provided
func (s Sample32BitFloat) WriteAt(data []byte, ofs int64, v volume.Volume) error {
if len(data) <= int(ofs) {
return io.EOF
}
if ofs < 0 {
ofs = 0
}

s.byteOrder.PutUint32(data[ofs:], math.Float32bits(float32(v.WithOverflowProtection())))
return nil
}

// Write writes a value to the Writer provided in the byte order provided
func (s Sample32BitFloat) Write(out io.Writer, v volume.Volume) error {
return binary.Write(out, s.byteOrder, math.Float32bits(float32(v.WithOverflowProtection())))
}
55 changes: 55 additions & 0 deletions sampling/format_bit64float.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package sampling

import (
"encoding/binary"
"io"
"math"

"github.com/gotracker/gomixing/volume"
)

const (
//cSample64BitFloatVolumeCoeff = volume.Volume(1)
cSample64BitFloatBytes = 8
)

// Sample64BitFloat is a 64-bit floating-point sample
type Sample64BitFloat struct {
byteOrder binary.ByteOrder
}

// Size returns the size of the sample in bytes
func (Sample64BitFloat) Size() int {
return cSample64BitFloatBytes
}

// ReadAt reads a value from the reader provided in the byte order provided
func (s Sample64BitFloat) ReadAt(data []byte, ofs int64) (volume.Volume, error) {
if len(data) <= int(ofs)+(cSample64BitFloatBytes-1) {
return 0, io.EOF
}
if ofs < 0 {
ofs = 0
}

f := math.Float64frombits(s.byteOrder.Uint64(data[ofs:]))
return volume.Volume(f), nil
}

// WriteAt writes a value to the slice provided in the byte order provided
func (s Sample64BitFloat) WriteAt(data []byte, ofs int64, v volume.Volume) error {
if len(data) <= int(ofs) {
return io.EOF
}
if ofs < 0 {
ofs = 0
}

s.byteOrder.PutUint64(data[ofs:], math.Float64bits(v.WithOverflowProtection()))
return nil
}

// Write writes a value to the Writer provided in the byte order provided
func (s Sample64BitFloat) Write(out io.Writer, v volume.Volume) error {
return binary.Write(out, s.byteOrder, math.Float64bits(v.WithOverflowProtection()))
}
Loading