github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/attachments/audio.go (about) 1 package attachments 2 3 import ( 4 "bytes" 5 "image" 6 "image/color" 7 "image/png" 8 "math" 9 10 "github.com/keybase/client/go/protocol/chat1" 11 "golang.org/x/net/context" 12 ) 13 14 type audioVisualizer struct { 15 amps []float64 16 bkgColor color.Color 17 strokeColor color.Color 18 strokeWidth int 19 strokeGap int 20 height int 21 minAmp float64 22 } 23 24 func newAudioVisualizer(amps []float64) *audioVisualizer { 25 return &audioVisualizer{ 26 amps: amps, 27 bkgColor: color.White, 28 strokeColor: color.Black, 29 strokeWidth: 1, 30 strokeGap: 1, 31 height: 64, 32 minAmp: -80, 33 } 34 } 35 36 func (a *audioVisualizer) stroke(offset, height int, img *image.NRGBA) { 37 for i := offset; i < offset+a.strokeWidth; i++ { 38 for j := 0; j < height; j++ { 39 img.Set(i, a.height-j, a.strokeColor) 40 } 41 } 42 } 43 44 func (a *audioVisualizer) getHeight(amp float64) int { 45 prop := math.Min(1.0, math.Max(1.0-amp/a.minAmp, 0.1)) 46 return int(float64(a.height) * prop) 47 } 48 49 func (a *audioVisualizer) visualize() ([]byte, int) { 50 numStrokes := len(a.amps) 51 width := numStrokes * (a.strokeWidth + a.strokeGap) 52 img := image.NewNRGBA(image.Rect(0, 0, width, a.height)) 53 offset := 0 54 for i := 0; i < width; i++ { 55 for j := 0; j < a.height; j++ { 56 img.Set(i, j, a.bkgColor) 57 } 58 } 59 for i := 0; i < numStrokes; i++ { 60 height := a.getHeight(a.amps[i]) 61 a.stroke(offset, height, img) 62 offset += a.strokeWidth + a.strokeGap 63 } 64 var buf bytes.Buffer 65 _ = png.Encode(&buf, img) 66 return buf.Bytes(), width 67 } 68 69 func (s *Sender) MakeAudioPreview(ctx context.Context, amps []float64, duration int) (res chat1.MakePreviewRes, err error) { 70 defer s.Trace(ctx, &err, "MakeAudioPreview")() 71 v := newAudioVisualizer(amps) 72 previewDat, previewWidth := v.visualize() 73 res.MimeType = "video/mp4" 74 res.PreviewMimeType = new(string) 75 *res.PreviewMimeType = "image/png" 76 location := chat1.NewPreviewLocationWithBytes(previewDat) 77 res.Location = &location 78 baseMd := chat1.NewAssetMetadataWithVideo(chat1.AssetMetadataVideo{ 79 Width: previewWidth, 80 Height: v.height, 81 DurationMs: duration, 82 IsAudio: true, 83 }) 84 res.BaseMetadata = &baseMd 85 previewMd := chat1.NewAssetMetadataWithImage(chat1.AssetMetadataImage{ 86 Width: previewWidth, 87 Height: v.height, 88 AudioAmps: amps, 89 }) 90 res.Metadata = &previewMd 91 return res, nil 92 }