mirror of https://github.com/jetkvm/kvm.git
128 lines
3.3 KiB
Go
128 lines
3.3 KiB
Go
package audio
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
// MicrophoneContentionManager manages microphone access with cooldown periods
|
|
type MicrophoneContentionManager struct {
|
|
// Atomic fields MUST be first for ARM32 alignment (int64 fields need 8-byte alignment)
|
|
lastOpNano int64
|
|
cooldownNanos int64
|
|
operationID int64
|
|
|
|
lockPtr unsafe.Pointer
|
|
}
|
|
|
|
func NewMicrophoneContentionManager(cooldown time.Duration) *MicrophoneContentionManager {
|
|
return &MicrophoneContentionManager{
|
|
cooldownNanos: int64(cooldown),
|
|
}
|
|
}
|
|
|
|
type OperationResult struct {
|
|
Allowed bool
|
|
RemainingCooldown time.Duration
|
|
OperationID int64
|
|
}
|
|
|
|
func (mcm *MicrophoneContentionManager) TryOperation() OperationResult {
|
|
now := time.Now().UnixNano()
|
|
cooldown := atomic.LoadInt64(&mcm.cooldownNanos)
|
|
lastOp := atomic.LoadInt64(&mcm.lastOpNano)
|
|
elapsed := now - lastOp
|
|
|
|
if elapsed >= cooldown {
|
|
if atomic.CompareAndSwapInt64(&mcm.lastOpNano, lastOp, now) {
|
|
opID := atomic.AddInt64(&mcm.operationID, 1)
|
|
return OperationResult{
|
|
Allowed: true,
|
|
RemainingCooldown: 0,
|
|
OperationID: opID,
|
|
}
|
|
}
|
|
// Retry once if CAS failed
|
|
lastOp = atomic.LoadInt64(&mcm.lastOpNano)
|
|
elapsed = now - lastOp
|
|
if elapsed >= cooldown && atomic.CompareAndSwapInt64(&mcm.lastOpNano, lastOp, now) {
|
|
opID := atomic.AddInt64(&mcm.operationID, 1)
|
|
return OperationResult{
|
|
Allowed: true,
|
|
RemainingCooldown: 0,
|
|
OperationID: opID,
|
|
}
|
|
}
|
|
}
|
|
|
|
remaining := time.Duration(cooldown - elapsed)
|
|
if remaining < 0 {
|
|
remaining = 0
|
|
}
|
|
|
|
return OperationResult{
|
|
Allowed: false,
|
|
RemainingCooldown: remaining,
|
|
OperationID: atomic.LoadInt64(&mcm.operationID),
|
|
}
|
|
}
|
|
|
|
func (mcm *MicrophoneContentionManager) SetCooldown(cooldown time.Duration) {
|
|
atomic.StoreInt64(&mcm.cooldownNanos, int64(cooldown))
|
|
}
|
|
|
|
func (mcm *MicrophoneContentionManager) GetCooldown() time.Duration {
|
|
return time.Duration(atomic.LoadInt64(&mcm.cooldownNanos))
|
|
}
|
|
|
|
func (mcm *MicrophoneContentionManager) GetLastOperationTime() time.Time {
|
|
nanos := atomic.LoadInt64(&mcm.lastOpNano)
|
|
if nanos == 0 {
|
|
return time.Time{}
|
|
}
|
|
return time.Unix(0, nanos)
|
|
}
|
|
|
|
func (mcm *MicrophoneContentionManager) GetOperationCount() int64 {
|
|
return atomic.LoadInt64(&mcm.operationID)
|
|
}
|
|
|
|
func (mcm *MicrophoneContentionManager) Reset() {
|
|
atomic.StoreInt64(&mcm.lastOpNano, 0)
|
|
atomic.StoreInt64(&mcm.operationID, 0)
|
|
}
|
|
|
|
var (
|
|
globalMicContentionManager unsafe.Pointer
|
|
micContentionInitialized int32
|
|
)
|
|
|
|
func GetMicrophoneContentionManager() *MicrophoneContentionManager {
|
|
ptr := atomic.LoadPointer(&globalMicContentionManager)
|
|
if ptr != nil {
|
|
return (*MicrophoneContentionManager)(ptr)
|
|
}
|
|
|
|
if atomic.CompareAndSwapInt32(&micContentionInitialized, 0, 1) {
|
|
manager := NewMicrophoneContentionManager(GetConfig().MicContentionTimeout)
|
|
atomic.StorePointer(&globalMicContentionManager, unsafe.Pointer(manager))
|
|
return manager
|
|
}
|
|
|
|
ptr = atomic.LoadPointer(&globalMicContentionManager)
|
|
if ptr != nil {
|
|
return (*MicrophoneContentionManager)(ptr)
|
|
}
|
|
|
|
return NewMicrophoneContentionManager(GetConfig().MicContentionTimeout)
|
|
}
|
|
|
|
func TryMicrophoneOperation() OperationResult {
|
|
return GetMicrophoneContentionManager().TryOperation()
|
|
}
|
|
|
|
func SetMicrophoneCooldown(cooldown time.Duration) {
|
|
GetMicrophoneContentionManager().SetCooldown(cooldown)
|
|
}
|