func NewPlayer(sampleRate, channelNum, bytesPerSample int) (*Player, error) { p := &Player{ sampleRate: sampleRate, channelNum: channelNum, bytesPerSample: bytesPerSample, buffer: []byte{}, chErr: make(chan error), chBuffer: make(chan []byte, 8), } if err := jni.RunOnJVM(func(vm, env, ctx uintptr) error { audioTrack := C.jobject(nil) bufferSize := C.int(0) if msg := C.initAudioTrack(C.uintptr_t(vm), C.uintptr_t(env), C.int(sampleRate), C.int(channelNum), C.int(bytesPerSample), &audioTrack, &bufferSize); msg != nil { return errors.New("driver: " + C.GoString(msg)) } p.audioTrack = audioTrack p.bufferSize = int(bufferSize) return nil }); err != nil { return nil, err } go p.loop() return p, nil }
func (p *Player) loop() { for bufInBytes := range p.chBuffer { var bufInShorts []int16 if p.bytesPerSample == 2 { bufInShorts = make([]int16, len(bufInBytes)/2) for i := 0; i < len(bufInShorts); i++ { bufInShorts[i] = int16(bufInBytes[2*i]) | (int16(bufInBytes[2*i+1]) << 8) } } if err := jni.RunOnJVM(func(vm, env, ctx uintptr) error { msg := (*C.char)(nil) switch p.bytesPerSample { case 1: msg = C.writeToAudioTrack(C.uintptr_t(vm), C.uintptr_t(env), p.audioTrack, C.int(p.bytesPerSample), unsafe.Pointer(&bufInBytes[0]), C.int(len(bufInBytes))) case 2: msg = C.writeToAudioTrack(C.uintptr_t(vm), C.uintptr_t(env), p.audioTrack, C.int(p.bytesPerSample), unsafe.Pointer(&bufInShorts[0]), C.int(len(bufInShorts))) } if msg != nil { return errors.New(C.GoString(msg)) } return nil }); err != nil { p.chErr <- err return } } }
func deviceScale() float64 { if 0 < androidDeviceScale { return androidDeviceScale } if err := jni.RunOnJVM(func(vm, env, ctx uintptr) error { androidDeviceScale = float64(C.deviceScale(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx))) return nil }); err != nil { panic(fmt.Sprintf("ui: error %v", err)) } return androidDeviceScale }