159 lines
4.1 KiB
Markdown
159 lines
4.1 KiB
Markdown
# Custom Audio Source Example
|
|
|
|
This example demonstrates how to implement the `AudioSource` interface to create custom audio generators for streaming.
|
|
|
|
## What it does
|
|
|
|
This example shows three different custom audio source implementations:
|
|
|
|
1. **MultiToneSource** - Mixes multiple sine waves (creates chords)
|
|
2. **SweepSource** - Generates frequency sweeps (chirps)
|
|
3. **Built-in TestTone** - Single frequency tone (for comparison)
|
|
|
|
Each demonstrates different aspects of the `AudioSource` interface.
|
|
|
|
## Building
|
|
|
|
```bash
|
|
cd examples/custom-source
|
|
go build
|
|
```
|
|
|
|
## Running
|
|
|
|
Generate an A major chord (440, 554, 659 Hz):
|
|
|
|
```bash
|
|
./custom-source -mode chord
|
|
```
|
|
|
|
Generate a frequency sweep from 220 to 880 Hz:
|
|
|
|
```bash
|
|
./custom-source -mode sweep
|
|
```
|
|
|
|
Generate a single 440 Hz tone:
|
|
|
|
```bash
|
|
./custom-source -mode single
|
|
```
|
|
|
|
## Command-line options
|
|
|
|
- `-port` - Server port (default: 8927)
|
|
- `-mode` - Source mode: chord, sweep, single (default: chord)
|
|
- `-rate` - Sample rate in Hz (default: 192000)
|
|
- `-channels` - Number of channels (default: 2)
|
|
|
|
## AudioSource Interface
|
|
|
|
To create a custom audio source, implement this interface:
|
|
|
|
```go
|
|
type AudioSource interface {
|
|
// Read fills the buffer with PCM samples (int32 for 24-bit audio)
|
|
Read(samples []int32) (int, error)
|
|
|
|
// SampleRate returns the sample rate
|
|
SampleRate() int
|
|
|
|
// Channels returns the number of channels
|
|
Channels() int
|
|
|
|
// Metadata returns title, artist, album
|
|
Metadata() (title, artist, album string)
|
|
|
|
// Close closes the audio source
|
|
Close() error
|
|
}
|
|
```
|
|
|
|
## Implementation details
|
|
|
|
### MultiToneSource
|
|
|
|
Generates multiple sine waves and mixes them together:
|
|
|
|
```go
|
|
type MultiToneSource struct {
|
|
frequencies []float64
|
|
sampleRate int
|
|
channels int
|
|
sampleIndex uint64
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func (s *MultiToneSource) Read(samples []int32) (int, error) {
|
|
// For each sample frame:
|
|
for i := 0; i < numFrames; i++ {
|
|
t := float64(s.sampleIndex + i) / float64(s.sampleRate)
|
|
|
|
// Mix all frequencies
|
|
var mixed float64
|
|
for _, freq := range s.frequencies {
|
|
mixed += math.Sin(2 * math.Pi * freq * t)
|
|
}
|
|
mixed /= float64(len(s.frequencies))
|
|
|
|
// Convert to 24-bit PCM (int32)
|
|
pcmValue := int32(mixed * 8388607 * 0.5)
|
|
|
|
// Write to all channels
|
|
for ch := 0; ch < s.channels; ch++ {
|
|
samples[i*s.channels + ch] = pcmValue
|
|
}
|
|
}
|
|
return len(samples), nil
|
|
}
|
|
```
|
|
|
|
### SweepSource
|
|
|
|
Generates a time-varying frequency sweep:
|
|
|
|
```go
|
|
func (s *SweepSource) Read(samples []int32) (int, error) {
|
|
for i := 0; i < numFrames; i++ {
|
|
t := float64(s.sampleIndex + i) / float64(s.sampleRate)
|
|
|
|
// Calculate current frequency based on progress
|
|
progress := math.Mod(t, s.duration) / s.duration
|
|
freq := s.startFreq + (s.endFreq - s.startFreq) * progress
|
|
|
|
// Generate sine wave at current frequency
|
|
sample := math.Sin(2 * math.Pi * freq * t)
|
|
pcmValue := int32(sample * 8388607 * 0.5)
|
|
|
|
// Write to all channels
|
|
for ch := 0; ch < s.channels; ch++ {
|
|
samples[i*s.channels + ch] = pcmValue
|
|
}
|
|
}
|
|
return len(samples), nil
|
|
}
|
|
```
|
|
|
|
## Key concepts
|
|
|
|
1. **Sample format**: Use `int32` for 24-bit audio (range: -8388608 to 8388607)
|
|
2. **Interleaving**: For stereo (2 channels), samples are stored as [L, R, L, R, ...]
|
|
3. **Thread safety**: Use mutex if internal state is accessed from multiple goroutines
|
|
4. **Continuous playback**: Track `sampleIndex` to maintain phase continuity
|
|
|
|
## Use cases for custom sources
|
|
|
|
- **File streaming**: Read from MP3, FLAC, WAV files
|
|
- **Live input**: Capture from microphone or line-in
|
|
- **Synthesizers**: Generate music programmatically
|
|
- **Network streams**: Proxy audio from internet radio
|
|
- **Audio processing**: Apply effects, mixing, filtering
|
|
- **Testing**: Generate test signals for debugging
|
|
|
|
## Next steps
|
|
|
|
- Implement a file-based source using `pkg/audio/decode`
|
|
- Add audio effects (reverb, echo, filters)
|
|
- Create a source that mixes multiple input sources
|
|
- Implement a source that reads from an audio device
|