1. 调教了琴谱,现在可以识别更多乐谱。

2.增加了更多乐谱
3.增加了调度器配置,现在可以在调度器中调用设置
This commit is contained in:
石李城
2024-12-29 14:10:37 +08:00
parent 1dfb1fe698
commit f06985104c
17 changed files with 726 additions and 64 deletions

View File

@@ -1,12 +1,16 @@
/**
* 谱子说明:/为节拍线,每两个/之间为一拍每个字符占1/4拍小括号 )内视为一个整体,一起按下,中括号【 】内从左到右依次快速演奏。
* @author 阿城同学
* @link https://github.com/shilic
*/
// 所有的代码必须由 async function 包裹
(async function () {
log.info("[{YuanQin}]正在启动", "原琴");
/* ------------------------------------------------------1. 数据结构区, 以下代码用于预定义数据结构-------------------------------------------------- */
/** 乐谱文件,后期可以优化,将该文件放到 settings.json 文件中进行配置。 */ // assets/JOJO黄金之风.json
let scoreFilePath = "assets/孤勇者.json";
// 从配置中获取你选择的乐谱
let selectValue = settings.selectValue;
//log.info("selectValue:{options}",selectValue);
let scoreFilePath = `assets/${selectValue}.json`;
/** 定义分割类型枚举 */
const SplitTypeEnum = {
/** 括号分割 */
@@ -18,12 +22,14 @@
};
/** 定义音符类型枚举 */
const ToneTypeEnum = {
/** 该音符表示一个长停顿,例如某段歌曲中的间隔 就是换气谁TM 唱歌不换气啊) */
/** 该音符表示一个长停顿,例如某段歌曲中的间隔 就是换气谁TM 唱歌不换气啊)。文件中用 / 表示 */
LongPause : 0,
/** 该音符表示一个单音符(简简单单) */
Single : 1,
/** 该音符表示一个多音符,即多个音符同时按下(原琴玩家表示办不到啊,只有用脚本了) */
/** 该音符表示一个多音符,即多个音符同时按下(原琴玩家表示办不到啊,只有用脚本了) ,如 (QA) 表示这两个同时按下 */
Multi : 2,
/** 一个短间隔,在文件中用 <> 表示 ,表示左右两个字符需要快速弹奏。 */
ShortInterval : 3,
};
/** 音符对象模型 */
var ToneObject = {
@@ -34,6 +40,8 @@
var MusicScoreObject = {
/** 乐谱名称 */
name : "" ,
/** 一个短间隔(单位:毫秒)。在文件中用 <> 表示 ,表示左右两个字符需要快速弹奏。 */
shortMills : 80,
/** 暂停间隔 (单位:毫秒)。当你按下一个音符时,需要暂停这么多毫秒的时间再按下一个音符(废话,难道你一直按啊)*/
pauseMills : 200 ,
/** 长间隔 (单位:毫秒)。表示一个长停顿,例如某段歌曲中的间隔 。就是换气谁TM 唱歌不换气啊)*/
@@ -46,19 +54,23 @@
toneList : [] ,
};
/* ----------------------------------------------------2. 执行区域 ,以下代码用于正式执行操作------------------------------------------------------ */
/** 乐谱对象 */
/** 乐谱对象
* @class MusicScoreObject
*/
let musicScore = await getMusicScoreFromFile(scoreFilePath); // 使用 await 来等待函数执行完毕。获取乐谱
//await printMusicScore(musicScore); //打印一个乐谱的基本信息
////await printMusicScore(musicScore); //打印一个乐谱的基本信息
let ifParse = await parseScore(musicScore); // 解析乐谱
if(ifParse){
log.info("音符解析成功,尝试播放《{name}》",musicScore.name);
await printTones(musicScore); // 打印解析后的音符列表
//await printTones(musicScore); // 打印解析后的音符列表
await sleep(3000);
await playMusic(musicScore);
}
else{
log.error("音符解析失败");
}
//await regexTest2();
/* ---------------------------------------------------3. 方法区 , 以下代码用于定义方法------------------------------------------------------------*/
/**
* 从文件中读取一个乐谱
@@ -72,7 +84,7 @@
try {
fileContent = file.readTextSync(scorePath); // 同步阻塞式的将乐谱从文件中读取。
//log.info("{fileContent}",fileContent); // 用于测试读取文件,测试成功
log.info("[{y}]读取乐谱文件《{scorePath}》成功","原琴",scorePath);
//log.info("[{y}]读取乐谱文件《{scorePath}》成功","原琴",scorePath);
} catch (error) {
log.error("[{y}]在读取文件 《{scorePath}》时,发生错误","原琴",scorePath);
return null ;
@@ -82,33 +94,43 @@
return null ;
}
// 一些正则表达式,用于解析你的乐谱文件
const scoreNameRegex = /"\s*name\s*"\s*:\s*"\s*(?<scoreName>[^"\s]+)\s*"\s*,/;
const scoreNameRegex = /"\s*name\s*"\s*:\s*"\s*(?<scoreName>[^"]+)\s*"\s*,/;
const scoreShortRegex = /"\s*short\s*"\s*:\s*"?\s*(?<scoreShort>\d+)\s*"?\s*,/;
const scorePauseRegex = /"\s*pause\s*"\s*:\s*"?\s*(?<scorePause>\d+)\s*"?\s*,/;
const scoreLongPauseRegex = /"\s*longPause\s*"\s*:\s*"?\s*(?<scoreLongPause>\d+)\s*"?\s*,/;
const scoreSplitRegex = /"\s*split\s*"\s*:\s*"\s*(?<scoreSplit>[^"\s]+)\s*"\s*,/;
const scoreTonesRegex = /"\s*toneStr\s*"\s*:\s*"\s*(?<scoreTones>[^"]+)\s*"\s*,?/;
// 执行正则表达式 匹配操作
const scoreNameMatch = scoreNameRegex.exec(fileContent);
const scoreShortMatch = scoreShortRegex.exec(fileContent);
const scorePauseMatch = scorePauseRegex.exec(fileContent);
const scoreLongPauseMatch = scoreLongPauseRegex.exec(fileContent);
const scoreSplitMatch = scoreSplitRegex.exec(fileContent);
const scoreTonesMatch = scoreTonesRegex.exec(fileContent);
if(scoreNameMatch.groups.scoreName != null) { // 可能会出现无法捕获的情况,例如用户没有填这个字段。给代码增加容错性。
// 得到匹配结果
// 可能会出现无法捕获的情况,例如用户没有填这个字段。给代码增加容错性。
// 使用 try-catch 语句替换了原来的空值判断看来java的正则表达式和js还是不太一样java是可以直接获取但是可能为空而js是直接报错。
try {
musicScore.name = scoreNameMatch.groups.scoreName;
} catch (error) {
log.error("未识别到乐曲名称");
}
if(scorePauseMatch.groups.scorePause != null) {
let value = parseInt(scorePauseMatch.groups.scorePause);
musicScore.pauseMills = value;
try {
musicScore.shortMills = parseInt(scoreShortMatch.groups.scoreShort) ;
} catch (error) {
log.error("未识别到短间隔");
}
if(scoreLongPauseMatch.groups.scoreLongPause != null){
let str = scoreLongPauseMatch.groups.scoreLongPause;
//log.debug("长间隔 String = {m}",str);
let value = parseInt(str);
//log.debug("长间隔 int = {v}",value);
musicScore.longPauseMills = value;
try {
musicScore.pauseMills = parseInt(scorePauseMatch.groups.scorePause);
} catch (error) {
log.error("未识别到普通间隔");
}
if(scoreSplitMatch.groups.scoreSplit != null){ // 分割类型 ,如 括号
//log.debug("开始识别 分割类型");
try {
musicScore.longPauseMills = parseInt(scoreLongPauseMatch.groups.scoreLongPause);
} catch (error) {
log.error("未识别到长间隔");
}
try {
let value = scoreSplitMatch.groups.scoreSplit;
if(value === "括号"){
musicScore.split = SplitTypeEnum.Bracket ;
@@ -117,10 +139,13 @@
}else{
musicScore.split = SplitTypeEnum.Bracket ;
}
} catch (error) {
log.error("未识别到分割类型");
}
if(scoreTonesMatch.groups.scoreTones != null){
//log.debug("开始识别 音符");
try {
musicScore.toneStr = scoreTonesMatch.groups.scoreTones;
} catch (error) {
log.error("未识别到乐谱");
}
return musicScore;
} //getMusicScoreFromFile 从文件中读取一个乐谱
@@ -148,8 +173,8 @@
async function parseTones(tonesString ,splitType) {
let toneList = [] ; //预定义返回值
/** 括号分割模式下,用于识别音符的正则表达式 /g 表示全局匹配*/
// /([(]\s*(?<multi>[A-Za-z]{2,})\s*[)])|(?<single>[a-zA-Z])/g
const BracketRegex =/([(]\s*(?<multi>[A-Za-z]{2,})\s*[)])|(?<single>[a-zA-Z])|(?<LongPause>[+])/g ;
// /([(]\s*(?<multi>[A-Za-z]{2,})\s*[)])|(?<single>[a-zA-Z])|(?<LongPause>[/])/g
const BracketRegex =/([(]\s*(?<multi>[A-Za-z]{2,})\s*[)])|(?<single>[a-zA-Z])|(?<LongPause>[/|])|(?<shortPause>[<\[\{]\s*(?<item>[A-Za-z]{2,})\s*[\}>\]])/g ; // 由 + 号表示长间隔,改为了由 / 表示
let regex = null;
if(splitType == SplitTypeEnum.Bracket){ // 括号分割
regex = BracketRegex;
@@ -168,6 +193,7 @@
let tone = Object.create(ToneObject); // 通过原型创建一个音符对象
tone.tones = [] ;
let value ;
if(match.groups.multi != null){
//log.debug("{m}",match[1]);
tone.toneType = ToneTypeEnum.Multi ;
@@ -176,12 +202,14 @@
for (const element of charArray) {
tone.tones.push(element);
}
//log.debug("multi ={m}",value);
}
if(match.groups.single != null){
tone.toneType = ToneTypeEnum.Single ;
value = match.groups.single ; // 如 B
//tone.tones.push(value);
tone.tones = value ;
//log.debug("single ={m}",value);
}
if(match.groups.LongPause != null){
tone.toneType = ToneTypeEnum.LongPause ;
@@ -192,6 +220,19 @@
// }
tone.tones = value ;
}
if(match.groups.shortPause != null){
tone.toneType = ToneTypeEnum.ShortInterval ;
//let shortPauseValue = match.groups.shortPause ; // <JHHKJ>
let value = match.groups.item ; // JHHKJ
const charArray = Array.from(value);
for (const element of charArray) {
tone.tones.push(element);
}
//log.info("快速弹奏的音符:{m}",shortPauseValue);
}
// else{
// log.info("未识别到快速弹奏的音符{m}",match.groups.shortPause);
// }
toneList.push(tone);
// //log.debug("{m}",value);
//toneArray.push(value);
@@ -212,9 +253,9 @@
let tones = toneObject.tones;
switch(type){
case ToneTypeEnum.Multi:
toneStr = toneStr + "[" ;
toneStr = toneStr + "(" ;
toneStr = toneStr + tones;
toneStr = toneStr + "]" ;
toneStr = toneStr + ")" ;
toneStr = toneStr + " ";
break;
case ToneTypeEnum.Single:
@@ -225,52 +266,53 @@
toneStr = toneStr + tones;
toneStr = toneStr + " ";
break;
case ToneTypeEnum.ShortInterval:
toneStr = toneStr + "<" ;
toneStr = toneStr + tones;
toneStr = toneStr + ">" ;
toneStr = toneStr + " ";
break;
default:
break;
}
} // 遍历所有音符
log.info("{m}",toneStr);
} // 打印解析后的音符列表
/**
* 打印解析后的乐谱的基本信息
* @param {MusicScoreObject} musicScore
*/
async function printMusicScore(musicScore) {
log.debug("22尝试输出捕获到的乐谱名称:{m}",musicScore.name); // 例如输出:[scoreName,黄金之风],或输出: 黄金之风
log.debug("尝试输出捕获到的乐谱暂停间隔:{m}",musicScore.pauseMills);
log.debug("尝试输出捕获到的乐谱长间隔:{m}",musicScore.longPauseMills);
log.debug("尝试输出捕获到的分割类型:{m}",musicScore.split);
//log.debug("尝试输出捕获到的音符列表:{m}",musicScore.toneStr);
} //打印一个乐谱
/**
* 执行播放操作
* @param {MusicScoreObject} musicScore
*/
async function playMusic(musicScore) {
let mills = musicScore.pauseMills ;
let shortMills = musicScore.shortMills;
let longPause = musicScore.longPauseMills ;
let toneList = musicScore.toneList;
for(const toneObject of toneList) { // 遍历所有音符
let type = toneObject.toneType;
let tones = toneObject.tones;
// 1. 按下按键,并松开
// 1. 按下按键,并松开
switch(type){
case ToneTypeEnum.Multi:
playSingle(tones);
await playMulti(tones);
await sleep(mills);
break;
case ToneTypeEnum.Single:
playMulti(tones);
await playSingle(tones);
await sleep(mills);
break;
case ToneTypeEnum.LongPause:
await sleep(longPause);
break;
case ToneTypeEnum.ShortInterval:
for(const item of tones){
await playSingle(item);
await sleep(shortMills) ;
}
break;
default:
break;
}
// 2. 间隔一定时间
} // 遍历所有音符
} //执行播放操作
/**
@@ -296,23 +338,14 @@
keyUp(key);
}
} // 模拟多个按键同时按下
async function regexTest2(params) {
let regex = /((?<item>[A-Za-z])~?)/g;
let match;
let input = "J~H~J~W~K";
while(match = regex.exec(input)){
log.info("音符{m}",match.groups.item);
//log.info("音符{m}",match[1]);
} //while
}
/** 这段代码用于测试正则表达捕获组的输出
*/
async function testRegex() {
let str = '{systemName=系统1, systemId=1001} , {systemName=系统2,systemId=1002} , {systemName=系统3, systemId=1003}';
let reg = /systemName=(.*?),/g;
// str .match(reg);
// reg.exec(str )
let result = "";
while(result = reg.exec(str)) {
log.info(result[1]);
}
}
/** 这段代码用于测试播放效果 */
async function playTest() {
await sleep(1000);
let array = ["E","H","N"];
playMulti(array);
}
})();// 所有的代码必须由 async function 包裹