| | |
| | | return nil |
| | | } |
| | | |
| | | // Convert 16-bit PCM to float32 |
| | | numSamples := len(pcm) / 2 |
| | | samples := make([]float32, numSamples) |
| | | for i := 0; i < numSamples; i++ { |
| | | sample := int16(pcm[i*2]) | int16(pcm[i*2+1])<<8 |
| | | samples[i] = float32(sample) / 32768.0 |
| | | } |
| | | |
| | | logger.Info("Recording stopped, %d samples captured", numSamples) |
| | | samples := pcm16LEToFloat32(pcm) |
| | | logger.Info("Recording stopped, %d samples captured", len(samples)) |
| | | return samples |
| | | } |
| | | |
| | | // ReadSamplesSince returns newly captured samples after sampleOffset and the |
| | | // next offset to pass on the following call. It is safe to call while recording. |
| | | func (r *Recorder) ReadSamplesSince(sampleOffset int) ([]float32, int) { |
| | | r.mu.Lock() |
| | | totalSamples := len(r.pcmBuf) / 2 |
| | | if sampleOffset < 0 { |
| | | sampleOffset = 0 |
| | | } |
| | | if sampleOffset > totalSamples { |
| | | sampleOffset = totalSamples |
| | | } |
| | | startByte := sampleOffset * 2 |
| | | pcm := append([]byte(nil), r.pcmBuf[startByte:]...) |
| | | r.mu.Unlock() |
| | | |
| | | if len(pcm) < 2 { |
| | | return nil, totalSamples |
| | | } |
| | | return pcm16LEToFloat32(pcm), totalSamples |
| | | } |
| | | |
| | | // HasVoiceActivity returns true if the max volume exceeded the silence threshold. |
| | |
| | | logger.Info("Audio device uninitialized") |
| | | } |
| | | |
| | | func pcm16LEToFloat32(pcm []byte) []float32 { |
| | | numSamples := len(pcm) / 2 |
| | | if numSamples == 0 { |
| | | return nil |
| | | } |
| | | samples := make([]float32, numSamples) |
| | | for i := 0; i < numSamples; i++ { |
| | | sample := int16(pcm[i*2]) | int16(pcm[i*2+1])<<8 |
| | | samples[i] = float32(sample) / 32768.0 |
| | | } |
| | | return samples |
| | | } |
| | | |
| | | func (r *Recorder) onData(input []byte) { |
| | | r.mu.Lock() |
| | | |