Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
J
jensen
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Skye Yu
jensen
Commits
c2355191
Commit
c2355191
authored
Aug 05, 2025
by
martin hou
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 录音试听测试
parent
2589667c
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
325 additions
and
122 deletions
+325
-122
jensen.d.ts
jensen.d.ts
+17
-6
audio-worker.js
src/audio-worker.js
+12
-7
index.tsx
src/index.tsx
+89
-17
jensen.js
src/utils/jensen.js
+130
-92
waveform-worker.js
src/waveform-worker.js
+77
-0
No files found.
jensen.d.ts
View file @
c2355191
...
...
@@ -5,6 +5,13 @@ export type DeviceInfo = {
versionCode
:
string
;
// 固件版本号的字符串表示,如1.2.3
};
// 电池电量信息
export
type
BatteryStatus
=
{
status
:
'idle'
|
'charging'
|
'full'
;
// 电池状态,idle:闲置,charging:充电中,full:满电
battery
:
number
;
// 电量百分比,整型,0~100
voltage
:
number
;
// 电池电压,单位:微伏
}
// 常规指令的应答结构,表示指令的结果是成功或是失败
export
type
ReturnStruct
=
{
common
:
{
result
:
'failed'
|
'success'
};
...
...
@@ -168,21 +175,27 @@ declare class Jensen {
// 注意:文件传输进度和数据并不是同时到达的,在文件传输到100%时才会开始调用on回调进行转移数据
getFile
:
(
fileName
:
string
,
length
:
number
,
on
?:
(
msg
:
Uint8Array
|
'fail'
)
=>
void
,
onprogress
?:
(
size
:
number
)
=>
void
)
=>
void
;
// 持续传输文件,filename为文件名称,length为文件长度,ondata为数据回调,seconds为超时时长
transferFile
:
(
filename
:
string
,
length
:
number
,
ondata
:
(
msg
:
Uint8Array
|
'fail'
)
=>
void
,
seconds
?:
number
)
=>
Promise
<
ReturnStruct
[
'common'
]
>
;
// 获取指定长度的录音文件内容,length参数可以小于或等于文件的原始长度,其它参数与getFile()方法一致
getFilePart
:
(
fileName
:
string
,
length
:
number
,
on
?:
(
msg
:
Uint8Array
|
'fail'
)
=>
void
,
onprogress
?:
(
size
:
number
)
=>
void
)
=>
void
;
// 暂不使用
getFileBlock
:
(
fileName
:
string
,
length
:
number
,
on
?:
(
msg
:
Uint8Array
|
'fail'
)
=>
void
)
=>
Promise
<
ReturnStruct
[
'common'
]
>
;
// 录音文件随机读取,从指定位置起读取指定数量个字节
readFile
:
(
fname
:
string
,
offset
:
number
,
length
:
number
)
=>
Promise
<
Uint8Array
>
;
// 传输文件
transferFile
:
(
filename
:
string
,
length
:
number
,
ondata
:
(
msg
:
Uint8Array
|
'fail'
)
=>
void
,
seconds
?:
number
)
=>
Promise
<
ReturnStruct
[
'common'
]
>
;
// 请求固件更新
// #versionNumber 新固件的版本号
// #length 新固件的内容长度
// #time 超时时长
requestFirmwareUpgrade
:
(
versionNumber
:
number
,
length
:
number
,
time
?:
number
)
=>
Promise
<
{
result
:
'accepted'
|
'fail'
}
>
;
// 获取电池电量信息
getBatteryStatus
:
(
time
?:
number
)
=>
Promise
<
BatteryStatus
>
;
// 发送新固件内容到设备端
// #data 为固件数据内容
// #seconds 为超时时长
...
...
@@ -277,9 +290,6 @@ declare class Jensen {
// 获取实时音频流数据
getRealtime
:
(
frames
:
number
)
=>
Promise
<
{
data
:
Uint8Array
;
rest
:
number
}
>
;
// 录音文件随机读取,从指定位置起读取指定数量个字节
readFile
:
(
fname
:
string
,
offset
:
number
,
length
:
number
)
=>
Promise
<
Uint8Array
>
;
// 请求设备提示音更新
requestToneUpdate
:
(
signature
:
string
,
size
:
number
,
seconds
?:
number
)
=>
Promise
<
ToneUpdateResponse
>
;
...
...
@@ -291,6 +301,7 @@ declare class Jensen {
// 更新设备UAC固件
updateUAC
:
(
data
:
Uint8Array
,
seconds
?:
number
)
=>
Promise
<
ReturnStruct
[
'common'
]
>
;
}
export
=
Jensen
;
src/audio-worker.js
View file @
c2355191
...
...
@@ -31,36 +31,41 @@ async function initFFmpeg() {
}
// 解码mp3数据为pcm数据
async
function
decode_mp3
(
data
)
{
async
function
decode_mp3
(
req
)
{
try
{
const
ffmpeg
=
await
initFFmpeg
();
// 检查FFmpeg是否已加载
if
(
!
ffmpeg
||
!
ffmpeg
.
loaded
)
{
throw
new
Error
(
'FFmpeg not properly loaded'
);
// 如果没有加载完成,就一直等待
while
(
!
ffmpeg
||
!
ffmpeg
.
loaded
)
{
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
100
));
}
// throw new Error('FFmpeg not properly loaded');
}
console
.
log
(
'FFmpeg is ready, starting MP3 decode...'
);
// 将MP3数据写入FFmpeg
await
ffmpeg
.
writeFile
(
'input.mp3'
,
data
);
let
fname
=
req
.
name
;
await
ffmpeg
.
writeFile
(
fname
+
'.mp3'
,
req
.
data
);
// 使用FFmpeg解码MP3为PCM s16le格式
await
ffmpeg
.
exec
([
'-i'
,
'input
.mp3'
,
'-i'
,
fname
+
'
.mp3'
,
'-f'
,
's16le'
,
'-acodec'
,
'pcm_s16le'
,
'-ar'
,
'16000'
,
'-ac'
,
'1'
,
'
output.pcm'
fname
+
'-
output.pcm'
]);
// 读取解码后的PCM数据
const
pcmData
=
await
ffmpeg
.
readFile
(
'output.pcm'
);
// 清理临时文件
await
ffmpeg
.
deleteFile
(
'input
.mp3'
);
await
ffmpeg
.
deleteFile
(
'
output.pcm'
);
await
ffmpeg
.
deleteFile
(
fname
+
'
.mp3'
);
await
ffmpeg
.
deleteFile
(
fname
+
'-
output.pcm'
);
// 将Uint8Array转换为Uint16Array (s16le格式)
const
uint8Array
=
new
Uint8Array
(
pcmData
);
...
...
src/index.tsx
View file @
c2355191
...
...
@@ -98,7 +98,9 @@ export function Home() {
}
const
getTime
=
async
()
=>
{
let
time
=
await
jensen
.
getTime
();
console
.
log
(
jensen
);
jensen
.
dump
();
let
time
=
await
jensen
.
getTime
(
5
);
alert
(
JSON
.
stringify
(
time
));
}
...
...
@@ -223,11 +225,17 @@ export function Home() {
// 先创建audio-worker,给到后面使用
const
audioWorker
=
new
Worker
(
new
URL
(
'./audio-worker.js'
,
import
.
meta
.
url
),
{
type
:
'module'
});
const
waveformWorker
=
new
Worker
(
new
URL
(
'./waveform-worker.js'
,
import
.
meta
.
url
),
{
type
:
'module'
});
waveformWorker
.
onmessage
=
(
e
)
=>
{
console
.
log
(
'waveformWorker'
,
e
);
}
audioWorker
.
onmessage
=
(
e
)
=>
{
// console.log(e);
if
(
e
.
data
.
waveformData
)
{
console
.
log
(
e
.
data
.
waveformData
);
console
.
log
(
'waveform'
,
e
.
data
.
waveformData
);
// 把e.data.waveformData追加到waveformData的末尾
// let waveform = [];
/*
...
...
@@ -292,6 +300,7 @@ export function Home() {
const
prepareAudio
=
()
=>
{
const
mediaSource
=
new
MediaSource
();
const
audioElement
=
document
.
createElement
(
'audio'
);
audioElement
.
autoplay
=
true
;
audioElement
.
src
=
URL
.
createObjectURL
(
mediaSource
);
document
.
body
.
appendChild
(
audioElement
);
...
...
@@ -299,34 +308,34 @@ export function Home() {
// Create source buffer for MP3
let
sourceBuffer
=
mediaSource
.
addSourceBuffer
(
'audio/mpeg'
);
setSourceBuffer
(
sourceBuffer
);
// sourceBuffer.mode = 'sequence';
alert
(
'sourceopen'
);
// Feed MP3 data to source buffer
sourceBuffer
.
addEventListener
(
'updateend'
,
()
=>
{
if
(
!
sourceBuffer
.
updating
&&
mediaSource
.
readyState
===
'open'
)
{
mediaSource
.
endOfStream
();
audioElement
.
play
();
}
if
(
mp3
.
length
>
0
)
{
sourceBuffer
.
appendBuffer
(
mp3
.
shift
());
}
// dequeue();
});
});
}
const
enqueue
=
(
clip
:
Uint8Array
)
=>
{
if
(
!
sourceBuffer
)
return
;
if
(
!
sourceBuffer
.
updating
&&
mp3
.
length
===
0
)
{
const
dequeue
=
()
=>
{
if
(
sourceBuffer
?.
updating
==
false
&&
mp3
.
length
>
0
&&
sourceBuffer
.
buffered
.
length
<
100
)
{
let
clip
=
mp3
.
shift
();
sourceBuffer
.
appendBuffer
(
clip
);
}
else
{
mp3
.
push
(
clip
);
console
.
log
(
'appendBuffer'
,
clip
.
length
);
}
else
{
setTimeout
(()
=>
dequeue
(),
100
);
}
}
const
mp3data
:
any
[]
=
[];
const
test
=
async
()
=>
{
let
idx
=
prompt
(
'请输入文件序号'
,
'
0
'
);
let
idx
=
prompt
(
'请输入文件序号'
,
'
8
'
);
if
(
idx
===
null
||
idx
===
undefined
)
return
;
let
file
=
files
[
parseInt
(
idx
)];
if
(
file
===
null
||
file
===
undefined
)
return
alert
(
'文件不存在'
);
...
...
@@ -334,10 +343,72 @@ export function Home() {
jensen
.
transferFile
(
file
.
name
,
file
.
length
,
(
data
:
Uint8Array
|
'fail'
)
=>
{
if
(
data
instanceof
Uint8Array
)
{
enqueue
(
data
);
if
(
sourceBuffer
?.
updating
==
false
)
{
sourceBuffer
.
appendBuffer
(
data
);
console
.
log
(
'directly appendBuffer'
,
data
.
length
);
}
else
{
console
.
log
(
'enqueue'
,
data
.
length
);
mp3
.
push
(
data
);
dequeue
();
}
mp3data
.
push
(
data
);
let
totalLength
=
0
;
for
(
let
i
=
0
;
i
<
mp3data
.
length
;
i
++
)
{
totalLength
+=
mp3data
[
i
].
length
;
}
if
(
totalLength
==
file
.
length
)
{
let
audioData
=
new
Uint8Array
(
totalLength
);
let
offset
=
0
;
for
(
let
i
=
0
;
i
<
mp3data
.
length
;
i
++
)
{
audioData
.
set
(
mp3data
[
i
],
offset
);
offset
+=
mp3data
[
i
].
length
;
}
decodeAudioData
(
audioData
);
}
}
});
}
const
decodeAudioData
=
(
audioData
:
Uint8Array
)
=>
{
// 将audioData分成10块,分别发送给audioWorker进行处理
let
clips
=
1
;
let
blockSize
=
Math
.
ceil
(
audioData
.
length
/
clips
);
let
audioContext
=
new
AudioContext
();
for
(
let
i
=
0
;
i
<
clips
;
i
++
)
{
let
block
=
audioData
.
buffer
.
slice
(
i
*
blockSize
,
(
i
+
1
)
*
blockSize
);
let
name
=
'block-'
+
i
;
// audioWorker.postMessage({ data: block, name: name });
audioContext
.
decodeAudioData
(
block
).
then
((
buffer
)
=>
{
// console.log('decodeAudioData' + i, buffer);
waveformWorker
.
postMessage
({
id
:
name
,
pcmdata
:
buffer
});
}).
catch
((
err
)
=>
{
console
.
log
(
'decodeAudioData'
+
i
,
err
);
});
}
}
const
transmit
=
()
=>
{
let
idx
=
prompt
(
'请输入文件序号'
,
'10'
);
if
(
idx
===
null
||
idx
===
undefined
)
return
;
let
file
=
files
[
parseInt
(
idx
)];
if
(
file
===
null
||
file
===
undefined
)
return
alert
(
'文件不存在'
);
jensen
.
getFile
(
file
.
name
,
file
.
length
,
(
data
:
Uint8Array
|
'fail'
)
=>
{},
(
b
:
number
)
=>
{
console
.
log
(
'progress'
,
b
)});
setTimeout
(()
=>
{
jensen
.
stopReceive
();
console
.
log
(
'stop receive.......'
);
setTimeout
(()
=>
{
jensen
.
resumeReceive
();
console
.
log
(
'resume receive.......'
);
},
1500
);
},
5000
);
}
return
(
<>
...
...
@@ -346,6 +417,7 @@ export function Home() {
<
button
onClick=
{
listFiles
}
>
文件列表
</
button
>
<
button
onClick=
{
prepareAudio
}
>
准备音频
</
button
>
<
button
onClick=
{
test
}
>
传输与播放
</
button
>
<
button
onClick=
{
transmit
}
>
传输中断测试
</
button
>
<
button
onClick=
{
getFilePart
}
>
获取文件
</
button
>
<
button
onClick=
{
writeSN
}
>
SN写号
</
button
>
<
button
onClick=
{
getTime
}
>
获取时间
</
button
>
...
...
src/utils/jensen.js
View file @
c2355191
import
{
Logger
as
internalLogger
,
formatTime
,
shortcutKeys
,
sliceTime
}
from
'./utils'
;
import
md5
from
'js-md5'
;
const
INVAILD
=
0x00
;
const
QUERY_DEVICE_INFO
=
0x01
;
...
...
@@ -26,7 +27,7 @@ const TONE_UPDATE = 0x17;
const
REQUEST_UAC_UPDATE
=
0x18
;
const
UAC_UPDATE
=
0x19
;
const
FACTORY_RESET
=
0xf00b
;
const
GET_BATTERY_STATUS
=
0x1004
;
const
REALTIME_READ_SETTING
=
0x20
;
const
REALTIME_CONTROL
=
0x21
;
const
REALTIME_TRANSFER
=
0x22
;
...
...
@@ -50,20 +51,20 @@ const COMMAND_NAMES = {
[
DELETE_FILE
]:
'delete-file'
,
[
REQUEST_FIRMWARE_UPGRADE
]:
'request-firmware-upgrade'
,
[
FIRMWARE_UPLOAD
]:
'firmware-upload'
,
[
READ_CARD_INFO
]:
'read
-card-
info'
,
[
FORMAT_CARD
]:
'format
-
card'
,
[
GET_RECORDING_FILE
]:
'get
-recording-
file'
,
[
RESTORE_FACTORY_SETTINGS
]:
'restore
-factory-
settings'
,
[
SCHEDULE_INFO
]:
'
calendar
'
,
[
DEVICE_MSG_TEST
]:
'test'
,
[
BNC_DEMO_TEST
]:
'bnc
-demo
'
,
[
READ_CARD_INFO
]:
'read
card
info'
,
[
FORMAT_CARD
]:
'format
card'
,
[
GET_RECORDING_FILE
]:
'get
recording
file'
,
[
RESTORE_FACTORY_SETTINGS
]:
'restore
factory
settings'
,
[
SCHEDULE_INFO
]:
'
send meeting schedule info
'
,
[
DEVICE_MSG_TEST
]:
'
device msg
test'
,
[
BNC_DEMO_TEST
]:
'bnc
demo test
'
,
[
GET_SETTINGS
]:
'get-settings'
,
[
SET_SETTINGS
]:
'set-settings'
,
[
GET_FILE_BLOCK
]:
'get
-file-
block'
,
[
FACTORY_RESET
]:
'factory
-
reset'
,
[
TEST_SN_WRITE
]:
'test
-sn-
write'
,
[
RECORD_TEST_START
]:
'record
-test-
start'
,
[
RECORD_TEST_END
]:
'record
-test-
end'
,
[
GET_FILE_BLOCK
]:
'get
file
block'
,
[
FACTORY_RESET
]:
'factory
reset'
,
[
TEST_SN_WRITE
]:
'test
sn
write'
,
[
RECORD_TEST_START
]:
'record
test
start'
,
[
RECORD_TEST_END
]:
'record
test
end'
,
[
BLUETOOTH_SCAN
]:
'bluetooth-scan'
,
[
BLUETOOTH_CMD
]:
'bluetooth-cmd'
,
[
BLUETOOTH_STATUS
]:
'bluetooth-status'
...
...
@@ -86,9 +87,11 @@ function Jensen(log, conn) {
let
totalBytes
=
0
;
let
handlers
=
{};
let
pid
=
0
;
let
recvable
=
true
;
let
self
=
this
;
this
.
data
=
{};
this
.
sqidx
=
new
Date
().
getTime
();
// 消息
this
.
decodeTimeout
=
0
;
this
.
timewait
=
1
;
...
...
@@ -122,6 +125,20 @@ function Jensen(log, conn) {
self
.
versionNumber
=
null
;
};
function
determineModel
(
productId
)
{
if
(
productId
==
0xb00c
)
return
'hidock-h1'
;
if
(
productId
==
0xb00d
)
return
'hidock-h1e'
;
if
(
productId
==
0xb00e
)
return
'hidock-p1'
;
if
(
productId
==
0xb00f
)
return
'hidock-p1:mini'
;
if
(
productId
==
0x0100
)
return
'hidock-h1'
;
if
(
productId
==
0x0101
)
return
'hidock-h1e'
;
if
(
productId
==
0x0102
)
return
'hidock-h1'
;
if
(
productId
==
0x0103
)
return
'hidock-h1e'
;
if
(
productId
==
0x2040
)
return
'hidock-p1'
;
if
(
productId
==
0x2041
)
return
'hidock-p1:mini'
;
return
'unknown'
;
}
const
setup
=
async
function
(
disableOnConnect
)
{
if
(
ready
)
return
;
self
.
versionCode
=
null
;
...
...
@@ -132,10 +149,7 @@ function Jensen(log, conn) {
await
device
.
claimInterface
(
0
);
await
device
.
selectAlternateInterface
(
0
,
0
);
pid
=
device
.
productId
;
self
.
model
=
device
.
productId
==
0xb00c
?
'hidock-h1'
:
device
.
productId
==
0xb00d
?
'hidock-h1e'
:
device
.
productId
==
0xb00e
?
'hidock-p1'
:
device
.
productId
==
0xb00f
?
'hidock-p1:mini'
:
'unknown'
;
self
.
model
=
determineModel
(
device
.
productId
);
Logger
.
info
(
'jensen'
,
'connect'
,
'device pid: '
+
device
.
productId
);
}
catch
(
e
)
{
Logger
.
error
(
'jensen'
,
'setup'
,
String
(
e
));
...
...
@@ -152,26 +166,27 @@ function Jensen(log, conn) {
}
};
this
.
initialize
=
async
function
()
{
if
(
conn
)
{
this
.
initialize
=
async
function
()
{
if
(
conn
)
{
if
(
!
conn
.
opened
)
conn
.
open
();
await
setup
();
}
return
true
;
}
this
.
sequence
=
function
()
{
return
this
.
sqidx
++
;
}
this
.
reconnect
=
async
function
()
{
try
{
try
{
await
device
.
close
();
await
device
.
open
();
ready
=
false
;
await
setup
();
return
true
;
}
catch
(
ex
)
{
catch
(
ex
)
{
Logger
.
error
(
'jensen'
,
'reconnect'
,
ex
);
}
}
...
...
@@ -182,7 +197,7 @@ function Jensen(log, conn) {
if
(
r
)
return
;
let
conn
=
await
navigator
.
usb
.
requestDevice
({
filters
:
[{
vendorId
:
0x10d6
}]
filters
:
[{
vendorId
:
0x10d6
}
,
{
vendorId
:
0x3887
}
]
});
await
conn
.
open
();
// self.model = conn.productId == 45069 ? 'hidock-h1e' : 'hidock-h1';
...
...
@@ -232,6 +247,19 @@ function Jensen(log, conn) {
setup
();
}
this
.
stopReceive
=
function
()
{
recvable
=
false
;
}
this
.
resumeReceive
=
async
function
()
{
recvable
=
true
;
if
(
current
)
trigger
(
null
,
current
.
replace
(
/^cmd-
(\d
+
)
-
(\d
+
)
$/gi
,
'$1'
));
blocks
.
length
=
0
;
totalBytes
=
0
;
// 不发送一下,是不会回应数据的
await
this
.
getTime
(
5
);
}
this
.
isConnected
=
function
()
{
return
device
!=
null
;
};
...
...
@@ -240,7 +268,7 @@ function Jensen(log, conn) {
Logger
.
info
(
'jensen'
,
'disconnect'
,
'disconnect'
);
try
{
await
device
?.
close
();
}
catch
(
e
)
{}
}
catch
(
e
)
{
}
};
this
.
send
=
function
(
cmd
,
seconds
,
onprogress
)
{
...
...
@@ -322,11 +350,16 @@ function Jensen(log, conn) {
});
};
this
.
_trigger
=
function
(
resp
,
msgid
)
{
trigger
(
resp
,
msgid
);
}
const
trigger
=
function
(
resp
,
msgid
)
{
if
(
current
==
null
)
return
;
Logger
.
debug
(
'jensen'
,
'trigger'
,
pid
+
':trigger - '
+
current
.
substring
(
0
,
current
.
lastIndexOf
(
'-'
))
+
' <---> cmd-'
+
(
msgid
||
'err'
));
if
(
current
.
substring
(
0
,
current
.
lastIndexOf
(
'-'
))
!=
'cmd-'
+
msgid
)
{
// current = null;
Logger
.
debug
(
'jensen'
,
'trigger'
,
'msgid mismatched'
);
return
;
}
if
(
current
in
actions
==
false
)
{
...
...
@@ -345,6 +378,7 @@ function Jensen(log, conn) {
let
p
=
actions
[
tag
];
p
.
resolve
(
null
);
delete
actions
[
tag
];
current
=
null
;
};
this
.
dump
=
function
()
{
...
...
@@ -355,7 +389,7 @@ function Jensen(log, conn) {
};
const
tryReceive
=
function
()
{
if
(
device
)
if
(
device
&&
recvable
)
// console.log('trace', device);
device
.
transferIn
(
2
,
RECV_BUFF_SIZE
).
then
((
r
)
=>
{
Logger
.
save
?.(
'jensen'
,
'tryReceive'
,
r
?.
data
);
...
...
@@ -378,9 +412,13 @@ function Jensen(log, conn) {
blocks
.
push
(
result
.
data
);
tryReceive
();
if
(
self
.
decodeTimeout
)
clearTimeout
(
self
.
decodeTimeout
);
self
.
decodeTimeout
=
setTimeout
(
function
()
{
tryDecode
();
},
self
.
timewait
);
self
.
decodeTimeout
=
setTimeout
(
function
()
{
tryDecode
();
},
self
.
timewait
);
if
(
self
.
onreceive
)
{
try
{
self
.
onreceive
(
totalBytes
);
}
catch
(
e
)
{}
try
{
self
.
onreceive
(
totalBytes
);
}
catch
(
e
)
{
}
}
};
...
...
@@ -451,6 +489,7 @@ function Jensen(log, conn) {
if
(
!
handler
)
handler
=
Jensen
.
handlers
[
msgid
];
handler
(
null
,
self
);
}
catch
(
e
)
{
console
.
error
(
e
);
trigger
(
e
);
Logger
.
error
(
'jensen'
,
'decode'
,
'decode error: '
+
String
(
e
));
}
...
...
@@ -667,14 +706,12 @@ Jensen.prototype.listFiles = async function () {
this
[
tag
]
=
[];
this
.
registerHandler
(
QUERY_FILE_LIST
,
(
msg
,
jensen
)
=>
{
console
.
log
(
'tag'
,
tag
);
if
(
msg
.
body
.
length
==
0
)
{
if
(
msg
.
body
.
length
==
0
)
{
console
.
log
(
'remove tag1: '
+
tag
);
jensen
[
tag
]
=
null
;
return
[];
}
if
(
jensen
[
tag
]
==
null
)
{
if
(
jensen
[
tag
]
==
null
)
{
console
.
trace
(
'no tag for: '
+
tag
);
return
'x'
;
}
...
...
@@ -693,7 +730,7 @@ Jensen.prototype.listFiles = async function () {
let
fnpad
=
function
(
v
)
{
return
v
>
9
?
v
:
'0'
+
v
;
};
for
(
let
i
=
start
;
i
<
data
.
length
;
)
{
for
(
let
i
=
start
;
i
<
data
.
length
;)
{
let
len
=
0
;
let
fname
=
[];
...
...
@@ -728,24 +765,19 @@ Jensen.prototype.listFiles = async function () {
ftime
=
new
Date
(
ftime
);
duration
=
(
flen
/
32
)
*
4
;
}
else
{
else
{
ftime
=
null
;
}
if
(
ver
==
1
)
{
if
(
ver
==
1
)
{
duration
=
duration
*
2
;
}
else
if
(
ver
==
2
)
{
else
if
(
ver
==
2
)
{
duration
=
(
flen
-
44
)
/
48
/
2
;
}
else
if
(
ver
==
3
)
{
else
if
(
ver
==
3
)
{
duration
=
(
flen
-
44
)
/
48
/
2
/
2
;
}
else
if
(
ver
==
5
)
{
else
if
(
ver
==
5
)
{
duration
=
flen
/
12
;
}
let
createDate
=
''
;
...
...
@@ -790,8 +822,7 @@ Jensen.prototype.listFiles = async function () {
// 如果已经等待过长的时间了,那就直接返回好了
let
kv
=
tag
+
'-timer'
;
let
now
=
new
Date
().
getTime
();
if
(
kv
in
jensen
)
{
if
(
kv
in
jensen
)
{
window
.
clearTimeout
(
jensen
[
kv
]);
}
...
...
@@ -830,27 +861,6 @@ Jensen.prototype.readFile = async function (filename, offset, length, seconds) {
return
this
.
send
(
new
Command
(
TRANSFER_FILE_PARTIAL
).
body
(
data
),
seconds
);
}
Jensen
.
prototype
.
transferFile
=
async
function
(
filename
,
length
,
ondata
,
seconds
)
{
let
fname
=
[];
for
(
let
i
=
0
;
i
<
filename
.
length
;
i
++
)
fname
.
push
(
filename
.
charCodeAt
(
i
));
let
flen
=
0
;
this
.
registerHandler
(
TRANSFER_FILE
,
(
msg
)
=>
{
if
(
msg
!=
null
)
{
flen
+=
msg
.
body
.
length
||
msg
.
body
.
byteLength
;
ondata
(
msg
.
body
);
Logger
.
info
(
'jensen'
,
'transfer-file'
,
`
${
length
}
${
flen
}
`
);
if
(
flen
>=
length
)
{
Logger
.
info
(
'jensen'
,
'transfer-file'
,
'file transfer finish.'
);
return
'OK'
;
}
}
else
{
Logger
.
info
(
'jensen'
,
'transfer-file'
,
'file transfer fail.'
);
ondata
(
'fail'
);
}
});
return
this
.
send
(
new
Command
(
TRANSFER_FILE
).
body
(
fname
),
seconds
);
}
Jensen
.
prototype
.
setTime
=
async
function
(
time
,
seconds
)
{
let
str
=
time
.
getFullYear
()
+
...
...
@@ -1006,17 +1016,14 @@ Jensen.prototype.getFile = async function (filename, length, ondata, onprogress)
};
// this.onreceive = onprogress;
this
.
_progress_report
=
{};
this
.
onreceive
=
function
(
recvBytes
)
{
this
.
onreceive
=
function
(
recvBytes
)
{
let
percent
=
Math
.
floor
(
recvBytes
/
length
*
100
);
let
k
=
't'
+
percent
;
if
(
percent
%
5
==
0
&&
percent
>
0
&&
percent
<
100
)
{
if
(
percent
%
5
==
0
&&
percent
>
0
&&
percent
<
100
)
{
this
.
_progress_report
[
k
]
&&
onprogress
?.(
recvBytes
);
this
.
_progress_report
[
k
]
=
true
;
}
if
(
percent
==
100
)
{
if
(
percent
==
100
)
{
onprogress
?.(
recvBytes
);
}
}
...
...
@@ -1129,7 +1136,10 @@ Jensen.prototype.getFileBlock = async function (filename, length, ondata) {
return
this
.
send
(
new
Command
(
GET_FILE_BLOCK
).
body
(
data
));
};
Jensen
.
prototype
.
getBatteryStatus
=
function
(
seconds
)
{
if
(
this
.
model
!=
'hidock-p1'
)
return
null
;
return
this
.
send
(
new
Command
(
GET_BATTERY_STATUS
),
seconds
);
}
Jensen
.
prototype
.
writeSerialNumber
=
async
function
(
sn
)
{
let
data
=
[];
for
(
let
i
=
0
;
i
<
sn
.
length
;
i
++
)
{
...
...
@@ -1169,7 +1179,27 @@ Jensen.prototype.sendScheduleInfo = function (infos) {
return
this
.
send
(
new
Command
(
SCHEDULE_INFO
).
body
(
bodys
));
}
};
Jensen
.
prototype
.
transferFile
=
async
function
(
filename
,
length
,
ondata
,
seconds
)
{
let
fname
=
[];
for
(
let
i
=
0
;
i
<
filename
.
length
;
i
++
)
fname
.
push
(
filename
.
charCodeAt
(
i
));
let
flen
=
0
;
this
.
registerHandler
(
TRANSFER_FILE
,
(
msg
)
=>
{
if
(
msg
!=
null
)
{
flen
+=
msg
.
body
.
length
||
msg
.
body
.
byteLength
;
ondata
(
msg
.
body
);
// Logger.info('jensen', 'transfer-file', `${length} ${flen}`);
if
(
flen
>=
length
)
{
// Logger.info('jensen', 'transfer-file', 'file transfer finish.');
ondata
(
null
);
return
'OK'
;
}
}
else
{
Logger
.
info
(
'jensen'
,
'transfer-file'
,
'file transfer fail.'
);
ondata
(
'fail'
);
}
});
return
this
.
send
(
new
Command
(
TRANSFER_FILE
).
body
(
fname
),
seconds
);
}
Jensen
.
prototype
.
getRealtimeSettings
=
async
function
()
{
return
this
.
send
(
new
Command
(
REALTIME_READ_SETTING
));
};
...
...
@@ -1189,10 +1219,9 @@ Jensen.prototype.getRealtime = async function (frames) {
let
d
=
(
frames
>>
0
)
&
0xff
;
return
this
.
send
(
new
Command
(
REALTIME_TRANSFER
).
body
([
a
,
b
,
c
,
d
]));
};
Jensen
.
prototype
.
requestToneUpdate
=
async
function
(
signature
,
size
,
seconds
)
{
Jensen
.
prototype
.
requestToneUpdate
=
async
function
(
signature
,
size
,
seconds
)
{
let
data
=
[];
for
(
let
i
=
0
;
i
<
signature
.
length
;
i
+=
2
)
{
for
(
let
i
=
0
;
i
<
signature
.
length
;
i
+=
2
)
{
let
b
=
signature
.
substring
(
i
,
i
+
2
);
data
.
push
(
parseInt
(
b
,
16
));
}
...
...
@@ -1202,13 +1231,12 @@ Jensen.prototype.requestToneUpdate = async function(signature, size, seconds) {
data
.
push
((
size
>>
0
)
&
0xff
);
return
this
.
send
(
new
Command
(
REQUEST_TONE_UPDATE
).
body
(
data
),
seconds
);
}
Jensen
.
prototype
.
updateTone
=
async
function
(
toneFile
,
seconds
)
{
Jensen
.
prototype
.
updateTone
=
async
function
(
toneFile
,
seconds
)
{
return
this
.
send
(
new
Command
(
TONE_UPDATE
).
body
(
toneFile
),
seconds
);
}
Jensen
.
prototype
.
requestUACUpdate
=
async
function
(
signature
,
size
,
seconds
)
{
Jensen
.
prototype
.
requestUACUpdate
=
async
function
(
signature
,
size
,
seconds
)
{
let
data
=
[];
for
(
let
i
=
0
;
i
<
signature
.
length
;
i
+=
2
)
{
for
(
let
i
=
0
;
i
<
signature
.
length
;
i
+=
2
)
{
let
b
=
signature
.
substring
(
i
,
i
+
2
);
data
.
push
(
parseInt
(
b
,
16
));
}
...
...
@@ -1218,7 +1246,7 @@ Jensen.prototype.requestUACUpdate = async function(signature, size, seconds) {
data
.
push
((
size
>>
0
)
&
0xff
);
return
this
.
send
(
new
Command
(
REQUEST_UAC_UPDATE
).
body
(
data
),
seconds
);
}
Jensen
.
prototype
.
updateUAC
=
async
function
(
uacFile
,
seconds
)
{
Jensen
.
prototype
.
updateUAC
=
async
function
(
uacFile
,
seconds
)
{
return
this
.
send
(
new
Command
(
UAC_UPDATE
).
body
(
uacFile
),
seconds
);
}
Jensen
.
registerHandler
(
REALTIME_CONTROL
,
commonMessageParser
);
...
...
@@ -1226,6 +1254,16 @@ Jensen.registerHandler(REALTIME_READ_SETTING, (msg) => {
// console.log(msg);
return
msg
;
});
Jensen
.
registerHandler
(
GET_BATTERY_STATUS
,
(
msg
)
=>
{
let
status
=
msg
.
body
[
0
]
&
0xff
;
let
percent
=
msg
.
body
[
1
]
&
0xff
;
let
voltage
=
((
msg
.
body
[
2
]
&
0xff
)
<<
24
)
|
((
msg
.
body
[
3
]
&
0xff
)
<<
16
)
|
((
msg
.
body
[
4
]
&
0xff
)
<<
8
)
|
(
msg
.
body
[
5
]
&
0xff
);
return
{
status
:
status
==
0
?
'idle'
:
(
status
==
1
?
'charging'
:
'full'
),
battery
:
percent
,
voltage
:
voltage
};
})
Jensen
.
registerHandler
(
REALTIME_TRANSFER
,
(
msg
)
=>
{
let
a
=
msg
.
body
[
0
]
&
0xff
;
let
b
=
msg
.
body
[
1
]
&
0xff
;
...
...
@@ -1336,8 +1374,7 @@ Jensen.registerHandler(READ_CARD_INFO, (msg) => {
});
Jensen
.
registerHandler
(
TRANSFER_FILE_PARTIAL
,
(
msg
)
=>
{
let
buf
=
new
Uint8Array
(
msg
.
body
.
length
);
for
(
let
i
=
0
;
i
<
msg
.
body
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
msg
.
body
.
length
;
i
++
)
{
buf
[
i
]
=
msg
.
body
[
i
]
&
0xff
;
}
return
buf
;
...
...
@@ -1385,6 +1422,7 @@ Jensen.registerHandler(GET_RECORDING_FILE, (msg) => {
Jensen
.
registerHandler
(
BLUETOOTH_SCAN
,
(
msg
)
=>
{
// console.log('bluetooth-scan', msg);
if
(
msg
.
body
.
length
==
0
)
return
[];
let
nums
=
((
msg
.
body
[
0
]
&
0xff
)
<<
8
)
|
(
msg
.
body
[
1
]
&
0xff
);
let
devices
=
[];
let
decoder
=
new
TextDecoder
(
'UTF-8'
);
...
...
@@ -1449,7 +1487,7 @@ Jensen.registerHandler(REQUEST_TONE_UPDATE, (msg) => {
else
if
(
rst
==
0x03
)
txt
=
'card-full'
;
else
if
(
rst
==
0x04
)
txt
=
'card-error'
;
else
txt
=
String
(
rst
);
return
{
code
:
rst
,
result
:
txt
};
return
{
code
:
rst
,
result
:
txt
};
});
Jensen
.
registerHandler
(
TONE_UPDATE
,
commonMessageParser
);
Jensen
.
registerHandler
(
REQUEST_UAC_UPDATE
,
(
msg
)
=>
{
...
...
@@ -1460,7 +1498,7 @@ Jensen.registerHandler(REQUEST_UAC_UPDATE, (msg) => {
else
if
(
rst
==
0x03
)
txt
=
'card-full'
;
else
if
(
rst
==
0x04
)
txt
=
'card-error'
;
else
txt
=
String
(
rst
);
return
{
code
:
rst
,
result
:
txt
};
return
{
code
:
rst
,
result
:
txt
};
});
Jensen
.
registerHandler
(
UAC_UPDATE
,
commonMessageParser
);
export
{
Jensen
};
\ No newline at end of file
src/waveform-worker.js
0 → 100644
View file @
c2355191
/**
* 模拟 Recorder.getRecordAnalyseData 的功能
* @param {Int16Array} pcmData PCM 原始数据,s16le 格式
* @returns {Uint8Array} 长度固定 1024,范围 0–255 的波形数据
*/
function
getRecordAnalyseData
(
pcmData
)
{
const
BUF_LEN
=
800
;
const
out
=
new
Uint8Array
(
BUF_LEN
);
const
pcm
=
[];
for
(
let
i
=
0
;
i
<
pcmData
.
length
;
i
+=
2
)
{
let
h
=
pcmData
[
i
]
&
0xff
;
let
l
=
pcmData
[
i
+
1
]
&
0xff
;
let
p
=
(
l
<<
8
)
|
h
;
// 如果p是负数,则取反
if
(
p
&
0x8000
)
p
=
(
p
&
0x7fff
)
-
0x8000
;
pcm
.
push
(
p
);
}
const
len
=
pcm
.
length
;
if
(
len
===
0
)
return
out
.
fill
(
128
);
// 空数据,填充中值
const
step
=
len
/
BUF_LEN
;
let
rst
=
[];
for
(
let
i
=
0
;
i
<
BUF_LEN
;
i
++
)
{
const
start
=
Math
.
floor
(
i
*
step
);
const
end
=
Math
.
min
(
Math
.
floor
((
i
+
1
)
*
step
),
len
);
// 计算这一段的平均值(保持正负号,显示真实波形)
let
sum
=
0
;
for
(
let
j
=
start
;
j
<
end
;
j
++
)
{
sum
+=
Math
.
abs
(
pcm
[
j
]);
}
const
avg
=
Math
.
floor
(
sum
/
(
end
-
start
));
rst
.
push
(
avg
);
// 将平均值从 [-32768, 32767] 映射到 [0, 255]
// 128 是中心线(静音),0是最负值,255是最正值
const
normalized
=
avg
/
32767
;
// 归一化到 [0, 1]
out
[
i
]
=
Math
.
max
(
0
,
Math
.
min
(
127
,
Math
.
round
(
normalized
*
127
)));
}
return
out
;
}
self
.
addEventListener
(
'message'
,
async
function
(
e
)
{
try
{
const
task
=
e
.
data
;
// pcmdata的值为浮点类型的0~1,需要转换为s16le格式
const
pcmData
=
new
Int16Array
(
task
.
pcmdata
.
length
);
for
(
let
i
=
0
;
i
<
task
.
pcmdata
.
length
;
i
++
)
{
pcmData
[
i
]
=
Math
.
round
(
task
.
pcmdata
[
i
]
*
32767
);
}
// 生成波形分析数据
const
waveformData
=
getRecordAnalyseData
(
pcmData
);
// 返回结果给主线程
self
.
postMessage
({
id
:
task
.
id
,
success
:
true
,
waveform
:
waveformData
,
});
}
catch
(
error
)
{
console
.
error
(
'Worker error:'
,
error
);
// 返回错误信息给主线程
self
.
postMessage
({
id
:
task
.
id
,
success
:
false
,
error
:
error
.
message
});
}
});
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment