diff --git a/_server/config.json b/_server/config.json index 3ebb25c..84aac55 100644 --- a/_server/config.json +++ b/_server/config.json @@ -1 +1 @@ -{"viewportLoc":[0,0],"editorLastFloorId":"jiuguan"} \ No newline at end of file +{"viewportLoc":[0,0],"editorLastFloorId":"street02"} \ No newline at end of file diff --git a/_server/editor_blocklyconfig.js b/_server/editor_blocklyconfig.js index be72015..6c99618 100644 --- a/_server/editor_blocklyconfig.js +++ b/_server/editor_blocklyconfig.js @@ -1,705 +1,908 @@ -editor_blocklyconfig=(function(){ -// start mark sfergsvae +editor_blocklyconfig = function () { + // start mark sfergsvae + (function () { + var getCategory = function (name, custom) { + for (var node of document.getElementById("toolbox").children) { + if (node.getAttribute("name") == name) return node; + } + var node = document.createElement("category"); + node.setAttribute("name", name); + if (custom) node.setAttribute("custom", custom); + document.getElementById("toolbox").appendChild(node); + return node; + }; - -(function(){ - var getCategory = function(name,custom){ - for(var node of document.getElementById('toolbox').children) { - if(node.getAttribute('name')==name) return node; - } - var node = document.createElement('category'); - node.setAttribute('name',name); - if(custom)node.setAttribute('custom',custom); - document.getElementById('toolbox').appendChild(node); - return node; - } - - var toolboxObj = { - '入口方块':[ - '', - MotaActionFunctions.actionParser.parse([ - "欢迎使用事件编辑器", - "本事件触发一次后会消失", - {"type": "hide", "time": 500}, - ],'event'), - MotaActionFunctions.actionParser.parse({ - "condition": "flag:__door__===2", - "currentFloor": true, - "priority": 0, - "delayExecute": false, - "multiExecute": false, - "data": [ - {"type": "openDoor", "loc": [10,5]} - ], - },'autoEvent'), - MotaActionBlocks['changeFloor_m'].xmlText(), - MotaActionFunctions.actionParser.parse([{ - "id": "shop1", - "text": "\t[贪婪之神,moneyShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:", - "textInList": "1F金币商店", - "choices": [ - {"text": "生命+800", "need": "status:money>=20+2*flag:shop1", "action": [ - {"type": "comment", "text": "新版商店中需要手动扣减金币和增加访问次数"}, - {"type": "setValue", "name": "status:money", "operator": "-=", "value": "20+2*flag:shop1"}, - {"type": "setValue", "name": "flag:shop1", "operator": "+=", "value": "1"}, - {"type": "setValue", "name": "status:hp", "operator": "+=", "value": "800"} - ]} - ] - },{ - "id": "itemShop", - "item": true, - "textInList": "道具商店", - "choices": [ - {"id": "yellowKey", "number": 10, "money": 10} - ] - },{ - "id": "keyShop1", - "textInList": "回收钥匙商店", - "commonEvent": "回收钥匙商店", - "args": "" - }],'shop'), - MotaActionBlocks['common_m'].xmlText(), - MotaActionBlocks['beforeBattle_m'].xmlText(), - MotaActionBlocks['afterBattle_m'].xmlText(), - MotaActionBlocks['afterGetItem_m'].xmlText(), - MotaActionBlocks['afterOpenDoor_m'].xmlText(), - MotaActionBlocks['firstArrive_m'].xmlText(), - MotaActionBlocks['eachArrive_m'].xmlText(), - MotaActionBlocks['level_m'].xmlText(), - MotaActionFunctions.actionParser.parse([ - ['MTx', ''] - ], 'floorPartition'), - MotaActionBlocks['commonEvent_m'].xmlText(), - MotaActionBlocks['item_m'].xmlText(), - MotaActionFunctions.actionParser.parse([ - {"title":"简单", "name": "Easy", "hard": 1, "action": [ - {"type": "comment", "text": "在这里写该难度需执行的事件"} - ]} - ], 'levelChoose'), - MotaActionFunctions.actionParser.parse({ - "type": 0, "value": {"atk": 10}, "percentage": {"speed": 10}, - }, 'equip'), - MotaActionFunctions.actionParser.parse([{ - "name": "bg.jpg", "x": 0, "y": 0, "canvas": "bg" - }], 'floorImage'), - MotaActionFunctions.actionParser.parse({ - "time": 160, "openSound": "door.mp3", "closeSound": "door.mp3", "keys": {"yellowKey": 1, "orangeKey": 1} - }, 'doorInfo'), - MotaActionBlocks['faceIds_m'].xmlText(), - MotaActionBlocks['mainStyle_m'].xmlText(), - MotaActionFunctions.actionParser.parse({ - "背景音乐": "bgm.mp3", "确定": "confirm.mp3", "攻击": "attack.mp3", "背景图": "bg.jpg", "领域": "zone", "文件名": "file.jpg" - }, 'nameMap'), - MotaActionFunctions.actionParser.parse([ - {"name": "hero.png", "width": 32, "height": 32, "prefix": "hero_"}, - ], 'splitImages'), - ], - '显示文字':[ - MotaActionBlocks['text_0_s'].xmlText(), - MotaActionBlocks['text_1_s'].xmlText(), - MotaActionFunctions.actionParser.parseList("\t[小妖精,fairy]\f[fairy.png,0,0]欢迎使用事件编辑器(双击方块可直接预览)"), - MotaActionBlocks['moveTextBox_s'].xmlText(), - MotaActionBlocks['clearTextBox_s'].xmlText(), - MotaActionBlocks['comment_s'].xmlText(), - MotaActionBlocks['autoText_s'].xmlText(), - MotaActionBlocks['scrollText_s'].xmlText(), - MotaActionBlocks['setText_s'].xmlText(), - MotaActionBlocks['tip_s'].xmlText(), - MotaActionBlocks['confirm_s'].xmlText(), - MotaActionBlocks['choices_s'].xmlText([ - '选择剑或者盾','流浪者','man',0,'',MotaActionBlocks['choicesContext'].xmlText([ - '剑','','',null,'','',MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), - ]) - ]), - MotaActionBlocks['win_s'].xmlText(), - MotaActionBlocks['lose_s'].xmlText(), - MotaActionBlocks['restart_s'].xmlText(), - ], - '数据相关':[ - MotaActionBlocks['setValue_s'].xmlText([ - MotaActionBlocks['idIdList_e'].xmlText(['status','生命']), '=', '', false - ]), - MotaActionBlocks['setEnemy_s'].xmlText(), - MotaActionBlocks['setEnemyOnPoint_s'].xmlText(), - MotaActionBlocks['resetEnemyOnPoint_s'].xmlText(), - MotaActionBlocks['moveEnemyOnPoint_s'].xmlText(), - MotaActionBlocks['moveEnemyOnPoint_1_s'].xmlText(), - MotaActionBlocks['setEquip_s'].xmlText(), - MotaActionBlocks['setFloor_s'].xmlText(), - MotaActionBlocks['setGlobalAttribute_s'].xmlText(), - MotaActionBlocks['setGlobalValue_s'].xmlText(), - MotaActionBlocks['setGlobalFlag_s'].xmlText(), - MotaActionBlocks['setNameMap_s'].xmlText(), - MotaActionBlocks['input_s'].xmlText(), - MotaActionBlocks['input2_s'].xmlText(), - MotaActionBlocks['update_s'].xmlText(), - MotaActionBlocks['moveAction_s'].xmlText(), - MotaActionBlocks['changeFloor_s'].xmlText(), - MotaActionBlocks['changePos_s'].xmlText(), - MotaActionBlocks['battle_s'].xmlText(), - MotaActionBlocks['useItem_s'].xmlText(), - MotaActionBlocks['loadEquip_s'].xmlText(), - MotaActionBlocks['unloadEquip_s'].xmlText(), - MotaActionBlocks['openShop_s'].xmlText(), - MotaActionBlocks['disableShop_s'].xmlText(), - MotaActionBlocks['setHeroIcon_s'].xmlText(), - MotaActionBlocks['follow_s'].xmlText(), - MotaActionBlocks['unfollow_s'].xmlText(), - ], - '地图处理':[ - MotaActionBlocks['battle_1_s'].xmlText(), - MotaActionBlocks['openDoor_s'].xmlText(), - MotaActionBlocks['closeDoor_s'].xmlText(), - MotaActionBlocks['show_s'].xmlText(), - MotaActionBlocks['hide_s'].xmlText(), - MotaActionBlocks['setBlock_s'].xmlText(), - MotaActionBlocks['setBlockOpacity_s'].xmlText(), - MotaActionBlocks['setBlockFilter_s'].xmlText(), - MotaActionBlocks['turnBlock_s'].xmlText(), - MotaActionBlocks['moveHero_s'].xmlText(), - MotaActionBlocks['move_s'].xmlText(), - MotaActionBlocks['jumpHero_s'].xmlText(), - MotaActionBlocks['jumpHero_1_s'].xmlText(), - MotaActionBlocks['jump_s'].xmlText(), - MotaActionBlocks['jump_1_s'].xmlText(), - MotaActionBlocks['showBgFgMap_s'].xmlText(), - MotaActionBlocks['hideBgFgMap_s'].xmlText(), - MotaActionBlocks['setBgFgBlock_s'].xmlText(), - MotaActionBlocks['showFloorImg_s'].xmlText(), - MotaActionBlocks['hideFloorImg_s'].xmlText(), - ], - '事件控制':[ - MotaActionBlocks['if_1_s'].xmlText(), - MotaActionBlocks['if_s'].xmlText(), - MotaActionFunctions.actionParser.parseList({"type": "switch", "condition": "判别值", "caseList": [ - {"action": [{"type": "comment", "text": "当判别值是值的场合执行此事件"}]}, - {"case": "default", "action": [{"type": "comment", "text": "当没有符合的值的场合执行default事件"}]}, - ]}), - MotaActionFunctions.actionParser.parseList({"type": "for", "name": "temp:A", "from": "0", "to": "12", "step": "1", "data": []}), - MotaActionFunctions.actionParser.parseList({"type": "forEach", "name": "temp:A", "list": ["status:atk","status:def"], "data": []}), - MotaActionBlocks['while_s'].xmlText(), - MotaActionBlocks['dowhile_s'].xmlText(), - MotaActionBlocks['break_s'].xmlText(), - MotaActionBlocks['continue_s'].xmlText(), - MotaActionBlocks['exit_s'].xmlText(), - MotaActionBlocks['trigger_s'].xmlText(), - MotaActionBlocks['insert_1_s'].xmlText(), - MotaActionBlocks['insert_2_s'].xmlText(), - ], - '特效表现':[ - MotaActionBlocks['sleep_s'].xmlText(), - MotaActionFunctions.actionParser.parseList({"type": "wait", "timeout": 0, "data": [ - {"case": "keyboard", "keycode": "13,32", "action": [{"type": "comment", "text": "当按下回车(keycode=13)或空格(keycode=32)时执行此事件\n超时剩余时间会写入flag:timeout"}]}, - {"case": "mouse", "px": [0,32], "py": [0,32], "action": [{"type": "comment", "text": "当点击地图左上角时执行此事件\n超时剩余时间会写入flag:timeout"}]}, - {"case": "condition", "condition": "flag:type==0\n&&flag:keycode==13", "action": [{"type": "comment", "text": "当满足自定义条件时会执行此事件\n超时剩余时间会写入flag:timeout"}]}, - {"case": "timeout", "action": [{"type": "comment", "text": "当超时未操作时执行此事件"}]}, - ]}), - MotaActionBlocks['waitAsync_s'].xmlText(), - MotaActionBlocks['stopAsync_s'].xmlText(), - MotaActionBlocks['vibrate_s'].xmlText(), - MotaActionBlocks['animate_s'].xmlText(), - MotaActionBlocks['animate_1_s'].xmlText(), - MotaActionBlocks['stopAnimate_s'].xmlText(), - MotaActionBlocks['setViewport_s'].xmlText(), - MotaActionBlocks['setViewport_1_s'].xmlText(), - MotaActionBlocks['lockViewport_s'].xmlText(), - MotaActionBlocks['showStatusBar_s'].xmlText(), - MotaActionBlocks['hideStatusBar_s'].xmlText(), - MotaActionBlocks['setHeroOpacity_s'].xmlText(), - MotaActionBlocks['setCurtain_0_s'].xmlText(), - MotaActionBlocks['setCurtain_1_s'].xmlText(), - MotaActionBlocks['screenFlash_s'].xmlText(), - MotaActionBlocks['setWeather_s'].xmlText(), - MotaActionBlocks['callBook_s'].xmlText(), - MotaActionBlocks['callSave_s'].xmlText(), - MotaActionBlocks['autoSave_s'].xmlText(), - MotaActionBlocks['forbidSave_s'].xmlText(), - MotaActionBlocks['callLoad_s'].xmlText(), - ], - '音像处理':[ - MotaActionBlocks['showImage_s'].xmlText(), - MotaActionBlocks['showImage_1_s'].xmlText(), - MotaActionBlocks['hideImage_s'].xmlText(), - MotaActionBlocks['showTextImage_s'].xmlText(), - MotaActionBlocks['moveImage_s'].xmlText(), - MotaActionBlocks['rotateImage_s'].xmlText(), - MotaActionBlocks['scaleImage_s'].xmlText(), - MotaActionBlocks['showGif_s'].xmlText(), - MotaActionBlocks['playBgm_s'].xmlText(), - MotaActionBlocks['pauseBgm_s'].xmlText(), - MotaActionBlocks['resumeBgm_s'].xmlText(), - MotaActionBlocks['loadBgm_s'].xmlText(), - MotaActionBlocks['freeBgm_s'].xmlText(), - MotaActionBlocks['playSound_s'].xmlText(), - MotaActionBlocks['playSound_1_s'].xmlText(), - MotaActionBlocks['stopSound_s'].xmlText(), - MotaActionBlocks['setVolume_s'].xmlText(), - MotaActionBlocks['setBgmSpeed_s'].xmlText(), - ], - 'UI绘制':[ - MotaActionBlocks['previewUI_s'].xmlText(), - MotaActionBlocks['clearMap_s'].xmlText(), - MotaActionBlocks['setAttribute_s'].xmlText(), - MotaActionBlocks['setFilter_s'].xmlText(), - MotaActionBlocks['fillText_s'].xmlText(), - MotaActionBlocks['fillBoldText_s'].xmlText(), - MotaActionBlocks['drawTextContent_s'].xmlText(), - MotaActionBlocks['fillRect_s'].xmlText(), - MotaActionBlocks['strokeRect_s'].xmlText(), - MotaActionBlocks['drawLine_s'].xmlText(), - MotaActionBlocks['drawArrow_s'].xmlText(), - MotaActionBlocks['fillPolygon_s'].xmlText(), - MotaActionBlocks['strokePolygon_s'].xmlText(), - MotaActionBlocks['fillEllipse_s'].xmlText(), - MotaActionBlocks['strokeEllipse_s'].xmlText(), - MotaActionBlocks['fillArc_s'].xmlText(), - MotaActionBlocks['strokeArc_s'].xmlText(), - MotaActionBlocks['drawImage_s'].xmlText(), - MotaActionBlocks['drawImage_1_s'].xmlText(), - MotaActionBlocks['drawIcon_s'].xmlText(), - MotaActionBlocks['drawBackground_s'].xmlText(), - MotaActionBlocks['drawSelector_s'].xmlText(), - MotaActionBlocks['drawSelector_1_s'].xmlText(), - ], - '原生脚本':[ - MotaActionBlocks['function_s'].xmlText(), - MotaActionBlocks['unknown_s'].xmlText(), - ], - '值块':[ - MotaActionBlocks['setValue_s'].xmlText([ - MotaActionBlocks['idIdList_e'].xmlText(['status','生命']), '=', '', false - ]), - MotaActionBlocks['expression_arithmetic_0'].xmlText(), - MotaActionBlocks['idFlag_e'].xmlText(), - MotaActionBlocks['idTemp_e'].xmlText(), - MotaActionBlocks['negate_e'].xmlText(), - MotaActionBlocks['unaryOperation_e'].xmlText(), - MotaActionBlocks['bool_e'].xmlText(), - MotaActionBlocks['idString_e'].xmlText(), - MotaActionBlocks['idIdList_e'].xmlText(), - MotaActionBlocks['idFixedList_e'].xmlText(), - MotaActionBlocks['enemyattr_e'].xmlText(), - MotaActionBlocks['blockId_e'].xmlText(), - MotaActionBlocks['blockNumber_e'].xmlText(), - MotaActionBlocks['blockCls_e'].xmlText(), - MotaActionBlocks['hasEquip_e'].xmlText(), - MotaActionBlocks['equip_e'].xmlText(), - MotaActionBlocks['nextXY_e'].xmlText(), - MotaActionBlocks['isReplaying_e'].xmlText(), - MotaActionBlocks['hasVisitedFloor_e'].xmlText(), - MotaActionBlocks['isShopVisited_e'].xmlText(), - MotaActionBlocks['canBattle_e'].xmlText(), - MotaActionBlocks['damage_e'].xmlText(), - MotaActionBlocks['damage_1_e'].xmlText(), - MotaActionBlocks['rand_e'].xmlText(), - MotaActionBlocks['evalString_e'].xmlText(), - ], - '常见事件模板':[ - '', - MotaActionFunctions.actionParser.parseList({"type": "if", "condition": "!core.musicStatus.bgmStatus", - "true": [ - "\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳" - ], - "false": [] - }), - '', - MotaActionFunctions.actionParser.parse([ - { - "type": "if", - "condition": "switch:A", - "true": [ - "\t[行商,trader]\b[this]这是购买我的道具后我给玩家的提示。", - { - "type": "comment", - "text": "下一条指令可视情况使用或不使用" - }, - { - "type": "hide", - "remove": true, - "time": 250 - } + var toolboxObj = { + 入口方块: [ + '', + MotaActionFunctions.actionParser.parse( + [ + "欢迎使用事件编辑器", + "本事件触发一次后会消失", + { type: "hide", time: 500 }, ], - "false": [ + "event" + ), + MotaActionFunctions.actionParser.parse( + { + condition: "flag:__door__===2", + currentFloor: true, + priority: 0, + delayExecute: false, + multiExecute: false, + data: [{ type: "openDoor", loc: [10, 5] }], + }, + "autoEvent" + ), + MotaActionBlocks["changeFloor_m"].xmlText(), + MotaActionFunctions.actionParser.parse( + [ { - "type": "confirm", - "text": "我有3把黄钥匙,\n你出50金币就卖给你。", - "yes": [ + id: "shop1", + text: "\t[贪婪之神,moneyShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:", + textInList: "1F金币商店", + choices: [ { - "type": "if", - "condition": "status:money>=50", - "true": [ + text: "生命+800", + need: "status:money>=20+2*flag:shop1", + action: [ { - "type": "setValue", - "name": "status:money", - "operator": "-=", - "value": "50" + type: "comment", + text: "新版商店中需要手动扣减金币和增加访问次数", }, { - "type": "setValue", - "name": "item:yellowKey", - "operator": "+=", - "value": "3" + type: "setValue", + name: "status:money", + operator: "-=", + value: "20+2*flag:shop1", }, { - "type": "playSound", - "name": "确定", - "stop": true + type: "setValue", + name: "flag:shop1", + operator: "+=", + value: "1", }, { - "type": "setValue", - "name": "switch:A", - "value": "true" - } + type: "setValue", + name: "status:hp", + operator: "+=", + value: "800", + }, ], - "false": [ - { - "type": "playSound", - "name": "操作失败" - }, - "\t[行商,trader]\b[this]你的金币不足!" - ] - } + }, ], - "no": [] - } - ] - } - ], 'event'), - '', - MotaActionFunctions.actionParser.parse([ - { - "type": "comment", - "text": "全地图选中一个点,需要用鼠标或触屏操作" - }, - { - "type": "setValue", - "name": "temp:X", - "value": "status:x" - }, - { - "type": "setValue", - "name": "temp:Y", - "value": "status:y" - }, - { - "type": "tip", - "text": "再次点击闪烁位置确认" - }, - { - "type": "while", - "condition": "true", - "data": [ - { - "type": "drawSelector", - "image": "winskin.png", - "code": 1, - "x": "32*temp:X", - "y": "32*temp:Y", - "width": 32, - "height": 32 }, { - "type": "wait" + id: "itemShop", + item: true, + textInList: "道具商店", + choices: [{ id: "yellowKey", number: 10, money: 10 }], }, { - "type": "if", - "condition": "(flag:type === 1)", - "true": [ + id: "keyShop1", + textInList: "回收钥匙商店", + commonEvent: "回收钥匙商店", + args: "", + }, + ], + "shop" + ), + MotaActionBlocks["common_m"].xmlText(), + MotaActionBlocks["beforeBattle_m"].xmlText(), + MotaActionBlocks["afterBattle_m"].xmlText(), + MotaActionBlocks["afterGetItem_m"].xmlText(), + MotaActionBlocks["afterOpenDoor_m"].xmlText(), + MotaActionBlocks["firstArrive_m"].xmlText(), + MotaActionBlocks["eachArrive_m"].xmlText(), + MotaActionBlocks["level_m"].xmlText(), + MotaActionFunctions.actionParser.parse([["MTx", ""]], "floorPartition"), + MotaActionBlocks["commonEvent_m"].xmlText(), + MotaActionBlocks["item_m"].xmlText(), + MotaActionFunctions.actionParser.parse( + [ + { + title: "简单", + name: "Easy", + hard: 1, + action: [{ type: "comment", text: "在这里写该难度需执行的事件" }], + }, + ], + "levelChoose" + ), + MotaActionFunctions.actionParser.parse( + { + type: 0, + value: { atk: 10 }, + percentage: { speed: 10 }, + }, + "equip" + ), + MotaActionFunctions.actionParser.parse( + [ + { + name: "bg.jpg", + x: 0, + y: 0, + canvas: "bg", + }, + ], + "floorImage" + ), + MotaActionFunctions.actionParser.parse( + { + time: 160, + openSound: "door.mp3", + closeSound: "door.mp3", + keys: { yellowKey: 1, orangeKey: 1 }, + }, + "doorInfo" + ), + MotaActionBlocks["faceIds_m"].xmlText(), + MotaActionBlocks["mainStyle_m"].xmlText(), + MotaActionFunctions.actionParser.parse( + { + 背景音乐: "bgm.mp3", + 确定: "confirm.mp3", + 攻击: "attack.mp3", + 背景图: "bg.jpg", + 领域: "zone", + 文件名: "file.jpg", + }, + "nameMap" + ), + MotaActionFunctions.actionParser.parse( + [{ name: "hero.png", width: 32, height: 32, prefix: "hero_" }], + "splitImages" + ), + ], + 显示文字: [ + MotaActionBlocks["text_0_s"].xmlText(), + MotaActionBlocks["text_1_s"].xmlText(), + MotaActionFunctions.actionParser.parseList( + "\t[小妖精,fairy]\f[fairy.png,0,0]欢迎使用事件编辑器(双击方块可直接预览)" + ), + MotaActionBlocks["moveTextBox_s"].xmlText(), + MotaActionBlocks["clearTextBox_s"].xmlText(), + MotaActionBlocks["comment_s"].xmlText(), + MotaActionBlocks["autoText_s"].xmlText(), + MotaActionBlocks["scrollText_s"].xmlText(), + MotaActionBlocks["setText_s"].xmlText(), + MotaActionBlocks["tip_s"].xmlText(), + MotaActionBlocks["confirm_s"].xmlText(), + MotaActionBlocks["choices_s"].xmlText([ + "选择剑或者盾", + "流浪者", + "man", + 0, + "", + MotaActionBlocks["choicesContext"].xmlText([ + "剑", + "", + "", + null, + "", + "", + MotaActionFunctions.actionParser.parseList([ + { type: "openDoor", loc: [3, 3] }, + ]), + ]), + ]), + MotaActionBlocks["win_s"].xmlText(), + MotaActionBlocks["lose_s"].xmlText(), + MotaActionBlocks["restart_s"].xmlText(), + ], + 数据相关: [ + MotaActionBlocks["setValue_s"].xmlText([ + MotaActionBlocks["idIdList_e"].xmlText(["status", "生命"]), + "=", + "", + false, + ]), + MotaActionBlocks["setEnemy_s"].xmlText(), + MotaActionBlocks["setEnemyOnPoint_s"].xmlText(), + MotaActionBlocks["resetEnemyOnPoint_s"].xmlText(), + MotaActionBlocks["moveEnemyOnPoint_s"].xmlText(), + MotaActionBlocks["moveEnemyOnPoint_1_s"].xmlText(), + MotaActionBlocks["setEquip_s"].xmlText(), + MotaActionBlocks["setFloor_s"].xmlText(), + MotaActionBlocks["setGlobalAttribute_s"].xmlText(), + MotaActionBlocks["setGlobalValue_s"].xmlText(), + MotaActionBlocks["setGlobalFlag_s"].xmlText(), + MotaActionBlocks["setNameMap_s"].xmlText(), + MotaActionBlocks["input_s"].xmlText(), + MotaActionBlocks["input2_s"].xmlText(), + MotaActionBlocks["update_s"].xmlText(), + MotaActionBlocks["moveAction_s"].xmlText(), + MotaActionBlocks["changeFloor_s"].xmlText(), + MotaActionBlocks["changePos_s"].xmlText(), + MotaActionBlocks["battle_s"].xmlText(), + MotaActionBlocks["useItem_s"].xmlText(), + MotaActionBlocks["loadEquip_s"].xmlText(), + MotaActionBlocks["unloadEquip_s"].xmlText(), + MotaActionBlocks["openShop_s"].xmlText(), + MotaActionBlocks["disableShop_s"].xmlText(), + MotaActionBlocks["setHeroIcon_s"].xmlText(), + MotaActionBlocks["follow_s"].xmlText(), + MotaActionBlocks["unfollow_s"].xmlText(), + ], + 地图处理: [ + MotaActionBlocks["battle_1_s"].xmlText(), + MotaActionBlocks["openDoor_s"].xmlText(), + MotaActionBlocks["closeDoor_s"].xmlText(), + MotaActionBlocks["show_s"].xmlText(), + MotaActionBlocks["hide_s"].xmlText(), + MotaActionBlocks["setBlock_s"].xmlText(), + MotaActionBlocks["setBlockOpacity_s"].xmlText(), + MotaActionBlocks["setBlockFilter_s"].xmlText(), + MotaActionBlocks["turnBlock_s"].xmlText(), + MotaActionBlocks["moveHero_s"].xmlText(), + MotaActionBlocks["move_s"].xmlText(), + MotaActionBlocks["jumpHero_s"].xmlText(), + MotaActionBlocks["jumpHero_1_s"].xmlText(), + MotaActionBlocks["jump_s"].xmlText(), + MotaActionBlocks["jump_1_s"].xmlText(), + MotaActionBlocks["showBgFgMap_s"].xmlText(), + MotaActionBlocks["hideBgFgMap_s"].xmlText(), + MotaActionBlocks["setBgFgBlock_s"].xmlText(), + MotaActionBlocks["showFloorImg_s"].xmlText(), + MotaActionBlocks["hideFloorImg_s"].xmlText(), + ], + 事件控制: [ + MotaActionBlocks["if_1_s"].xmlText(), + MotaActionBlocks["if_s"].xmlText(), + MotaActionFunctions.actionParser.parseList({ + type: "switch", + condition: "判别值", + caseList: [ + { + action: [ + { type: "comment", text: "当判别值是值的场合执行此事件" }, + ], + }, + { + case: "default", + action: [ { - "type": "if", - "condition": "((temp:X===flag:x)&&(temp:Y===flag:y))", - "true": [ + type: "comment", + text: "当没有符合的值的场合执行default事件", + }, + ], + }, + ], + }), + MotaActionFunctions.actionParser.parseList({ + type: "for", + name: "temp:A", + from: "0", + to: "12", + step: "1", + data: [], + }), + MotaActionFunctions.actionParser.parseList({ + type: "forEach", + name: "temp:A", + list: ["status:atk", "status:def"], + data: [], + }), + MotaActionBlocks["while_s"].xmlText(), + MotaActionBlocks["dowhile_s"].xmlText(), + MotaActionBlocks["break_s"].xmlText(), + MotaActionBlocks["continue_s"].xmlText(), + MotaActionBlocks["exit_s"].xmlText(), + MotaActionBlocks["trigger_s"].xmlText(), + MotaActionBlocks["insert_1_s"].xmlText(), + MotaActionBlocks["insert_2_s"].xmlText(), + ], + 特效表现: [ + MotaActionBlocks["sleep_s"].xmlText(), + MotaActionFunctions.actionParser.parseList({ + type: "wait", + timeout: 0, + data: [ + { + case: "keyboard", + keycode: "13,32", + action: [ + { + type: "comment", + text: "当按下回车(keycode=13)或空格(keycode=32)时执行此事件\n超时剩余时间会写入flag:timeout", + }, + ], + }, + { + case: "mouse", + px: [0, 32], + py: [0, 32], + action: [ + { + type: "comment", + text: "当点击地图左上角时执行此事件\n超时剩余时间会写入flag:timeout", + }, + ], + }, + { + case: "condition", + condition: "flag:type==0\n&&flag:keycode==13", + action: [ + { + type: "comment", + text: "当满足自定义条件时会执行此事件\n超时剩余时间会写入flag:timeout", + }, + ], + }, + { + case: "timeout", + action: [{ type: "comment", text: "当超时未操作时执行此事件" }], + }, + ], + }), + MotaActionBlocks["waitAsync_s"].xmlText(), + MotaActionBlocks["stopAsync_s"].xmlText(), + MotaActionBlocks["vibrate_s"].xmlText(), + MotaActionBlocks["animate_s"].xmlText(), + MotaActionBlocks["animate_1_s"].xmlText(), + MotaActionBlocks["stopAnimate_s"].xmlText(), + MotaActionBlocks["setViewport_s"].xmlText(), + MotaActionBlocks["setViewport_1_s"].xmlText(), + MotaActionBlocks["lockViewport_s"].xmlText(), + MotaActionBlocks["showStatusBar_s"].xmlText(), + MotaActionBlocks["hideStatusBar_s"].xmlText(), + MotaActionBlocks["setHeroOpacity_s"].xmlText(), + MotaActionBlocks["setCurtain_0_s"].xmlText(), + MotaActionBlocks["setCurtain_1_s"].xmlText(), + MotaActionBlocks["screenFlash_s"].xmlText(), + MotaActionBlocks["setWeather_s"].xmlText(), + MotaActionBlocks["callBook_s"].xmlText(), + MotaActionBlocks["callSave_s"].xmlText(), + MotaActionBlocks["autoSave_s"].xmlText(), + MotaActionBlocks["forbidSave_s"].xmlText(), + MotaActionBlocks["callLoad_s"].xmlText(), + ], + 音像处理: [ + MotaActionBlocks["showImage_s"].xmlText(), + MotaActionBlocks["showImage_1_s"].xmlText(), + MotaActionBlocks["hideImage_s"].xmlText(), + MotaActionBlocks["showTextImage_s"].xmlText(), + MotaActionBlocks["moveImage_s"].xmlText(), + MotaActionBlocks["rotateImage_s"].xmlText(), + MotaActionBlocks["scaleImage_s"].xmlText(), + MotaActionBlocks["showGif_s"].xmlText(), + MotaActionBlocks["playBgm_s"].xmlText(), + MotaActionBlocks["pauseBgm_s"].xmlText(), + MotaActionBlocks["resumeBgm_s"].xmlText(), + MotaActionBlocks["loadBgm_s"].xmlText(), + MotaActionBlocks["freeBgm_s"].xmlText(), + MotaActionBlocks["playSound_s"].xmlText(), + MotaActionBlocks["playSound_1_s"].xmlText(), + MotaActionBlocks["stopSound_s"].xmlText(), + MotaActionBlocks["setVolume_s"].xmlText(), + MotaActionBlocks["setBgmSpeed_s"].xmlText(), + ], + UI绘制: [ + MotaActionBlocks["previewUI_s"].xmlText(), + MotaActionBlocks["clearMap_s"].xmlText(), + MotaActionBlocks["setAttribute_s"].xmlText(), + MotaActionBlocks["setFilter_s"].xmlText(), + MotaActionBlocks["fillText_s"].xmlText(), + MotaActionBlocks["fillBoldText_s"].xmlText(), + MotaActionBlocks["drawTextContent_s"].xmlText(), + MotaActionBlocks["fillRect_s"].xmlText(), + MotaActionBlocks["strokeRect_s"].xmlText(), + MotaActionBlocks["drawLine_s"].xmlText(), + MotaActionBlocks["drawArrow_s"].xmlText(), + MotaActionBlocks["fillPolygon_s"].xmlText(), + MotaActionBlocks["strokePolygon_s"].xmlText(), + MotaActionBlocks["fillEllipse_s"].xmlText(), + MotaActionBlocks["strokeEllipse_s"].xmlText(), + MotaActionBlocks["fillArc_s"].xmlText(), + MotaActionBlocks["strokeArc_s"].xmlText(), + MotaActionBlocks["drawImage_s"].xmlText(), + MotaActionBlocks["drawImage_1_s"].xmlText(), + MotaActionBlocks["drawIcon_s"].xmlText(), + MotaActionBlocks["drawBackground_s"].xmlText(), + MotaActionBlocks["drawSelector_s"].xmlText(), + MotaActionBlocks["drawSelector_1_s"].xmlText(), + ], + 原生脚本: [ + MotaActionBlocks["function_s"].xmlText(), + MotaActionBlocks["unknown_s"].xmlText(), + ], + 值块: [ + MotaActionBlocks["setValue_s"].xmlText([ + MotaActionBlocks["idIdList_e"].xmlText(["status", "生命"]), + "=", + "", + false, + ]), + MotaActionBlocks["expression_arithmetic_0"].xmlText(), + MotaActionBlocks["idFlag_e"].xmlText(), + MotaActionBlocks["idTemp_e"].xmlText(), + MotaActionBlocks["negate_e"].xmlText(), + MotaActionBlocks["unaryOperation_e"].xmlText(), + MotaActionBlocks["bool_e"].xmlText(), + MotaActionBlocks["idString_e"].xmlText(), + MotaActionBlocks["idIdList_e"].xmlText(), + MotaActionBlocks["idFixedList_e"].xmlText(), + MotaActionBlocks["enemyattr_e"].xmlText(), + MotaActionBlocks["blockId_e"].xmlText(), + MotaActionBlocks["blockNumber_e"].xmlText(), + MotaActionBlocks["blockCls_e"].xmlText(), + MotaActionBlocks["hasEquip_e"].xmlText(), + MotaActionBlocks["equip_e"].xmlText(), + MotaActionBlocks["nextXY_e"].xmlText(), + MotaActionBlocks["isReplaying_e"].xmlText(), + MotaActionBlocks["hasVisitedFloor_e"].xmlText(), + MotaActionBlocks["isShopVisited_e"].xmlText(), + MotaActionBlocks["canBattle_e"].xmlText(), + MotaActionBlocks["damage_e"].xmlText(), + MotaActionBlocks["damage_1_e"].xmlText(), + MotaActionBlocks["rand_e"].xmlText(), + MotaActionBlocks["evalString_e"].xmlText(), + ], + 常见事件模板: [ + '', + MotaActionFunctions.actionParser.parseList({ + type: "if", + condition: "!core.musicStatus.bgmStatus", + true: ["\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳"], + false: [], + }), + '', + MotaActionFunctions.actionParser.parse( + [ + { + type: "if", + condition: "switch:A", + true: [ + "\t[行商,trader]\b[this]这是购买我的道具后我给玩家的提示。", + { + type: "comment", + text: "下一条指令可视情况使用或不使用", + }, + { + type: "hide", + remove: true, + time: 250, + }, + ], + false: [ + { + type: "confirm", + text: "我有3把黄钥匙,\n你出50金币就卖给你。", + yes: [ { - "type": "break", - "n": 1 - } - ] + type: "if", + condition: "status:money>=50", + true: [ + { + type: "setValue", + name: "status:money", + operator: "-=", + value: "50", + }, + { + type: "setValue", + name: "item:yellowKey", + operator: "+=", + value: "3", + }, + { + type: "playSound", + name: "确定", + stop: true, + }, + { + type: "setValue", + name: "switch:A", + value: "true", + }, + ], + false: [ + { + type: "playSound", + name: "操作失败", + }, + "\t[行商,trader]\b[this]你的金币不足!", + ], + }, + ], + no: [], }, - { - "type": "setValue", - "name": "temp:X", - "value": "flag:x" - }, - { - "type": "setValue", - "name": "temp:Y", - "value": "flag:y" - } - ] - } - ] - }, - { - "type": "drawSelector", - "code": 1 - }, - { - "type": "comment", - "text": "流程进行到这里可以对[X,Y]点进行处理,比如" - }, - { - "type": "closeDoor", - "id": "yellowDoor", - "loc": [ - "temp:X", - "temp:Y" - ] - } - ],'event'), - '', - MotaActionFunctions.actionParser.parse([ - { - "type": "comment", - "text": "多阶段boss,请直接作为战后事件使用" - }, - { - "type": "setValue", - "name": "switch:A", - "operator": "+=", - "value": "1" - }, - { - "type": "switch", - "condition": "switch:A", - "caseList": [ + ], + }, + ], + "event" + ), + '', + MotaActionFunctions.actionParser.parse( + [ { - "case": "1", - "action": [ - { - "type": "setBlock", - "number": "redSlime" - }, - "\t[2阶段boss,redSlime]\b[this]你以为你已经打败我了吗?没听说过史莱姆有九条命吗?" - ] + type: "comment", + text: "全地图选中一个点,需要用鼠标或触屏操作", }, { - "case": "2", - "action": [ - { - "type": "setBlock", - "number": "blackSlime" - }, - "\t[3阶段boss,blackSlime]\b[this]不能消灭我的,只会让我更强大!" - ] + type: "setValue", + name: "temp:X", + value: "status:x", }, { - "case": "3", - "action": [ - { - "type": "setBlock", - "number": "slimelord" - }, - "\t[4阶段boss,slimelord]\b[this]我还能打!" - ] + type: "setValue", + name: "temp:Y", + value: "status:y", }, { - "case": "4", - "action": [ - "\t[4阶段boss,slimelord]我一定会回来的!" - ] - } - ] - } - ],'afterBattle'), - ], - '最近使用事件':[ - '', - ] - } - var toolboxgap = '' - //xml_text = MotaActionFunctions.actionParser.parse(obj,type||'event') - //MotaActionBlocks['idString_e'].xmlText() + type: "tip", + text: "再次点击闪烁位置确认", + }, + { + type: "while", + condition: "true", + data: [ + { + type: "drawSelector", + image: "winskin.png", + code: 1, + x: "32*temp:X", + y: "32*temp:Y", + width: 32, + height: 32, + }, + { + type: "wait", + }, + { + type: "if", + condition: "(flag:type === 1)", + true: [ + { + type: "if", + condition: "((temp:X===flag:x)&&(temp:Y===flag:y))", + true: [ + { + type: "break", + n: 1, + }, + ], + }, + { + type: "setValue", + name: "temp:X", + value: "flag:x", + }, + { + type: "setValue", + name: "temp:Y", + value: "flag:y", + }, + ], + }, + ], + }, + { + type: "drawSelector", + code: 1, + }, + { + type: "comment", + text: "流程进行到这里可以对[X,Y]点进行处理,比如", + }, + { + type: "closeDoor", + id: "yellowDoor", + loc: ["temp:X", "temp:Y"], + }, + ], + "event" + ), + '', + MotaActionFunctions.actionParser.parse( + [ + { + type: "comment", + text: "多阶段boss,请直接作为战后事件使用", + }, + { + type: "setValue", + name: "switch:A", + operator: "+=", + value: "1", + }, + { + type: "switch", + condition: "switch:A", + caseList: [ + { + case: "1", + action: [ + { + type: "setBlock", + number: "redSlime", + }, + "\t[2阶段boss,redSlime]\b[this]你以为你已经打败我了吗?没听说过史莱姆有九条命吗?", + ], + }, + { + case: "2", + action: [ + { + type: "setBlock", + number: "blackSlime", + }, + "\t[3阶段boss,blackSlime]\b[this]不能消灭我的,只会让我更强大!", + ], + }, + { + case: "3", + action: [ + { + type: "setBlock", + number: "slimelord", + }, + "\t[4阶段boss,slimelord]\b[this]我还能打!", + ], + }, + { + case: "4", + action: ["\t[4阶段boss,slimelord]我一定会回来的!"], + }, + ], + }, + ], + "afterBattle" + ), + ], + 最近使用事件: [ + '', + ], + }; + var toolboxgap = ''; + //xml_text = MotaActionFunctions.actionParser.parse(obj,type||'event') + //MotaActionBlocks['idString_e'].xmlText() - for (var name in toolboxObj){ - var custom = null; - if(name=='最近使用事件')custom='searchBlockCategory'; - if(name=='入口方块')custom='entranceCategory'; - getCategory(name,custom).innerHTML = toolboxObj[name].join(toolboxgap); - } - -var blocklyArea = document.getElementById('blocklyArea'); -var blocklyDiv = document.getElementById('blocklyDiv'); -var workspace = Blockly.inject(blocklyDiv,{ - media: '_server/blockly/media/', - toolbox: document.getElementById('toolbox'), - zoom:{ - controls: true, - wheel: false,//滚轮改为上下(shift:左右)翻滚 - startScale: 1.0, - maxScale: 3, - minScale: 0.3, - scaleSpeed: 1.08 - }, - trashcan: false, -}); - -editor_blockly.isCommonEntry = function () { - var commonEntries = ['beforeBattle', 'afterBattle', 'afterOpenDoor', 'firstArrive', 'eachArrive', 'commonEvent', 'item']; - return commonEntries.indexOf(editor_blockly.entryType) >= 0; -} - -editor_blockly.entranceCategoryCallback = function(workspace) { - var list=toolboxObj['入口方块'] - var xmlList = []; - var eventType = (editor_blockly.isCommonEntry() ? 'common' : editor_blockly.entryType)+'_m'; - for(var ii=0,blockText;blockText=list[ii];ii++){ - if(new RegExp('').exec(blockText)){ - var block = Blockly.Xml.textToDom(''+blockText+'').firstChild; - block.setAttribute("gap", 5); - xmlList.push(block); + for (var name in toolboxObj) { + var custom = null; + if (name == "最近使用事件") custom = "searchBlockCategory"; + if (name == "入口方块") custom = "entranceCategory"; + getCategory(name, custom).innerHTML = toolboxObj[name].join(toolboxgap); } - } - return xmlList; -} -workspace.registerToolboxCategoryCallback( - 'entranceCategory', editor_blockly.entranceCategoryCallback); + var blocklyArea = document.getElementById("blocklyArea"); + var blocklyDiv = document.getElementById("blocklyDiv"); + var workspace = Blockly.inject(blocklyDiv, { + media: "_server/blockly/media/", + toolbox: document.getElementById("toolbox"), + zoom: { + controls: true, + wheel: false, //滚轮改为上下(shift:左右)翻滚 + startScale: 1.0, + maxScale: 3, + minScale: 0.3, + scaleSpeed: 1.08, + }, + trashcan: false, + }); -editor_blockly.searchBlockCategoryCallback = function(workspace) { - var xmlList = []; - var labels = editor_blockly.searchBlock(); - for (var i = 0; i < labels.length; i++) { - var blockText = '' + - MotaActionBlocks[labels[i]].xmlText() + - ''; - var block = Blockly.Xml.textToDom(blockText).firstChild; - block.setAttribute("gap", 5); - xmlList.push(block); - } - return xmlList; -}; + editor_blockly.isCommonEntry = function () { + var commonEntries = [ + "beforeBattle", + "afterBattle", + "afterOpenDoor", + "firstArrive", + "eachArrive", + "commonEvent", + "item", + ]; + return commonEntries.indexOf(editor_blockly.entryType) >= 0; + }; -workspace.registerToolboxCategoryCallback( - 'searchBlockCategory', editor_blockly.searchBlockCategoryCallback); - -var onresize = function(e) { - blocklyDiv.style.width = blocklyArea.offsetWidth + 'px'; - blocklyDiv.style.height = blocklyArea.offsetHeight + 'px'; - Blockly.svgResize(workspace); -}; -if(typeof editor !== "undefined" && !editor.isMobile)window.addEventListener('resize', onresize, false); -onresize(); -//Blockly.svgResize(workspace); - -//Blockly.bindEventWithChecks_(workspace.svgGroup_,"wheel",workspace,function(e){}); -document.getElementById('blocklyDiv').onmousewheel = function(e){ - //console.log(e); - e.preventDefault(); - var hvScroll = e.shiftKey?'hScroll':'vScroll'; - var mousewheelOffsetValue=20/380*workspace.scrollbar[hvScroll].handleLength_*3; - workspace.scrollbar[hvScroll].handlePosition_+=( ((e.deltaY||0)+(e.detail||0)) >0?mousewheelOffsetValue:-mousewheelOffsetValue); - workspace.scrollbar[hvScroll].onScroll_(); - // workspace.setScale(workspace.scale); -} - -var doubleClickCheck=[[0,'abc']]; -function omitedcheckUpdateFunction(event) { - if(event.type==='create'){ - editor_blockly.addIntoLastUsedType(event.blockId); - } - if(event.type==='ui' && event.element == 'click'){ - var newClick = [new Date().getTime(),event.blockId]; - var lastClick = doubleClickCheck.shift(); - doubleClickCheck.push(newClick); - if(newClick[0]-lastClick[0]<500){ - if(newClick[1]===lastClick[1]){ - editor_blockly.doubleClickBlock(newClick[1]); - } - } - } - // Only handle these events - if (["create", "move", "change", "delete"].indexOf(event.type) < 0) return; - if(editor_blockly.workspace.topBlocks_.length>=2){ - editor_blockly.setValue('入口方块只能有一个'); - return; - } - var eventType = editor_blockly.entryType; - if(editor_blockly.workspace.topBlocks_.length==1){ - var blockType = editor_blockly.workspace.topBlocks_[0].type; - if(blockType!==eventType+'_m' && !(editor_blockly.isCommonEntry() && blockType == 'common_m')){ - editor_blockly.setValue('入口方块类型错误'); - return; - } - } - try { - var code = Blockly.JavaScript.workspaceToCode(workspace).replace(/\\(i|c|d|e|g|z)/g, '\\\\$1'); - editor_blockly.setValue(code); - } catch (error) { - editor_blockly.setValue(String(error)); - if (error instanceof OmitedError){ - var blockName = error.blockName; - var varName = error.varName; - var block = error.block; - } - // console.log(error); - } - } - - workspace.addChangeListener(omitedcheckUpdateFunction); - - workspace.addChangeListener(Blockly.Events.disableOrphans); - - editor_blockly.workspace = workspace; - - MotaActionFunctions.workspace = function(){ - return editor_blockly.workspace; - } - - // 因为在editor_blockly.parse里已经HTML转义过一次了,所以这里要覆盖掉以避免在注释中出现<等 - MotaActionFunctions.xmlText = function (ruleName,inputs,isShadow,comment,collapsed,disabled) { - var rule = MotaActionBlocks[ruleName]; - var blocktext = isShadow?'shadow':'block'; - var xmlText = []; - xmlText.push('<'+blocktext+' type="'+ruleName+'"'+(collapsed ? ' collapsed="true"' : '')+(disabled ? ' disabled="true"' : '')+'>'); - if(!inputs)inputs=[]; - for (var ii=0,inputType;inputType=rule.argsType[ii];ii++) { - var input = inputs[ii]; - var _input = ''; - var noinput = (input===null || input===undefined); - if(noinput && inputType==='field' && MotaActionBlocks[rule.argsGrammarName[ii]].type!=='field_dropdown') continue; - if(noinput && inputType==='field') { - noinput = false; - input = rule.fieldDefault(rule.args[ii]) - } - if(noinput) input = ''; - if(inputType==='field' && MotaActionBlocks[rule.argsGrammarName[ii]].type==='field_checkbox')input=input?'TRUE':'FALSE'; - if(inputType!=='field') { - var subList = false; - var subrulename = rule.argsGrammarName[ii]; - var subrule = MotaActionBlocks[subrulename]; - if (subrule instanceof Array) { - subrulename=subrule[subrule.length-1]; - subrule = MotaActionBlocks[subrulename]; - subList = true; - } - _input = subrule.xmlText([],true); - if(noinput && !subList && !isShadow) { - //无输入的默认行为是: 如果语句块的备选方块只有一个,直接代入方块 - input = subrule.xmlText(); + editor_blockly.entranceCategoryCallback = function (workspace) { + var list = toolboxObj["入口方块"]; + var xmlList = []; + var eventType = + (editor_blockly.isCommonEntry() ? "common" : editor_blockly.entryType) + + "_m"; + for (var ii = 0, blockText; (blockText = list[ii]); ii++) { + if (new RegExp('').exec(blockText)) { + var block = Blockly.Xml.textToDom( + "" + blockText + "" + ).firstChild; + block.setAttribute("gap", 5); + xmlList.push(block); } } - xmlText.push('<'+inputType+' name="'+rule.args[ii]+'">'); - xmlText.push(_input+input); - xmlText.push(''); - } - if(comment){ - xmlText.push(''); - xmlText.push(comment); - xmlText.push(''); - } - var next = inputs[rule.args.length]; - if (next) {//next - xmlText.push(''); - xmlText.push(next); - xmlText.push(''); - } - xmlText.push(''); - return xmlText.join(''); - } -})(); + return xmlList; + }; + workspace.registerToolboxCategoryCallback( + "entranceCategory", + editor_blockly.entranceCategoryCallback + ); + editor_blockly.searchBlockCategoryCallback = function (workspace) { + var xmlList = []; + var labels = editor_blockly.searchBlock(); + for (var i = 0; i < labels.length; i++) { + var blockText = + "" + MotaActionBlocks[labels[i]].xmlText() + ""; + var block = Blockly.Xml.textToDom(blockText).firstChild; + block.setAttribute("gap", 5); + xmlList.push(block); + } + return xmlList; + }; -// end mark sfergsvae -}).toString().split('// start mark sfergsvae')[1].split('// end mark sfergsvae')[0] + workspace.registerToolboxCategoryCallback( + "searchBlockCategory", + editor_blockly.searchBlockCategoryCallback + ); + + var onresize = function (e) { + blocklyDiv.style.width = blocklyArea.offsetWidth + "px"; + blocklyDiv.style.height = blocklyArea.offsetHeight + "px"; + Blockly.svgResize(workspace); + }; + if (typeof editor !== "undefined" && !editor.isMobile) + window.addEventListener("resize", onresize, false); + onresize(); + //Blockly.svgResize(workspace); + + //Blockly.bindEventWithChecks_(workspace.svgGroup_,"wheel",workspace,function(e){}); + document.getElementById("blocklyDiv").onmousewheel = function (e) { + //console.log(e); + e.preventDefault(); + var hvScroll = e.shiftKey ? "hScroll" : "vScroll"; + var mousewheelOffsetValue = + (20 / 380) * workspace.scrollbar[hvScroll].handleLength_ * 3; + workspace.scrollbar[hvScroll].handlePosition_ += + (e.deltaY || 0) + (e.detail || 0) > 0 + ? mousewheelOffsetValue + : -mousewheelOffsetValue; + workspace.scrollbar[hvScroll].onScroll_(); + // workspace.setScale(workspace.scale); + }; + + var doubleClickCheck = [[0, "abc"]]; + function omitedcheckUpdateFunction(event) { + if (event.type === "create") { + editor_blockly.addIntoLastUsedType(event.blockId); + } + if (event.type === "ui" && event.element == "click") { + var newClick = [new Date().getTime(), event.blockId]; + var lastClick = doubleClickCheck.shift(); + doubleClickCheck.push(newClick); + if (newClick[0] - lastClick[0] < 500) { + if (newClick[1] === lastClick[1]) { + editor_blockly.doubleClickBlock(newClick[1]); + } + } + } + // Only handle these events + if (["create", "move", "change", "delete"].indexOf(event.type) < 0) + return; + if (editor_blockly.workspace.topBlocks_.length >= 2) { + editor_blockly.setValue("入口方块只能有一个"); + return; + } + var eventType = editor_blockly.entryType; + if (editor_blockly.workspace.topBlocks_.length == 1) { + var blockType = editor_blockly.workspace.topBlocks_[0].type; + if ( + blockType !== eventType + "_m" && + !(editor_blockly.isCommonEntry() && blockType == "common_m") + ) { + editor_blockly.setValue("入口方块类型错误"); + return; + } + } + try { + var code = Blockly.JavaScript.workspaceToCode(workspace).replace( + /\\(i|c|d|e|g|z)/g, + "\\\\$1" + ); + editor_blockly.setValue(code); + } catch (error) { + editor_blockly.setValue(String(error)); + if (error instanceof OmitedError) { + var blockName = error.blockName; + var varName = error.varName; + var block = error.block; + } + // console.log(error); + } + } + + workspace.addChangeListener(omitedcheckUpdateFunction); + + workspace.addChangeListener(Blockly.Events.disableOrphans); + + editor_blockly.workspace = workspace; + + MotaActionFunctions.workspace = function () { + return editor_blockly.workspace; + }; + + // 因为在editor_blockly.parse里已经HTML转义过一次了,所以这里要覆盖掉以避免在注释中出现<等 + MotaActionFunctions.xmlText = function ( + ruleName, + inputs, + isShadow, + comment, + collapsed, + disabled + ) { + var rule = MotaActionBlocks[ruleName]; + var blocktext = isShadow ? "shadow" : "block"; + var xmlText = []; + xmlText.push( + "<" + + blocktext + + ' type="' + + ruleName + + '"' + + (collapsed ? ' collapsed="true"' : "") + + (disabled ? ' disabled="true"' : "") + + ">" + ); + if (!inputs) inputs = []; + for (var ii = 0, inputType; (inputType = rule.argsType[ii]); ii++) { + var input = inputs[ii]; + var _input = ""; + var noinput = input === null || input === undefined; + if ( + noinput && + inputType === "field" && + MotaActionBlocks[rule.argsGrammarName[ii]].type !== "field_dropdown" + ) + continue; + if (noinput && inputType === "field") { + noinput = false; + input = rule.fieldDefault(rule.args[ii]); + } + if (noinput) input = ""; + if ( + inputType === "field" && + MotaActionBlocks[rule.argsGrammarName[ii]].type === "field_checkbox" + ) + input = input ? "TRUE" : "FALSE"; + if (inputType !== "field") { + var subList = false; + var subrulename = rule.argsGrammarName[ii]; + var subrule = MotaActionBlocks[subrulename]; + if (subrule instanceof Array) { + subrulename = subrule[subrule.length - 1]; + subrule = MotaActionBlocks[subrulename]; + subList = true; + } + _input = subrule.xmlText([], true); + if (noinput && !subList && !isShadow) { + //无输入的默认行为是: 如果语句块的备选方块只有一个,直接代入方块 + input = subrule.xmlText(); + } + } + xmlText.push("<" + inputType + ' name="' + rule.args[ii] + '">'); + xmlText.push(_input + input); + xmlText.push(""); + } + if (comment) { + xmlText.push(""); + xmlText.push(comment); + xmlText.push(""); + } + var next = inputs[rule.args.length]; + if (next) { + //next + xmlText.push(""); + xmlText.push(next); + xmlText.push(""); + } + xmlText.push(""); + return xmlText.join(""); + }; + })(); + + // end mark sfergsvae +} + .toString() + .split("// start mark sfergsvae")[1] + .split("// end mark sfergsvae")[0]; diff --git a/_server/table/data.comment.js b/_server/table/data.comment.js index 10e76cf..b6c921a 100644 --- a/_server/table/data.comment.js +++ b/_server/table/data.comment.js @@ -89,7 +89,7 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_range": "editor.mode.checkUnique(thiseval)", "_directory": "./project/bgms/", "_transform": (function (one) { - if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac')) + if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac') || one.endsWith('.opus')) return one; return null; }).toString(), @@ -147,7 +147,7 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_type": "material", "_directory": "./project/bgms/", "_transform": (function (one) { - if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac')) + if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac') || one.endsWith('.opus')) return one; return null; }).toString(), diff --git a/index.html b/index.html index dceab9b..c49e005 100644 --- a/index.html +++ b/index.html @@ -213,6 +213,9 @@
+ + + diff --git a/libs/thirdparty/codec-parser.min.js b/libs/thirdparty/codec-parser.min.js new file mode 100644 index 0000000..fb4ac2e --- /dev/null +++ b/libs/thirdparty/codec-parser.min.js @@ -0,0 +1 @@ +var CodecParser=function(e){"use strict";const t=Symbol,s=(()=>{const e="left",t="center",s="right";return["","front ","side ","rear "].map((i=>[[e,s],[e,s,t],[e,t,s],[t,e,s],[t]].flatMap((e=>e.map((e=>i+e)).join(", ")))))})(),i="LFE",r="monophonic (mono)",n="stereo",a="surround",o=(e,...t)=>`${[r,n,`linear ${a}`,"quadraphonic",`5.0 ${a}`,`5.1 ${a}`,`6.1 ${a}`,`7.1 ${a}`][e-1]} (${t.join(", ")})`,h=[r,o(2,s[0][0]),o(3,s[0][2]),o(4,s[1][0],s[3][0]),o(5,s[1][2],s[3][0]),o(6,s[1][2],s[3][0],i),o(7,s[1][2],s[2][0],s[3][4],i),o(8,s[1][2],s[2][0],s[3][0],i)],c=48e3,l=44100,u=32e3,d=24e3,p=22050,_=16e3,m=8e3,g="absoluteGranulePosition",f="bandwidth",b="bitDepth",C="bitrate",y=C+"Maximum",S=C+"Minimum",w=C+"Nominal",P="buffer",k=P+"Fullness",v="codec",F=v+"Frames",x="coupledStreamCount",H="crc",I=H+"16",M=H+"32",U="data",O="description",N="duration",$="emphasis",A="hasOpusPadding",R="header",B="isContinuedPacket",L="isCopyrighted",D="isFirstPage",T="isHome",E="isLastPage",G="isOriginal",z="isPrivate",q="isVbr",V="layer",j="length",W="mode",J=W+"Extension",K="mpeg",Q=K+"Version",X="numberAACFrames",Y="outputGain",Z="preSkip",ee="profile",te=t(),se="protection",ie="rawData",re="segments",ne="subarray",ae="version",oe="vorbis",he=oe+"Comments",ce=oe+"Setup",le="block",ue=le+"ingStrategy",de=t(),pe=le+"Size",_e=le+"size0",me=le+"size1",ge=t(),fe="channel",be=fe+"MappingFamily",Ce=fe+"MappingTable",ye=fe+"Mode",Se=t(),we=fe+"s",Pe="copyright",ke=Pe+"Id",ve=Pe+"IdStart",Fe="frame",xe=Fe+"Count",He=Fe+"Length",Ie="Number",Me=Fe+Ie,Ue=Fe+"Padding",Oe=Fe+"Size",Ne="Rate",$e="inputSample"+Ne,Ae="page",Re=Ae+"Checksum",Be=t(),Le=Ae+"SegmentTable",De=Ae+"Sequence"+Ie,Te="sample",Ee=Te+Ie,Ge=Te+Ne,ze=t(),qe=Te+"s",Ve="stream",je=Ve+"Count",We=Ve+"Info",Je=Ve+"Serial"+Ie,Ke=Ve+"StructureVersion",Qe="total",Xe=Qe+"BytesOut",Ye=Qe+"Duration",Ze=Qe+"Samples",et=t(),tt=t(),st=t(),it=t(),rt=t(),nt=t(),at=t(),ot=t(),ht=t(),ct=t(),lt=t(),ut=t(),dt=t(),pt=t(),_t=t(),mt=t(),gt=t(),ft=t(),bt=Uint8Array,Ct=DataView,yt="reserved",St="bad",wt="free",Pt="none",kt="16bit CRC",vt=(e,t,s)=>{for(let i=0;i0;e--)r=s(r);e[i]=r}return e},Ft=vt(new bt(256),(e=>e),(e=>128&e?7^e<<1:e<<1)),xt=[vt(new Uint16Array(256),(e=>e<<8),(e=>e<<1^(32768&e?32773:0)))],Ht=[vt(new Uint32Array(256),(e=>e),(e=>e>>>1^3988292384*(1&e)))];for(let e=0;e<15;e++){xt.push(new Uint16Array(256)),Ht.push(new Uint32Array(256));for(let t=0;t<=255;t++)xt[e+1][t]=xt[0][xt[e][t]>>>8]^xt[e][t]<<8,Ht[e+1][t]=Ht[e][t]>>>8^Ht[0][255&Ht[e][t]]}const It=e=>{const t=e[j],s=t-16;let i=0,r=0;for(;r<=s;)i=Ht[15][255&(e[r++]^i)]^Ht[14][255&(e[r++]^i>>>8)]^Ht[13][255&(e[r++]^i>>>16)]^Ht[12][e[r++]^i>>>24]^Ht[11][e[r++]]^Ht[10][e[r++]]^Ht[9][e[r++]]^Ht[8][e[r++]]^Ht[7][e[r++]]^Ht[6][e[r++]]^Ht[5][e[r++]]^Ht[4][e[r++]]^Ht[3][e[r++]]^Ht[2][e[r++]]^Ht[1][e[r++]]^Ht[0][e[r++]];for(;r!==t;)i=Ht[0][255&(i^e[r++])]^i>>>8;return~i},Mt=(...e)=>{const t=new bt(e.reduce(((e,t)=>e+t[j]),0));return e.reduce(((e,s)=>(t.set(s,e),e+s[j])),0),t},Ut=e=>String.fromCharCode(...e),Ot=[0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15],Nt=e=>Ot[15&e]<<4|Ot[e>>4];class $t{constructor(e){this._data=e,this._pos=8*e[j]}set position(e){this._pos=e}get position(){return this._pos}read(e){const t=Math.floor(this._pos/8),s=this._pos%8;this._pos-=e;return(Nt(this._data[t-1])<<8)+Nt(this._data[t])>>7-s&255}}class At{constructor(e,t){this._onCodecHeader=e,this._onCodecUpdate=t,this[_t]()}[mt](){this._isEnabled=!0}[_t](){this._headerCache=new Map,this._codecUpdateData=new WeakMap,this._codecHeaderSent=!1,this._codecShouldUpdate=!1,this._bitrate=null,this._isEnabled=!1}[pt](e,t){if(this._onCodecUpdate){this._bitrate!==e&&(this._bitrate=e,this._codecShouldUpdate=!0);const s=this._codecUpdateData.get(this._headerCache.get(this._currentHeader));this._codecShouldUpdate&&s&&this._onCodecUpdate({bitrate:e,...s},t),this._codecShouldUpdate=!1}}[ht](e){const t=this._headerCache.get(e);return t&&this._updateCurrentHeader(e),t}[ct](e,t,s){this._isEnabled&&(this._codecHeaderSent||(this._onCodecHeader({...t}),this._codecHeaderSent=!0),this._updateCurrentHeader(e),this._headerCache.set(e,t),this._codecUpdateData.set(t,s))}_updateCurrentHeader(e){this._onCodecUpdate&&e!==this._currentHeader&&(this._codecShouldUpdate=!0,this._currentHeader=e)}}const Rt=new WeakMap,Bt=new WeakMap;class Lt{constructor(e,t){this._codecParser=e,this._headerCache=t}*[at](){let e;for(;;){if(e=yield*this.Frame[lt](this._codecParser,this._headerCache,0),e)return e;this._codecParser[tt](1)}}*[ot](e){let t=yield*this[at]();const s=Bt.get(t)[j];if(e||this._codecParser._flushing||(yield*this.Header[ht](this._codecParser,this._headerCache,s)))return this._headerCache[mt](),this._codecParser[tt](s),this._codecParser[it](t),t;this._codecParser[rt](`Missing ${Fe} at ${s} bytes from current position.`,`Dropping current ${Fe} and trying again.`),this._headerCache[_t](),this._codecParser[tt](1)}}class Dt{constructor(e,t){Bt.set(this,{[R]:e}),this[U]=t}}class Tt extends Dt{static*[lt](e,t,s,i,r){const n=yield*e[ht](s,i,r);if(n){const e=Rt.get(n)[He],i=Rt.get(n)[qe];return new t(n,(yield*s[et](e,r))[ne](0,e),i)}return null}constructor(e,t,s){super(e,t),this[R]=e,this[qe]=s,this[N]=s/e[Ge]*1e3,this[Me]=null,this[Xe]=null,this[Ze]=null,this[Ye]=null,Bt.get(this)[j]=t[j]}}const Et="unsynchronizationFlag",Gt="extendedHeaderFlag",zt="experimentalFlag",qt="footerPresent";class Vt{static*getID3v2Header(e,t,s){const i={};let r=yield*e[et](3,s);if(73!==r[0]||68!==r[1]||51!==r[2])return null;if(r=yield*e[et](10,s),i[ae]=`id3v2.${r[3]}.${r[4]}`,15&r[5])return null;if(i[Et]=!!(128&r[5]),i[Gt]=!!(64&r[5]),i[zt]=!!(32&r[5]),i[qt]=!!(16&r[5]),128&r[6]||128&r[7]||128&r[8]||128&r[9])return null;const n=r[6]<<21|r[7]<<14|r[8]<<7|r[9];return i[j]=10+n,new Vt(i)}constructor(e){this[ae]=e[ae],this[Et]=e[Et],this[Gt]=e[Gt],this[zt]=e[zt],this[qt]=e[qt],this[j]=e[j]}}class jt{constructor(e){Rt.set(this,e),this[b]=e[b],this[C]=null,this[we]=e[we],this[ye]=e[ye],this[Ge]=e[Ge]}}const Wt={0:[wt,wt,wt,wt,wt],16:[32,32,32,32,8],240:[St,St,St,St,St]},Jt=(e,t,s)=>8*((e+s)%t+t)*(1<<(e+s)/t)-8*t*(t/8|0);for(let e=2;e<15;e++)Wt[e<<4]=[32*e,Jt(e,4,0),Jt(e,4,-1),Jt(e,8,4),Jt(e,8,0)];const Kt="bands ",Qt=" to 31",Xt={0:Kt+4+Qt,16:Kt+8+Qt,32:Kt+12+Qt,48:Kt+16+Qt},Yt="bitrateIndex",Zt="v2",es="v1",ts="Intensity stereo ",ss=", MS stereo ",is="on",rs="off",ns={0:ts+rs+ss+rs,16:ts+is+ss+rs,32:ts+rs+ss+is,48:ts+is+ss+is},as={0:{[O]:yt},2:{[O]:"Layer III",[Ue]:1,[J]:ns,[es]:{[Yt]:2,[qe]:1152},[Zt]:{[Yt]:4,[qe]:576}},4:{[O]:"Layer II",[Ue]:1,[J]:Xt,[qe]:1152,[es]:{[Yt]:1},[Zt]:{[Yt]:4}},6:{[O]:"Layer I",[Ue]:4,[J]:Xt,[qe]:384,[es]:{[Yt]:0},[Zt]:{[Yt]:3}}},os="MPEG Version ",hs="ISO/IEC ",cs={0:{[O]:`${os}2.5 (later extension of MPEG 2)`,[V]:Zt,[Ge]:{0:11025,4:12e3,8:m,12:yt}},8:{[O]:yt},16:{[O]:`${os}2 (${hs}13818-3)`,[V]:Zt,[Ge]:{0:p,4:d,8:_,12:yt}},24:{[O]:`${os}1 (${hs}11172-3)`,[V]:es,[Ge]:{0:l,4:c,8:u,12:yt}},length:j},ls={0:kt,1:Pt},us={0:Pt,1:"50/15 ms",2:yt,3:"CCIT J.17"},ds={0:{[we]:2,[O]:n},64:{[we]:2,[O]:"joint "+n},128:{[we]:2,[O]:"dual channel"},192:{[we]:1,[O]:r}};class ps extends jt{static*[ht](e,t,s){const i={},r=yield*Vt.getID3v2Header(e,t,s);r&&(yield*e[et](r[j],s),e[tt](r[j]));const n=yield*e[et](4,s),a=Ut(n[ne](0,4)),o=t[ht](a);if(o)return new ps(o);if(255!==n[0]||n[1]<224)return null;const h=cs[24&n[1]];if(h[O]===yt)return null;const c=6&n[1];if(as[c][O]===yt)return null;const l={...as[c],...as[c][h[V]]};if(i[Q]=h[O],i[V]=l[O],i[qe]=l[qe],i[se]=ls[1&n[1]],i[j]=4,i[C]=Wt[240&n[2]][l[Yt]],i[C]===St)return null;if(i[Ge]=h[Ge][12&n[2]],i[Ge]===yt)return null;if(i[Ue]=2&n[2]&&l[Ue],i[z]=!!(1&n[2]),i[He]=Math.floor(125*i[C]*i[qe]/i[Ge]+i[Ue]),!i[He])return null;const u=192&n[3];if(i[ye]=ds[u][O],i[we]=ds[u][we],i[J]=l[J][48&n[3]],i[L]=!!(8&n[3]),i[G]=!!(4&n[3]),i[$]=us[3&n[3]],i[$]===yt)return null;i[b]=16;{const{length:e,frameLength:s,samples:r,...n}=i;t[ct](a,i,n)}return new ps(i)}constructor(e){super(e),this[C]=e[C],this[$]=e[$],this[Ue]=e[Ue],this[L]=e[L],this[G]=e[G],this[z]=e[z],this[V]=e[V],this[J]=e[J],this[Q]=e[Q],this[se]=e[se]}}class _s extends Tt{static*[lt](e,t,s){return yield*super[lt](ps,_s,e,t,s)}constructor(e,t,s){super(e,t,s)}}class ms extends Lt{constructor(e,t,s){super(e,t),this.Frame=_s,this.Header=ps,s(this[v])}get[v](){return K}*[ut](){return yield*this[ot]()}}const gs={0:"MPEG-4",8:"MPEG-2"},fs={0:"valid",2:St,4:St,6:St},bs={0:kt,1:Pt},Cs={0:"AAC Main",64:"AAC LC (Low Complexity)",128:"AAC SSR (Scalable Sample Rate)",192:"AAC LTP (Long Term Prediction)"},ys={0:96e3,4:88200,8:64e3,12:c,16:l,20:u,24:d,28:p,32:_,36:12e3,40:11025,44:m,48:7350,52:yt,56:yt,60:"frequency is written explicitly"},Ss={0:{[we]:0,[O]:"Defined in AOT Specific Config"},64:{[we]:1,[O]:r},128:{[we]:2,[O]:o(2,s[0][0])},192:{[we]:3,[O]:o(3,s[1][3])},256:{[we]:4,[O]:o(4,s[1][3],s[3][4])},320:{[we]:5,[O]:o(5,s[1][3],s[3][0])},384:{[we]:6,[O]:o(6,s[1][3],s[3][0],i)},448:{[we]:8,[O]:o(8,s[1][3],s[2][0],s[3][0],i)}};class ws extends jt{static*[ht](e,t,s){const i={},r=yield*e[et](7,s),n=Ut([r[0],r[1],r[2],252&r[3]|3&r[6]]),a=t[ht](n);if(a)Object.assign(i,a);else{if(255!==r[0]||r[1]<240)return null;if(i[Q]=gs[8&r[1]],i[V]=fs[6&r[1]],i[V]===St)return null;const e=1&r[1];i[se]=bs[e],i[j]=e?7:9,i[te]=192&r[2],i[ze]=60&r[2];const s=2&r[2];if(i[ee]=Cs[i[te]],i[Ge]=ys[i[ze]],i[Ge]===yt)return null;i[z]=!!s,i[Se]=448&(r[2]<<8|r[3]),i[ye]=Ss[i[Se]][O],i[we]=Ss[i[Se]][we],i[G]=!!(32&r[3]),i[T]=!!(8&r[3]),i[ke]=!!(8&r[3]),i[ve]=!!(4&r[3]),i[b]=16,i[qe]=1024,i[X]=3&r[6];{const{length:e,channelModeBits:s,profileBits:r,sampleRateBits:a,frameLength:o,samples:h,numberAACFrames:c,...l}=i;t[ct](n,i,l)}}if(i[He]=8191&(r[3]<<11|r[4]<<3|r[5]>>5),!i[He])return null;const o=2047&(r[5]<<6|r[6]>>2);return i[k]=2047===o?"VBR":o,new ws(i)}constructor(e){super(e),this[ke]=e[ke],this[ve]=e[ve],this[k]=e[k],this[T]=e[T],this[G]=e[G],this[z]=e[z],this[V]=e[V],this[j]=e[j],this[Q]=e[Q],this[X]=e[X],this[ee]=e[ee],this[se]=e[se]}get audioSpecificConfig(){const e=Rt.get(this),t=e[te]+64<<5|e[ze]<<5|e[Se]>>3,s=new bt(2);return new Ct(s[P]).setUint16(0,t,!1),s}}class Ps extends Tt{static*[lt](e,t,s){return yield*super[lt](ws,Ps,e,t,s)}constructor(e,t,s){super(e,t,s)}}class ks extends Lt{constructor(e,t,s){super(e,t),this.Frame=Ps,this.Header=ws,s(this[v])}get[v](){return"aac"}*[ut](){return yield*this[ot]()}}class vs extends Tt{static _getFrameFooterCrc16(e){return(e[e[j]-2]<<8)+e[e[j]-1]}static[ft](e){const t=vs._getFrameFooterCrc16(e),s=(e=>{const t=e[j],s=t-16;let i=0,r=0;for(;r<=s;)i^=e[r++]<<8|e[r++],i=xt[15][i>>8]^xt[14][255&i]^xt[13][e[r++]]^xt[12][e[r++]]^xt[11][e[r++]]^xt[10][e[r++]]^xt[9][e[r++]]^xt[8][e[r++]]^xt[7][e[r++]]^xt[6][e[r++]]^xt[5][e[r++]]^xt[4][e[r++]]^xt[3][e[r++]]^xt[2][e[r++]]^xt[1][e[r++]]^xt[0][e[r++]];for(;r!==t;)i=(255&i)<<8^xt[0][i>>8^e[r++]];return i})(e[ne](0,-2));return t===s}constructor(e,t,s){t[We]=s,t[I]=vs._getFrameFooterCrc16(e),super(t,e,Rt.get(t)[qe])}}const Fs="get from STREAMINFO metadata block",xs={0:"Fixed",1:"Variable"},Hs={0:yt,16:192};for(let e=2;e<16;e++)Hs[e<<4]=e<6?576*2**(e-2):2**e;const Is={0:Fs,1:88200,2:176400,3:192e3,4:m,5:_,6:p,7:d,8:u,9:l,10:c,11:96e3,15:St},Ms={0:{[we]:1,[O]:r},16:{[we]:2,[O]:o(2,s[0][0])},32:{[we]:3,[O]:o(3,s[0][1])},48:{[we]:4,[O]:o(4,s[1][0],s[3][0])},64:{[we]:5,[O]:o(5,s[1][1],s[3][0])},80:{[we]:6,[O]:o(6,s[1][1],i,s[3][0])},96:{[we]:7,[O]:o(7,s[1][1],i,s[3][4],s[2][0])},112:{[we]:8,[O]:o(8,s[1][1],i,s[3][0],s[2][0])},128:{[we]:2,[O]:`${n} (left, diff)`},144:{[we]:2,[O]:`${n} (diff, right)`},160:{[we]:2,[O]:`${n} (avg, diff)`},176:yt,192:yt,208:yt,224:yt,240:yt},Us={0:Fs,2:8,4:12,6:yt,8:16,10:20,12:24,14:yt};class Os extends jt{static _decodeUTF8Int(e){if(e[0]>254)return null;if(e[0]<128)return{value:e[0],length:1};let t=1;for(let s=64;s&e[0];s>>=1)t++;let s=t-1,i=0,r=0;for(;s>0;r+=6,s--){if(128!=(192&e[s]))return null;i|=(63&e[s])<>t)<{let t=0;const s=e[j];for(let i=0;i!==s;i++)t=Ft[t^e[i]];return t})(i[ne](0,r[j]-1)))return null;if(!a){const{blockingStrategyBits:e,frameNumber:s,sampleNumber:i,samples:a,sampleRateBits:o,blockSizeBits:h,crc:c,length:l,...u}=r;t[ct](n,r,u)}return new Os(r)}constructor(e){super(e),this[I]=null,this[ue]=e[ue],this[pe]=e[pe],this[Me]=e[Me],this[Ee]=e[Ee],this[We]=null}}class Ns extends Lt{constructor(e,t,s){super(e,t),this.Frame=vs,this.Header=Os,s(this[v])}get[v](){return"flac"}*_getNextFrameSyncOffset(e){const t=yield*this._codecParser[et](2,0),s=t[j]-2;for(;e{const t=Os[gt](e,this._headerCache);if(t)return new vs(e,t,this._streamInfo);this._codecParser[rt]("Failed to parse Ogg FLAC frame","Skipping invalid FLAC frame")})).filter((e=>!!e))),e}}class $s{static*[ht](e,t,s){const i={};let r=yield*e[et](28,s);if(79!==r[0]||103!==r[1]||103!==r[2]||83!==r[3])return null;i[Ke]=r[4];if(248&r[5])return null;i[E]=!!(4&r[5]),i[D]=!!(2&r[5]),i[B]=!!(1&r[5]);const n=new Ct(bt.from(r[ne](0,28))[P]);i[g]=((e,t)=>{try{return e.getBigInt64(t,!0)}catch{const s=128&e.getUint8(t+7)?-1:1;let i=e.getUint32(t,!0),r=e.getUint32(t+4,!0);return-1===s&&(i=1+~i,r=1+~r),r>1048575&&console.warn("This platform does not support BigInt"),s*(i+r*2**32)}})(n,6),i[Je]=n.getInt32(14,!0),i[De]=n.getInt32(18,!0),i[Re]=n.getInt32(22,!0);const a=r[26];i[j]=a+27,r=yield*e[et](i[j],s),i[He]=0,i[Le]=[],i[Be]=bt.from(r[ne](27,i[j]));for(let e=0,t=0;e{const t=Ws[gt](this._identificationHeader,e,this._headerCache);if(t){null===this._preSkipRemaining&&(this._preSkipRemaining=t[Z]);let s=t[Oe]*t[xe]/1e3*t[Ge];return this._preSkipRemaining>0&&(this._preSkipRemaining-=s,s=this._preSkipRemaining<0?-this._preSkipRemaining:0),new Rs(e,t,s)}this._codecParser[nt]("Failed to parse Ogg Opus Header","Not a valid Ogg Opus file")}))),e}}class Ks extends Tt{constructor(e,t,s){super(t,e,s)}}const Qs={};for(let e=0;e<8;e++)Qs[e+6]=2**(6+e);class Xs extends jt{static[gt](e,t,s,i){if(e[j]<30)throw new Error("Out of data while inside an Ogg Page");const r=Ut(e[ne](0,30)),n=t[ht](r);if(n)return new Xs(n);const a={[j]:30};if("vorbis"!==r.substr(0,7))return null;a[U]=bt.from(e[ne](0,30));const o=new Ct(a[U][P]);if(a[ae]=o.getUint32(7,!0),0!==a[ae])return null;if(a[we]=e[11],a[ye]=h[a[we]-1]||"application defined",a[Ge]=o.getUint32(12,!0),a[y]=o.getInt32(16,!0),a[w]=o.getInt32(20,!0),a[S]=o.getInt32(24,!0),a[me]=Qs[(240&e[28])>>4],a[_e]=Qs[15&e[28]],a[_e]>a[me])return null;if(1!==e[29])return null;a[b]=32,a[ce]=i,a[he]=s;{const{length:e,data:s,version:i,vorbisSetup:n,vorbisComments:o,...h}=a;t[ct](r,a,h)}return new Xs(a)}constructor(e){super(e),this[y]=e[y],this[S]=e[S],this[w]=e[w],this[_e]=e[_e],this[me]=e[me],this[U]=e[U],this[he]=e[he],this[ce]=e[ce]}}class Ys extends Lt{constructor(e,t,s){super(e,t),this.Frame=Ks,s(this[v]),this._identificationHeader=null,this._setupComplete=!1,this._prevBlockSize=null}get[v](){return oe}[dt](e){e[F]=[];for(const t of Bt.get(e)[re])if(1===t[0])this._headerCache[mt](),this._identificationHeader=e[U],this._setupComplete=!1;else if(3===t[0])this._vorbisComments=t;else if(5===t[0])this._vorbisSetup=t,this._mode=this._parseSetupHeader(t),this._setupComplete=!0;else if(this._setupComplete){const s=Xs[gt](this._identificationHeader,this._headerCache,this._vorbisComments,this._vorbisSetup);s?e[F].push(new Ks(t,s,this._getSamples(t,s))):this._codecParser[logError]("Failed to parse Ogg Vorbis Header","Not a valid Ogg Vorbis file")}return e}_getSamples(e,t){const s=this._mode.blockFlags[e[0]>>1&this._mode.mask]?t[me]:t[_e],i=null===this._prevBlockSize?0:(this._prevBlockSize+s)/4;return this._prevBlockSize=s,i}_parseSetupHeader(e){const t=new $t(e),s={count:0,blockFlags:[]};for(;1&~t.read(1););let i;for(;s.count<64&&t.position>0;){Nt(t.read(8));let e=0;for(;0===t.read(8)&&e++<3;);if(4!==e){1+((126&Nt(i))>>1)!==s.count&&this._codecParser[rt]("vorbis derived mode count did not match actual mode count");break}i=t.read(7),s.blockFlags.unshift(1&i),t.position+=6,s.count++}return s.mask=(1<1&&e[De]>1&&this._codecParser[rt]("Unexpected gap in Ogg Page Sequence Number.",`Expected: ${this._pageSequenceNumber+1}, Got: ${e[De]}`),this._pageSequenceNumber=e[De]}_parsePage(e){null===this._isSupported&&(this._pageSequenceNumber=e[De],this._isSupported=this._checkCodecSupport(e)),this._checkPageSequenceNumber(e);const t=Bt.get(e),s=Rt.get(t[R]);let i=0;if(t[re]=s[Le].map((t=>e[U][ne](i,i+=t))),this._continuedPacket[j]&&(t[re][0]=Mt(this._continuedPacket,t[re][0]),this._continuedPacket=new bt),255===s[Be][s[Be][j]-1]&&(this._continuedPacket=Mt(this._continuedPacket,t[re].pop())),null!==this._previousAbsoluteGranulePosition&&(e[qe]=Number(e[g]-this._previousAbsoluteGranulePosition)),this._previousAbsoluteGranulePosition=e[g],this._isSupported){const t=this._parser[dt](e);return this._codecParser[it](t),t}return e}}class ei extends Lt{constructor(e,t,s){super(e,t),this._onCodec=s,this.Frame=As,this.Header=$s,this._streams=new Map,this._currentSerialNumber=null}get[v](){const e=this._streams.get(this._currentSerialNumber);return e?e.codec:""}*[ut](){const e=yield*this[ot](!0);this._currentSerialNumber=e[Je];let t=this._streams.get(this._currentSerialNumber);return t||(t=new Zs(this._codecParser,this._headerCache,this._onCodec),this._streams.set(this._currentSerialNumber,t)),e[E]&&this._streams.delete(this._currentSerialNumber),t._parsePage(e)}}const ti=()=>{};const si=g,ii=f,ri=b,ni=C,ai=y,oi=S,hi=w,ci=P,li=k,ui=v,di=F,pi=x,_i=H,mi=I,gi=M,fi=U,bi=O,Ci=N,yi=$,Si=A,wi=R,Pi=B,ki=L,vi=D,Fi=T,xi=E,Hi=G,Ii=z,Mi=q,Ui=V,Oi=j,Ni=W,$i=J,Ai=K,Ri=Q,Bi=X,Li=Y,Di=Z,Ti=ee,Ei=se,Gi=ie,zi=re,qi=ne,Vi=ae,ji=oe,Wi=he,Ji=ce,Ki=ue,Qi=pe,Xi=_e,Yi=me,Zi=be,er=Ce,tr=ye,sr=we,ir=ke,rr=ve,nr=Fe,ar=xe,or=He,hr=Me,cr=Ue,lr=Oe,ur=$e,dr=Re,pr=Le,_r=De,mr=Ee,gr=Ge,fr=qe,br=je,Cr=We,yr=Je,Sr=Ke,wr=Xe,Pr=Ye,kr=Ze;return e.CodecParser=class{constructor(e,{onCodec:t,onCodecHeader:s,onCodecUpdate:i,enableLogging:r=!1,enableFrameCRC32:n=!0}={}){this._inputMimeType=e,this._onCodec=t||ti,this._onCodecHeader=s||ti,this._onCodecUpdate=i,this._enableLogging=r,this._crc32=n?It:ti,this[_t]()}get[v](){return this._parser?this._parser[v]:""}[_t](){this._headerCache=new At(this._onCodecHeader,this._onCodecUpdate),this._generator=this._getGenerator(),this._generator.next()}*flush(){this._flushing=!0;for(let e=this._generator.next();e.value;e=this._generator.next())yield e.value;this._flushing=!1,this[_t]()}*parseChunk(e){for(let t=this._generator.next(e);t.value;t=this._generator.next())yield t.value}parseAll(e){return[...this.parseChunk(e),...this.flush()]}*_getGenerator(){if(this._inputMimeType.match(/aac/))this._parser=new ks(this,this._headerCache,this._onCodec);else if(this._inputMimeType.match(/mpeg/))this._parser=new ms(this,this._headerCache,this._onCodec);else if(this._inputMimeType.match(/flac/))this._parser=new Ns(this,this._headerCache,this._onCodec);else{if(!this._inputMimeType.match(/ogg/))throw new Error(`Unsupported Codec ${mimeType}`);this._parser=new ei(this,this._headerCache,this._onCodec)}for(this._frameNumber=0,this._currentReadPosition=0,this._totalBytesIn=0,this._totalBytesOut=0,this._totalSamples=0,this._sampleRate=void 0,this._rawData=new Uint8Array(0);;){const e=yield*this._parser[ut]();e&&(yield e)}}*[et](e=0,t=0){let s;for(;this._rawData[j]<=e+t;){if(s=yield,this._flushing)return this._rawData[ne](t);s&&(this._totalBytesIn+=s[j],this._rawData=Mt(this._rawData,s))}return this._rawData[ne](t)}[tt](e){this._currentReadPosition+=e,this._rawData=this._rawData[ne](e)}[st](e){this._sampleRate=e[R][Ge],e[R][C]=e[N]>0?8*Math.round(e[U][j]/e[N]):0,e[Me]=this._frameNumber++,e[Xe]=this._totalBytesOut,e[Ze]=this._totalSamples,e[Ye]=this._totalSamples/this._sampleRate*1e3,e[M]=this._crc32(e[U]),this._headerCache[pt](e[R][C],e[Ye]),this._totalBytesOut+=e[U][j],this._totalSamples+=e[qe]}[it](e){if(e[F]){if(e[E]){let t=e[qe];e[F].forEach((e=>{const s=e[qe];t0?t:0,e[N]=e[qe]/e[R][Ge]*1e3),t-=s,this[st](e)}))}else e[qe]=0,e[F].forEach((t=>{e[qe]+=t[qe],this[st](t)}));e[N]=e[qe]/this._sampleRate*1e3||0,e[Ze]=this._totalSamples,e[Ye]=this._totalSamples/this._sampleRate*1e3||0,e[Xe]=this._totalBytesOut}else this[st](e)}_log(e,t){if(this._enableLogging){const s=[`${v}: ${this[v]}`,`inputMimeType: ${this._inputMimeType}`,`readPosition: ${this._currentReadPosition}`,`totalBytesIn: ${this._totalBytesIn}`,`${Xe}: ${this._totalBytesOut}`],i=Math.max(...s.map((e=>e[j])));t.push(`--stats--${"-".repeat(i-9)}`,...s,"-".repeat(i)),e("codec-parser",t.reduce(((e,t)=>e+"\n "+t),""))}}[rt](...e){this._log(console.warn,e)}[nt](...e){this._log(console.error,e)}},e.absoluteGranulePosition=si,e.bandwidth=ii,e.bitDepth=ri,e.bitrate=ni,e.bitrateMaximum=ai,e.bitrateMinimum=oi,e.bitrateNominal=hi,e.blockSize=Qi,e.blockingStrategy=Ki,e.blocksize0=Xi,e.blocksize1=Yi,e.buffer=ci,e.bufferFullness=li,e.channelMappingFamily=Zi,e.channelMappingTable=er,e.channelMode=tr,e.channels=sr,e.codec=ui,e.codecFrames=di,e.copyrightId=ir,e.copyrightIdStart=rr,e.coupledStreamCount=pi,e.crc=_i,e.crc16=mi,e.crc32=gi,e.data=fi,e.description=bi,e.duration=Ci,e.emphasis=yi,e.frame=nr,e.frameCount=ar,e.frameLength=or,e.frameNumber=hr,e.framePadding=cr,e.frameSize=lr,e.hasOpusPadding=Si,e.header=wi,e.inputSampleRate=ur,e.isContinuedPacket=Pi,e.isCopyrighted=ki,e.isFirstPage=vi,e.isHome=Fi,e.isLastPage=xi,e.isOriginal=Hi,e.isPrivate=Ii,e.isVbr=Mi,e.layer=Ui,e.length=Oi,e.mode=Ni,e.modeExtension=$i,e.mpeg=Ai,e.mpegVersion=Ri,e.numberAACFrames=Bi,e.outputGain=Li,e.pageChecksum=dr,e.pageSegmentTable=pr,e.pageSequenceNumber=_r,e.preSkip=Di,e.profile=Ti,e.protection=Ei,e.rawData=Gi,e.sampleNumber=mr,e.sampleRate=gr,e.samples=fr,e.segments=zi,e.streamCount=br,e.streamInfo=Cr,e.streamSerialNumber=yr,e.streamStructureVersion=Sr,e.subarray=qi,e.totalBytesOut=wr,e.totalDuration=Pr,e.totalSamples=kr,e.version=Vi,e.vorbis=ji,e.vorbisComments=Wi,e.vorbisSetup=Ji,e}({}); diff --git a/libs/thirdparty/ogg-opus-decoder.min.js b/libs/thirdparty/ogg-opus-decoder.min.js new file mode 100644 index 0000000..9525201 Binary files /dev/null and b/libs/thirdparty/ogg-opus-decoder.min.js differ diff --git a/libs/thirdparty/ogg-vorbis-decoder.min.js b/libs/thirdparty/ogg-vorbis-decoder.min.js new file mode 100644 index 0000000..0f030ae --- /dev/null +++ b/libs/thirdparty/ogg-vorbis-decoder.min.js @@ -0,0 +1,195 @@ +/* Copyright 2021-2023 Ethan Halsall. This file is part of wasm-audio-decoders. https://github.com/eshaz/wasm-audio-decoders */ +var t,s;t=this,s=function(t,s){const i=(t,s=4294967295,i=79764919)=>{const e=new Int32Array(256);let r,n,h,o=s;for(r=0;r<256;r++){for(h=r<<24,n=8;n>0;--n)h=2147483648&h?h<<1^i:h<<1;e[r]=h}for(r=0;r>24^t[r])];return o},e=(t,s=i)=>{const e=t=>new Uint8Array(t.length/2).map(((s,i)=>parseInt(t.substring(2*i,2*(i+1)),16))),r=t=>e(t)[0],n=new Map;[,8364,,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,,381,,,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,,382,376].forEach(((t,s)=>n.set(t,s)));const h=new Uint8Array(t.length);let o,a,l,c=!1,u=0,U=42,d=t.length>13&&"dynEncode"===t.substring(0,9),f=0;d&&(f=11,a=r(t.substring(9,f)),a<=1&&(f+=2,U=r(t.substring(11,f))),1===a&&(f+=8,l=(t=>new DataView(e(t).buffer).getInt32(0,!0))(t.substring(13,f))));const p=256-U;for(let i=f;i255){const t=n.get(o);t&&(o=t+127)}c&&(c=!1,o-=64),h[u++]=o0?o+p:o-U}else c=!0;const M=h.subarray(0,u);if(d&&1===a){const t=s(M);if(t!==l){const s="Decode failed crc32 validation";throw console.error("`simple-yenc`\n",s+"\n","Expected: "+l+"; Got: "+t+"\n","Visit https://github.com/eshaz/simple-yenc for more information"),Error(s)}}return M};function r(){const t=Uint8Array,s=Float32Array;r.t||Object.defineProperties(r,{t:{value:new WeakMap},u:{value(t,s){r.t.set(t,Promise.resolve(s))}},U:{value(t,s){let i=r.t.get(t);return i||(s?i=WebAssembly.compile(e(s)):(s=t.M,i=r.Y(s).then((t=>WebAssembly.compile(t)))),r.t.set(t,i)),i}},T:{value(t,i){let e=new s(i),r=0,n=0;for(;r({O:t,channelData:s,samplesDecoded:i,sampleRate:e,bitDepth:r})},v:{value(t,s,i,e,n,h){let o,a,l=[];for(o=0;o{const e=String.raw`dynEncode0114db91da9bu‡*t“““t“““““t““““$#“U¤¤“U¤¤3yƒ†„‰zzss|yu„svu‡yÚ&ˆ“4<054<,5T44^T44<(6U~J(44< ~A544U~6J0444ˆ†545 444J0444‰J,4U“4ˆU“‡…Ò“7U45“4U4Z“4U4U^/6545T4T44BUˆ~64CU~O4U54U~5 U5T4B4Z!4U~5U5U5T4U~6U4ZTU5U5T44~4O4U2ZTU5T44Z!4B6T44Uˆ~64B6U~O44Uˆ~4O4U~54U~5 44~C4~54U~5 44~5454Uˆ4B6Ub!444~UO4U~5 “U5“4U4ZTUŠ#44U$4†64<4~B6^“4<444~Uˆ~B4U~54Uˆ544~544~Uˆ5 µ“Uä#UJUè#5TT4U0ZTTUX5U5T4T4Uà#~4OU4U $~Cˆ4~54U~5 T44$6U\!TTT4UaT4<6T4<64<Z!44~4N4<U~5 4U”Z!4U±_TUŠ#44U•Uˆ6UÔ~B$544$6U\!4Uˆ6U¤#~B44Uä#~B$~64<6_TU‰#444U”~B~6~54<Y!44<_!T4Y!4<64~444~AN44<U~6J4U5 44J4U”[!U#44UŠO4U~54U~5 U54 “7U6844J44J 4UJ4UJ04VK(44<J44<J$4U´~54U~5 4U¤~5!TTT4U$5"U“5TTTTTTT4U$"4VK,U54<(6U~64<$6_!4< 64~6A54A544U~6#J(U’54A4U‡[!44J(44#~A4Uˆ6U“‡UŠU…[!44†64~64_!4<64~54<6T4<4]TU5 T4Y!44~44~AN4U~54U~54U5 44J(44J UÄA!U5U”#UôJU"UÔJUœ#UÔ"JU˜#U´"JT4U´ZTU5T4UôZTU5T4UDZTU5T4U$[T44~UO4U~5 UÔUô4U~U´$.U5T4UP[T4U~4~UO4U~5 U˜#<Uœ#<4U~U2$.UÄUN 44 ~UO4U~5 44!~UO4U~5 4U~4~UO4U~5 44J44J(U5 44U¤~J@44Uä~J<44UD~J844U~J44U$54U$5U‘54U$54U1^4U1^†!4U•~54U~5U”54U~6U4U^/65T4T4U$54U~4BUˆ~4O4U54U~5 UU'464U'_/54UˆU”~5T4T4U~4BUˆ~UO4U54U~5 U54Uä~4U¤~4U~U'$!44~5U5T44\T44U<~$6U\!4U#aT4U~4Uˆ~4O4U~5 U5U5U5TTT4U$"4YTU5 4Uˆ4~C5U5 U5U5444$4~64~\TUŽ5 4U~4Uˆ~5T4Y!44O4U~54U~54U5 4CYTU‹5 4Uä~4U¤~4U~4$6TU54U\!44Bæ4Bä~[!4U~4UD~4U~4Uˆ~4$6TUŒ54U\!44B†4B„~[!44U<~4U4~$5 4U"U˜#$544"†Y!454U^!44<J44<(J454U~84­U”N!#%'+/37?GOWgw‡—·×÷Uä;U”9$%& !"#`;r.U(r,e).then((t=>WebAssembly.instantiate(t,{}))).then((({exports:e})=>{const r=new Map(Object.entries(e)),n=r.get("puff"),h=r.get("memory").buffer,o=new t(h),a=new DataView(h);let l=r.get("__heap_base");const c=s.length,u=l;l+=4,a.setInt32(u,c,!0);const U=l;l+=c,o.set(s,U);const d=l;l+=4,a.setInt32(d,o.byteLength-l,!0),n(l,d,U,u),i(o.slice(l,l+a.getInt32(d,!0)))}))}))}}}),Object.defineProperty(this,"M",{enumerable:!0,get:()=>this.B}),this.F=(t,s,i)=>{let e=[],r=0;for(;r{const e=this.B.V(s.BYTES_PER_ELEMENT*t);return i&&this.S.add(e),{H:e,$:t,C:new s(this.B.I,e,t)}},this.free=()=>{this.S.forEach((t=>{this.B.free(t)})),this.S.clear()},this.J=t=>{const s=[],i=new Uint8Array(this.B.I);for(let e=i[t];0!==e;e=i[++t])s.push(e);return String.fromCharCode.apply(null,s)},this.P=(t,s,i,e,r,n)=>{t.push({message:s,frameLength:i,frameNumber:e,inputBytes:r,outputSamples:n})},this.instantiate=(t,s)=>(s&&r.u(t,s),this.B=new t(r).instantiate(),this.S=new Set,this.B.ready.then((()=>this)))}class n extends((()=>globalThis.Worker||s)()){constructor(t,s,i,e){r.t||new r;let n=r.t.get(i);if(!n){let t,s="text/javascript",h=`'use strict';(${""+((t,s,i)=>{let e,r,n=new Promise((t=>{r=t}));self.onmessage=({data:{id:h,command:o,data:a}})=>{let l,c=n,u={id:h};"init"===o?(Object.defineProperties(t,{D:{value:s},N:{value:i},module:{value:a.module},Z:{value:!0}}),e=new t(a.options),r()):"free"===o?e.free():"ready"===o?c=c.then((()=>e.ready)):"reset"===o?c=c.then((()=>e.reset())):(Object.assign(u,e[o](Array.isArray(a)?a.map((t=>new Uint8Array(t))):new Uint8Array(a))),l=u.channelData?u.channelData.map((t=>t.buffer)):[]),c.then((()=>self.postMessage(u,l)))}})})(${i}, ${r}, ${e})`;try{t=void 0!==process.versions.node}catch{}n=t?`data:${s};base64,${Buffer.from(h).toString("base64")}`:URL.createObjectURL(new Blob([h],{type:s})),r.t.set(i,n)}super(n,{name:s}),this.K=Number.MIN_SAFE_INTEGER,this.R=new Map,this.onmessage=({data:t})=>{const{id:s,...i}=t;this.R.get(s)(i),this.R.delete(s)},new e(r).U().then((s=>{this.G("init",{module:s,options:t})}))}async G(t,s){return new Promise((i=>{this.postMessage({command:t,id:this.K,data:s}),this.R.set(this.K++,i)}))}get ready(){return this.G("ready")}async free(){await this.G("free").finally((()=>{this.terminate()}))}async reset(){await this.G("reset")}}const h=(t,s)=>{Object.defineProperty(t,"name",{value:s})},o=Symbol,a=(()=>{const t="left",s="center",i="right";return["","front ","side ","rear "].map((e=>[[t,i],[t,i,s],[t,s,i],[s,t,i],[s]].flatMap((t=>t.map((t=>e+t)).join(", ")))))})(),l="LFE",c="monophonic (mono)",u="stereo",U="surround",d=(t,...s)=>`${[c,u,"linear "+U,"quadraphonic","5.0 "+U,"5.1 "+U,"6.1 "+U,"7.1 "+U][t-1]} (${s.join(", ")})`,f=[c,d(2,a[0][0]),d(3,a[0][2]),d(4,a[1][0],a[3][0]),d(5,a[1][2],a[3][0]),d(6,a[1][2],a[3][0],l),d(7,a[1][2],a[2][0],a[3][4],l),d(8,a[1][2],a[2][0],a[3][0],l)],p=48e3,M=44100,y=32e3,m=24e3,w=22050,g=16e3,Y=8e3,T="absoluteGranulePosition",b="bandwidth",A="bitDepth",O="bitrate",v=O+"Maximum",B=O+"Minimum",F=O+"Nominal",_="buffer",V=_+"Fullness",k="codec",S=k+"Frames",q="coupledStreamCount",H="crc",$=H+"16",C=H+"32",I="data",j="description",J="duration",x="emphasis",P="hasOpusPadding",D="header",E="isContinuedPacket",N="isCopyrighted",Z="isFirstPage",z="isHome",K="isLastPage",Q="isOriginal",R="isPrivate",G="isVbr",X="layer",L="length",W="mode",tt=W+"Extension",st="mpeg",it=st+"Version",et="numberAACFrames",rt="outputGain",nt="preSkip",ht="profile",ot=o(),at="protection",lt="segments",ct="subarray",ut="version",Ut="vorbis",dt=Ut+"Comments",ft=Ut+"Setup",pt="block",Mt=pt+"ingStrategy",yt=o(),mt=pt+"Size",wt=pt+"size0",gt=pt+"size1",Yt=o(),Tt="channel",bt=Tt+"MappingFamily",At=Tt+"MappingTable",Ot=Tt+"Mode",vt=o(),Bt=Tt+"s",Ft="copyright",_t=Ft+"Id",Vt=Ft+"IdStart",kt="frame",St=kt+"Count",qt=kt+"Length",Ht="Number",$t=kt+Ht,Ct=kt+"Padding",It=kt+"Size",jt="Rate",Jt="inputSample"+jt,xt="page",Pt=xt+"Checksum",Dt=o(),Et=xt+"SegmentTable",Nt=xt+"Sequence"+Ht,Zt="sample",zt=Zt+Ht,Kt=Zt+jt,Qt=o(),Rt=Zt+"s",Gt="stream",Xt=Gt+"Count",Lt=Gt+"Info",Wt=Gt+"Serial"+Ht,ts=Gt+"StructureVersion",ss="total",is=ss+"BytesOut",es=ss+"Duration",rs=ss+"Samples",ns=o(),hs=o(),os=o(),as=o(),ls=o(),cs=o(),us=o(),Us=o(),ds=o(),fs=o(),ps=o(),Ms=o(),ys=o(),ms=o(),ws=o(),gs=o(),Ys=o(),Ts=o(),bs=Uint8Array,As=DataView,Os="reserved",vs="bad",Bs="free",Fs="none",_s="16bit CRC",Vs=(t,s,i)=>{for(let e=0;e0;t--)r=i(r);t[e]=r}return t},ks=Vs(new bs(256),(t=>t),(t=>128&t?7^t<<1:t<<1)),Ss=[Vs(new Uint16Array(256),(t=>t<<8),(t=>t<<1^(32768&t?32773:0)))],qs=[Vs(new Uint32Array(256),(t=>t),(t=>t>>>1^3988292384*(1&t)))];for(let Ye=0;Ye<15;Ye++){Ss.push(new Uint16Array(256)),qs.push(new Uint32Array(256));for(let t=0;t<=255;t++)Ss[Ye+1][t]=Ss[0][Ss[Ye][t]>>>8]^Ss[Ye][t]<<8,qs[Ye+1][t]=qs[Ye][t]>>>8^qs[0][255&qs[Ye][t]]}const Hs=t=>{const s=t[L],i=s-16;let e=0,r=0;for(;r<=i;)e=qs[15][255&(t[r++]^e)]^qs[14][255&(t[r++]^e>>>8)]^qs[13][255&(t[r++]^e>>>16)]^qs[12][t[r++]^e>>>24]^qs[11][t[r++]]^qs[10][t[r++]]^qs[9][t[r++]]^qs[8][t[r++]]^qs[7][t[r++]]^qs[6][t[r++]]^qs[5][t[r++]]^qs[4][t[r++]]^qs[3][t[r++]]^qs[2][t[r++]]^qs[1][t[r++]]^qs[0][t[r++]];for(;r!==s;)e=qs[0][255&(e^t[r++])]^e>>>8;return~e},$s=(...t)=>{const s=new bs(t.reduce(((t,s)=>t+s[L]),0));return t.reduce(((t,i)=>(s.set(i,t),t+i[L])),0),s},Cs=t=>String.fromCharCode(...t),Is=[0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15],js=t=>Is[15&t]<<4|Is[t>>4];class Js{constructor(t){this.X=t,this.L=8*t[L]}set position(t){this.L=t}get position(){return this.L}read(t){const s=Math.floor(this.L/8),i=this.L%8;return this.L-=t,(js(this.X[s-1])<<8)+js(this.X[s])>>7-i&255}}class xs{constructor(t,s){this.W=t,this.tt=s,this[ws]()}[gs](){this.st=!0}[ws](){this.it=new Map,this.et=new WeakMap,this.rt=!1,this.nt=!1,this.ht=null,this.st=!1}[ms](t,s){if(this.tt){this.ht!==t&&(this.ht=t,this.nt=!0);const i=this.et.get(this.it.get(this.ot));this.nt&&i&&this.tt({lt:t,...i},s),this.nt=!1}}[ds](t){const s=this.it.get(t);return s&&this.ct(t),s}[fs](t,s,i){this.st&&(this.rt||(this.W({...s}),this.rt=!0),this.ct(t),this.it.set(t,s),this.et.set(s,i))}ct(t){this.tt&&t!==this.ot&&(this.nt=!0,this.ot=t)}}const Ps=new WeakMap,Ds=new WeakMap;class Es{constructor(t,s){this.ut=t,this.it=s}*[us](){let t;for(;;){if(t=yield*this.Ut[ps](this.ut,this.it,0),t)return t;this.ut[hs](1)}}*[Us](t){let s=yield*this[us]();const i=Ds.get(s)[L];if(t||this.ut.dt||(yield*this.ft[ds](this.ut,this.it,i)))return this.it[gs](),this.ut[hs](i),this.ut[as](s),s;this.ut[ls](`Missing ${kt} at ${i} bytes from current position.`,`Dropping current ${kt} and trying again.`),this.it[ws](),this.ut[hs](1)}}class Ns{constructor(t,s){Ds.set(this,{[D]:t}),this[I]=s}}class Zs extends Ns{static*[ps](t,s,i,e,r){const n=yield*t[ds](i,e,r);if(n){const t=Ps.get(n)[qt],e=Ps.get(n)[Rt];return new s(n,(yield*i[ns](t,r))[ct](0,t),e)}return null}constructor(t,s,i){super(t,s),this[D]=t,this[Rt]=i,this[J]=i/t[Kt]*1e3,this[$t]=null,this[is]=null,this[rs]=null,this[es]=null,Ds.get(this)[L]=s[L]}}const zs="unsynchronizationFlag",Ks="extendedHeaderFlag",Qs="experimentalFlag",Rs="footerPresent";class Gs{static*Mt(t,s,i){const e={};let r=yield*t[ns](3,i);if(73!==r[0]||68!==r[1]||51!==r[2])return null;if(r=yield*t[ns](10,i),e[ut]=`id3v2.${r[3]}.${r[4]}`,15&r[5])return null;if(e[zs]=!!(128&r[5]),e[Ks]=!!(64&r[5]),e[Qs]=!!(32&r[5]),e[Rs]=!!(16&r[5]),128&r[6]||128&r[7]||128&r[8]||128&r[9])return null;const n=r[6]<<21|r[7]<<14|r[8]<<7|r[9];return e[L]=10+n,new Gs(e)}constructor(t){this[ut]=t[ut],this[zs]=t[zs],this[Ks]=t[Ks],this[Qs]=t[Qs],this[Rs]=t[Rs],this[L]=t[L]}}class Xs{constructor(t){Ps.set(this,t),this[A]=t[A],this[O]=null,this[Bt]=t[Bt],this[Ot]=t[Ot],this[Kt]=t[Kt]}}const Ls={0:[Bs,Bs,Bs,Bs,Bs],16:[32,32,32,32,8],240:[vs,vs,vs,vs,vs]},Ws=(t,s,i)=>8*((t+i)%s+s)*(1<<(t+i)/s)-8*s*(s/8|0);for(let Ye=2;Ye<15;Ye++)Ls[Ye<<4]=[32*Ye,Ws(Ye,4,0),Ws(Ye,4,-1),Ws(Ye,8,4),Ws(Ye,8,0)];const ti="bands ",si=" to 31",ii={0:ti+4+si,16:ti+8+si,32:ti+12+si,48:ti+16+si},ei="bitrateIndex",ri="v2",ni="v1",hi="Intensity stereo ",oi=", MS stereo ",ai="on",li="off",ci={0:hi+li+oi+li,16:hi+ai+oi+li,32:hi+li+oi+ai,48:hi+ai+oi+ai},ui={0:{[j]:Os},2:{[j]:"Layer III",[Ct]:1,[tt]:ci,[ni]:{[ei]:2,[Rt]:1152},[ri]:{[ei]:4,[Rt]:576}},4:{[j]:"Layer II",[Ct]:1,[tt]:ii,[Rt]:1152,[ni]:{[ei]:1},[ri]:{[ei]:4}},6:{[j]:"Layer I",[Ct]:4,[tt]:ii,[Rt]:384,[ni]:{[ei]:0},[ri]:{[ei]:3}}},Ui="MPEG Version ",di="ISO/IEC ",fi={0:{[j]:Ui+"2.5 (later extension of MPEG 2)",[X]:ri,[Kt]:{0:11025,4:12e3,8:Y,12:Os}},8:{[j]:Os},16:{[j]:`${Ui}2 (${di}13818-3)`,[X]:ri,[Kt]:{0:w,4:m,8:g,12:Os}},24:{[j]:`${Ui}1 (${di}11172-3)`,[X]:ni,[Kt]:{0:M,4:p,8:y,12:Os}},length:L},pi={0:_s,1:Fs},Mi={0:Fs,1:"50/15 ms",2:Os,3:"CCIT J.17"},yi={0:{[Bt]:2,[j]:u},64:{[Bt]:2,[j]:"joint "+u},128:{[Bt]:2,[j]:"dual channel"},192:{[Bt]:1,[j]:c}};class mi extends Xs{static*[ds](t,s,i){const e={},r=yield*Gs.Mt(t,s,i);r&&(yield*t[ns](r[L],i),t[hs](r[L]));const n=yield*t[ns](4,i),h=Cs(n[ct](0,4)),o=s[ds](h);if(o)return new mi(o);if(255!==n[0]||n[1]<224)return null;const a=fi[24&n[1]];if(a[j]===Os)return null;const l=6&n[1];if(ui[l][j]===Os)return null;const c={...ui[l],...ui[l][a[X]]};if(e[it]=a[j],e[X]=c[j],e[Rt]=c[Rt],e[at]=pi[1&n[1]],e[L]=4,e[O]=Ls[240&n[2]][c[ei]],e[O]===vs)return null;if(e[Kt]=a[Kt][12&n[2]],e[Kt]===Os)return null;if(e[Ct]=2&n[2]&&c[Ct],e[R]=!!(1&n[2]),e[qt]=Math.floor(125*e[O]*e[Rt]/e[Kt]+e[Ct]),!e[qt])return null;const u=192&n[3];if(e[Ot]=yi[u][j],e[Bt]=yi[u][Bt],e[tt]=c[tt][48&n[3]],e[N]=!!(8&n[3]),e[Q]=!!(4&n[3]),e[x]=Mi[3&n[3]],e[x]===Os)return null;e[A]=16;{const{length:t,frameLength:i,yt:r,...n}=e;s[fs](h,e,n)}return new mi(e)}constructor(t){super(t),this[O]=t[O],this[x]=t[x],this[Ct]=t[Ct],this[N]=t[N],this[Q]=t[Q],this[R]=t[R],this[X]=t[X],this[tt]=t[tt],this[it]=t[it],this[at]=t[at]}}class wi extends Zs{static*[ps](t,s,i){return yield*super[ps](mi,wi,t,s,i)}constructor(t,s,i){super(t,s,i)}}class gi extends Es{constructor(t,s,i){super(t,s),this.Ut=wi,this.ft=mi,i(this[k])}get[k](){return st}*[Ms](){return yield*this[Us]()}}const Yi={0:"MPEG-4",8:"MPEG-2"},Ti={0:"valid",2:vs,4:vs,6:vs},bi={0:_s,1:Fs},Ai={0:"AAC Main",64:"AAC LC (Low Complexity)",128:"AAC SSR (Scalable Sample Rate)",192:"AAC LTP (Long Term Prediction)"},Oi={0:96e3,4:88200,8:64e3,12:p,16:M,20:y,24:m,28:w,32:g,36:12e3,40:11025,44:Y,48:7350,52:Os,56:Os,60:"frequency is written explicitly"},vi={0:{[Bt]:0,[j]:"Defined in AOT Specific Config"},64:{[Bt]:1,[j]:c},128:{[Bt]:2,[j]:d(2,a[0][0])},192:{[Bt]:3,[j]:d(3,a[1][3])},256:{[Bt]:4,[j]:d(4,a[1][3],a[3][4])},320:{[Bt]:5,[j]:d(5,a[1][3],a[3][0])},384:{[Bt]:6,[j]:d(6,a[1][3],a[3][0],l)},448:{[Bt]:8,[j]:d(8,a[1][3],a[2][0],a[3][0],l)}};class Bi extends Xs{static*[ds](t,s,i){const e={},r=yield*t[ns](7,i),n=Cs([r[0],r[1],r[2],252&r[3]|3&r[6]]),h=s[ds](n);if(h)Object.assign(e,h);else{if(255!==r[0]||r[1]<240)return null;if(e[it]=Yi[8&r[1]],e[X]=Ti[6&r[1]],e[X]===vs)return null;const t=1&r[1];e[at]=bi[t],e[L]=t?7:9,e[ot]=192&r[2],e[Qt]=60&r[2];const i=2&r[2];if(e[ht]=Ai[e[ot]],e[Kt]=Oi[e[Qt]],e[Kt]===Os)return null;e[R]=!!i,e[vt]=448&(r[2]<<8|r[3]),e[Ot]=vi[e[vt]][j],e[Bt]=vi[e[vt]][Bt],e[Q]=!!(32&r[3]),e[z]=!!(8&r[3]),e[_t]=!!(8&r[3]),e[Vt]=!!(4&r[3]),e[A]=16,e[Rt]=1024,e[et]=3&r[6];{const{length:t,wt:i,gt:r,Yt:h,frameLength:o,yt:a,Tt:l,...c}=e;s[fs](n,e,c)}}if(e[qt]=8191&(r[3]<<11|r[4]<<3|r[5]>>5),!e[qt])return null;const o=2047&(r[5]<<6|r[6]>>2);return e[V]=2047===o?"VBR":o,new Bi(e)}constructor(t){super(t),this[_t]=t[_t],this[Vt]=t[Vt],this[V]=t[V],this[z]=t[z],this[Q]=t[Q],this[R]=t[R],this[X]=t[X],this[L]=t[L],this[it]=t[it],this[et]=t[et],this[ht]=t[ht],this[at]=t[at]}get bt(){const t=Ps.get(this),s=t[ot]+64<<5|t[Qt]<<5|t[vt]>>3,i=new bs(2);return new As(i[_]).setUint16(0,s,!1),i}}class Fi extends Zs{static*[ps](t,s,i){return yield*super[ps](Bi,Fi,t,s,i)}constructor(t,s,i){super(t,s,i)}}class _i extends Es{constructor(t,s,i){super(t,s),this.Ut=Fi,this.ft=Bi,i(this[k])}get[k](){return"aac"}*[Ms](){return yield*this[Us]()}}class Vi extends Zs{static At(t){return(t[t[L]-2]<<8)+t[t[L]-1]}static[Ts](t){const s=Vi.At(t),i=(t=>{const s=t[L],i=s-16;let e=0,r=0;for(;r<=i;)e^=t[r++]<<8|t[r++],e=Ss[15][e>>8]^Ss[14][255&e]^Ss[13][t[r++]]^Ss[12][t[r++]]^Ss[11][t[r++]]^Ss[10][t[r++]]^Ss[9][t[r++]]^Ss[8][t[r++]]^Ss[7][t[r++]]^Ss[6][t[r++]]^Ss[5][t[r++]]^Ss[4][t[r++]]^Ss[3][t[r++]]^Ss[2][t[r++]]^Ss[1][t[r++]]^Ss[0][t[r++]];for(;r!==s;)e=(255&e)<<8^Ss[0][e>>8^t[r++]];return e})(t[ct](0,-2));return s===i}constructor(t,s,i){s[Lt]=i,s[$]=Vi.At(t),super(s,t,Ps.get(s)[Rt])}}const ki="get from STREAMINFO metadata block",Si={0:"Fixed",1:"Variable"},qi={0:Os,16:192};for(let Ye=2;Ye<16;Ye++)qi[Ye<<4]=Ye<6?576*2**(Ye-2):2**Ye;const Hi={0:ki,1:88200,2:176400,3:192e3,4:Y,5:g,6:w,7:m,8:y,9:M,10:p,11:96e3,15:vs},$i={0:{[Bt]:1,[j]:c},16:{[Bt]:2,[j]:d(2,a[0][0])},32:{[Bt]:3,[j]:d(3,a[0][1])},48:{[Bt]:4,[j]:d(4,a[1][0],a[3][0])},64:{[Bt]:5,[j]:d(5,a[1][1],a[3][0])},80:{[Bt]:6,[j]:d(6,a[1][1],l,a[3][0])},96:{[Bt]:7,[j]:d(7,a[1][1],l,a[3][4],a[2][0])},112:{[Bt]:8,[j]:d(8,a[1][1],l,a[3][0],a[2][0])},128:{[Bt]:2,[j]:u+" (left, diff)"},144:{[Bt]:2,[j]:u+" (diff, right)"},160:{[Bt]:2,[j]:u+" (avg, diff)"},176:Os,192:Os,208:Os,224:Os,240:Os},Ci={0:ki,2:8,4:12,6:Os,8:16,10:20,12:24,14:Os};class Ii extends Xs{static Ot(t){if(t[0]>254)return null;if(t[0]<128)return{value:t[0],length:1};let s=1;for(let n=64;n&t[0];n>>=1)s++;let i=s-1,e=0,r=0;for(;i>0;r+=6,i--){if(128!=(192&t[i]))return null;e|=(63&t[i])<>s)<{let s=0;const i=t[L];for(let e=0;e!==i;e++)s=ks[s^t[e]];return s})(e[ct](0,r[L]-1)))return null;if(!h){const{vt:t,frameNumber:i,Bt:e,yt:h,Yt:o,Ft:a,_t:l,length:c,...u}=r;s[fs](n,r,u)}return new Ii(r)}constructor(t){super(t),this[$]=null,this[Mt]=t[Mt],this[mt]=t[mt],this[$t]=t[$t],this[zt]=t[zt],this[Lt]=null}}class ji extends Es{constructor(t,s,i){super(t,s),this.Ut=Vi,this.ft=Ii,i(this[k])}get[k](){return"flac"}*Vt(t){const s=yield*this.ut[ns](2,0),i=s[L]-2;for(;t{const s=Ii[Ys](t,this.it);if(s)return new Vi(t,s,this.kt);this.ut[ls]("Failed to parse Ogg FLAC frame","Skipping invalid FLAC frame")})).filter((t=>!!t))),t}}class Ji{static*[ds](t,s,i){const e={};let r=yield*t[ns](28,i);if(79!==r[0]||103!==r[1]||103!==r[2]||83!==r[3])return null;if(e[ts]=r[4],248&r[5])return null;e[K]=!!(4&r[5]),e[Z]=!!(2&r[5]),e[E]=!!(1&r[5]);const n=new As(bs.from(r[ct](0,28))[_]);e[T]=(t=>{try{return t.getBigInt64(6,!0)}catch{const s=128&t.getUint8(13)?-1:1;let i=t.getUint32(6,!0),e=t.getUint32(10,!0);return-1===s&&(i=1+~i,e=1+~e),e>1048575&&console.warn("This platform does not support BigInt"),s*(i+e*2**32)}})(n),e[Wt]=n.getInt32(14,!0),e[Nt]=n.getInt32(18,!0),e[Pt]=n.getInt32(22,!0);const h=r[26];e[L]=h+27,r=yield*t[ns](e[L],i),e[qt]=0,e[Et]=[],e[Dt]=bs.from(r[ct](27,e[L]));for(let o=0,a=0;o{const s=Li[Ys](this.Ht,t,this.it);if(s){null===this.$t&&(this.$t=s[nt]);let i=s[It]*s[St]/1e3*s[Kt];return this.$t>0&&(this.$t-=i,i=this.$t<0?-this.$t:0),new Pi(t,s,i)}this.ut[cs]("Failed to parse Ogg Opus Header","Not a valid Ogg Opus file")}))),t}}class te extends Zs{constructor(t,s,i){super(s,t,i)}}const se={};for(let Ye=0;Ye<8;Ye++)se[Ye+6]=2**(6+Ye);class ie extends Xs{static[Ys](t,s,i,e){if(t[L]<30)throw Error("Out of data while inside an Ogg Page");const r=Cs(t[ct](0,30)),n=s[ds](r);if(n)return new ie(n);const h={[L]:30};if("vorbis"!==r.substr(0,7))return null;h[I]=bs.from(t[ct](0,30));const o=new As(h[I][_]);if(h[ut]=o.getUint32(7,!0),0!==h[ut])return null;if(h[Bt]=t[11],h[Ot]=f[h[Bt]-1]||"application defined",h[Kt]=o.getUint32(12,!0),h[v]=o.getInt32(16,!0),h[F]=o.getInt32(20,!0),h[B]=o.getInt32(24,!0),h[gt]=se[(240&t[28])>>4],h[wt]=se[15&t[28]],h[wt]>h[gt])return null;if(1!==t[29])return null;h[A]=32,h[ft]=e,h[dt]=i;{const{length:t,data:i,version:e,Ct:n,It:o,...a}=h;s[fs](r,h,a)}return new ie(h)}constructor(t){super(t),this[v]=t[v],this[B]=t[B],this[F]=t[F],this[wt]=t[wt],this[gt]=t[gt],this[I]=t[I],this[dt]=t[dt],this[ft]=t[ft]}}class ee extends Es{constructor(t,s,i){super(t,s),this.Ut=te,i(this[k]),this.Ht=null,this.jt=!1,this.Jt=null}get[k](){return Ut}[ys](t){t[S]=[];for(const s of Ds.get(t)[lt])if(1===s[0])this.it[gs](),this.Ht=t[I],this.jt=!1;else if(3===s[0])this.xt=s;else if(5===s[0])this.Pt=s,this.Dt=this.Et(s),this.jt=!0;else if(this.jt){const i=ie[Ys](this.Ht,this.it,this.xt,this.Pt);i?t[S].push(new te(s,i,this.Nt(s,i))):this.ut[logError]("Failed to parse Ogg Vorbis Header","Not a valid Ogg Vorbis file")}return t}Nt(t,s){const i=this.Dt.Zt[t[0]>>1&this.Dt.mask]?s[gt]:s[wt],e=null===this.Jt?0:(this.Jt+i)/4;return this.Jt=i,e}Et(t){const s=new Js(t),i={count:0,Zt:[]};for(;1&~s.read(1););let e;for(;i.count<64&&s.position>0;){js(s.read(8));let t=0;for(;0===s.read(8)&&t++<3;);if(4!==t){1+((126&js(e))>>1)!==i.count&&this.ut[ls]("vorbis derived mode count did not match actual mode count");break}e=s.read(7),i.Zt.unshift(1&e),s.position+=6,i.count++}return i.mask=(1<1&&t[Nt]>1&&this.ut[ls]("Unexpected gap in Ogg Page Sequence Number.",`Expected: ${this.ss+1}, Got: ${t[Nt]}`),this.ss=t[Nt]}es(t){null===this.Rt&&(this.ss=t[Nt],this.Rt=this.Wt(t)),this.ts(t);const s=Ds.get(t),i=Ps.get(s[D]);let e=0;if(s[lt]=i[Et].map((s=>t[I][ct](e,e+=s))),this.Kt[L]&&(s[lt][0]=$s(this.Kt,s[lt][0]),this.Kt=new bs),255===i[Dt][i[Dt][L]-1]&&(this.Kt=$s(this.Kt,s[lt].pop())),null!==this.Gt&&(t[Rt]=Number(t[T]-this.Gt)),this.Gt=t[T],this.Rt){const s=this.Lt[ys](t);return this.ut[as](s),s}return t}}class ne extends Es{constructor(t,s,i){super(t,s),this.zt=i,this.Ut=xi,this.ft=Ji,this.rs=new Map,this.ns=null}get[k](){const t=this.rs.get(this.ns);return t?t.hs:""}*[Ms](){const t=yield*this[Us](!0);this.ns=t[Wt];let s=this.rs.get(this.ns);return s||(s=new re(this.ut,this.it,this.zt),this.rs.set(this.ns,s)),t[K]&&this.rs.delete(this.ns),s.es(t)}}const he=()=>{};class oe{constructor(t,{os:s,ls:i,cs:e,us:r=!1,Us:n=!0}={}){this.ds=t,this.zt=s||he,this.W=i||he,this.tt=e,this.fs=r,this.ps=n?Hs:he,this[ws]()}get[k](){return this.Lt?this.Lt[k]:""}[ws](){this.it=new xs(this.W,this.tt),this.Ms=this.ys(),this.Ms.next()}*flush(){this.dt=!0;for(let t=this.Ms.next();t.value;t=this.Ms.next())yield t.value;this.dt=!1,this[ws]()}*ws(t){for(let s=this.Ms.next(t);s.value;s=this.Ms.next())yield s.value}parseAll(t){return[...this.ws(t),...this.flush()]}*ys(){if(this.ds.match(/aac/))this.Lt=new _i(this,this.it,this.zt);else if(this.ds.match(/mpeg/))this.Lt=new gi(this,this.it,this.zt);else if(this.ds.match(/flac/))this.Lt=new ji(this,this.it,this.zt);else{if(!this.ds.match(/ogg/))throw Error("Unsupported Codec "+mimeType);this.Lt=new ne(this,this.it,this.zt)}for(this.gs=0,this.Ys=0,this.Ts=0,this.bs=0,this.As=0,this.Os=void 0,this.vs=new Uint8Array(0);;){const t=yield*this.Lt[Ms]();t&&(yield t)}}*[ns](t=0,s=0){let i;for(;this.vs[L]<=t+s;){if(i=yield,this.dt)return this.vs[ct](s);i&&(this.Ts+=i[L],this.vs=$s(this.vs,i))}return this.vs[ct](s)}[hs](t){this.Ys+=t,this.vs=this.vs[ct](t)}[os](t){this.Os=t[D][Kt],t[D][O]=t[J]>0?8*Math.round(t[I][L]/t[J]):0,t[$t]=this.gs++,t[is]=this.bs,t[rs]=this.As,t[es]=this.As/this.Os*1e3,t[C]=this.ps(t[I]),this.it[ms](t[D][O],t[es]),this.bs+=t[I][L],this.As+=t[Rt]}[as](t){if(t[S]){if(t[K]){let s=t[Rt];t[S].forEach((t=>{const i=t[Rt];s0?s:0,t[J]=t[Rt]/t[D][Kt]*1e3),s-=i,this[os](t)}))}else t[Rt]=0,t[S].forEach((s=>{t[Rt]+=s[Rt],this[os](s)}));t[J]=t[Rt]/this.Os*1e3||0,t[rs]=this.As,t[es]=this.As/this.Os*1e3||0,t[is]=this.bs}else this[os](t)}Bs(t,s){if(this.fs){const i=[`${k}: ${this[k]}`,"inputMimeType: "+this.ds,"readPosition: "+this.Ys,"totalBytesIn: "+this.Ts,`${is}: ${this.bs}`],e=Math.max(...i.map((t=>t[L])));s.push("--stats--"+"-".repeat(e-9),...i,"-".repeat(e)),t("codec-parser",s.reduce(((t,s)=>t+"\n "+s),""))}}[ls](...t){this.Bs(console.warn,t)}[cs](...t){this.Bs(console.error,t)}}const ae=S,le=I,ce=D,ue=K,Ue=dt,de=ft,fe=rs;function pe(t){function s(){}for(var i=new Uint8Array(123),e=25;e>=0;--e)i[48+e]=52+e,i[65+e]=e,i[97+e]=26+e;var r,n;i[43]=62,i[47]=63,pe.M||Object.defineProperty(pe,"M",{get:()=>String.raw`dynEncode01a33fc84f8dïoØå§ä³¨s¢þéõ?§šæ—ƒQ¬·ñì>_•å”è£Mí'…ËÏÊí çåzmòeúaݐ§¢é,šî= 9z…ó¡= $.JõT¬K„'¨®ç ƒä“Ó·ßé‘àäà(u¾ŽÃ÷¯ÃìÍH¶3KQtíŒ;Çø=}ÉóX=}Çã»°£)½SÉVy{oEqûUHhéì8÷7ó[Ÿ”¼ˆiÖ¨A2º»ƒÈ(ä+Ú»‰Ðé*7šç}ìÏց@ü€¿À¿B;?=}A?>ýÿûüÿ~{xÎ#†ë5X.]J +=Mi=M°UH +˜5šâ’ª±ÀéOmrn¿¯2Ljª•˜{• ÝájÔֆ ±Û©bŸÕé)j{èüül8üàJr/¡™¨‘‡²Ýþ^UÜàÈO6܅jÙ5èâ×að1º€HH=M2Û6íÊóI̧[TÜïÚÉ)„ÁW|âßHeF,l Ì¥ 'ž0Äb8ÏK“ß è!Œe‘€J +vÑz–Œ& WrnV "5Êñ ýkwP”4Ãp¨‘¸RAx[p¼Ïóƒ;š7>ÃÑþ¨VU?°Rˆ›q9~ò‰Q”ڃw‡I=}­žœ• f›òÔ·rñ¾ª¸gðx7QE5-±®M߀ãl–?£Á•ãÃE óØ{ËÒÂÌ@µ –z5fñ‘}.;¾ê%Mi‡‰_³ZU@å.ɂñ‰î¾Þ€m„Ät†×¢. ,ä²BB;朤‘äûûZ_¡V)Œ‡ŒªAUµ¾BfÈéŸ Ò&§¦$¢Š»ôåÀk£z‹(Ÿ_zܟ^r>†ï ¾bˆ>ÁŽð/ªÄDTçQŽlÄIÒ°Q}:8_þŽ!@ßvñ!œOö +\”\ebVpȜ±Ö~\¥NÐ'eŸBœb·#ÈN¥ŸY ßà#W´0a]¼!þ½\n×ØT¿¿‡¶çÉS­îäx7óÎ6ñI-ììùpfåh¸Çú,cµÔeiÂeic ÓÂçˆ#ê²up¹aÛÝb0¼þ7–Ÿ™Ü”>F3ÞÔTþñfkï±bxö”€„öfٍ†ˆ±…6H„”ΟÊß6 šÒ°ÂyV¶Á?»‰‚@?{¨Q¥+=}¥±ÿԓÌá9%û!>Âì&¸Õ@Ó„Uùq÷?ñqt˜¿“çIU+÷g‚y+5sä +¼¬åí• GÖ®‡S1Kƒ;…B\©™f‹hX˜d)ºÐ¿²Uw ûûX1ƒ5°Ò1„LÁ”-Œ;j= piN:}îºB¯ÅÉwXtô§¶¸3Œy¶¯oJáàLž¸jÎn®În -Š¸ýûÄàsT ½ÎòÓÄes ´{OsèÚÎþ´ƒÇ“å9SekF†‰TV³úŽK-”š÷WE¾OÔ +µR½‹ø*ٞvVèJ°§†¼Å¤ªàCŠœsr²^Õvü3ããÔ¯/´ Poü|$ RÞ§KuÉ#þ‰[ªß{¦ÑVú>ý>Ôws~~š9ž=Mz¶-ږ•hiò>ñ²1ždüÿ!ü>ý>í¾¿ñ赙À ž/«~yæ¨Êwór„b0©ì!ˆ ~°»Ïžøj§Ý#ß88š÷AlÛ¦3¹ÇS8“oK:xÜ×'dAkÉÜ<É;–)3ÀbÐ{í«Û5?þ +ÈNøRÙᾙ¹/+ß>ۏ¾6åô}mzQ|p÷Mž¼ðjÑí©;2½&Ì#g×g|/[k*Zõ8ZvãXƒçàF–ª¯a*÷«ª+V,\¨rÏâ'É:G-àëˆW£óÿ‡ˆîŸ‚8rpÿ]k€=Mîéû|]´$Î"’îá“VìDŽ_d%~m¢>aj%U6¨^8"žb ]¥®}l†ì­ïèÈfõ lÔ֋çÝÜIÃqÜ;ñ‘°[‚ÙåTàÆä>í•÷™õ{¯oí½3*iú•D¿gÔæÁ@sÙ Ñèځ=MÉkáÑüçuêÕÙðw¨ À ³Fô~{ï_µ9Ѭ5,}í5×0ÿ>_6l*{îò)¯GjE7(aôrÃbôÚj˜Ç½ T°ÜÃÄgk;ß_߇Â֍ûÄRZðùÒ¸§=MX*gê[ÉXÚ©^uN„~u~® +:Õ_NøW‰ëP\^WpÀttØ÷2ÿ2wTØï2þ2v6y6;V˜ÌۆI÷?ªê„̶^&>&í£F]VBfŒ¨Tße|þîõ¹ù±úÁ$˜]ö wá†yƒÏV.îbÁۂŠÆ.Þð¡ØۆïtnZ$þ:—KÌòñӘð?«Ÿ»…±l“ⶠ+SӀÿZ·ƒ©'7 +s%»4äcL+øÈ{ +^ÑKCU¨n9ðÑ~g³/rӆV$ +[cÉ6 &›(¿ô‚qðx ú’Æ6ÞYSÇO×Èò{ ’-L;áèÚÆUhk(öyžÄ„è7´Þ1CY˜¤Dèé׃40,ä$Em9†¦S£"¨ü“ÇkU€ô¹ŠË UgÎ{>%[2\ösiUîçÓ.œÀ¨ãôÂÏûÇ~që¤ +˜$¼À&*ãhæM#Ïî8å#ûûU@·I‡§a†Âú[Hc°cŠ¥öém¶«è䪪ˆ#÷‡ˆñg;ß¾óºš/9í? g~ïB9¨'Süß.›),‡gi±Æèzåëf¹,Èâ^k¿|Ð= ”ÿÉUvˆñÚÍÖـ"&•|ª[Å¢lŽy‚D¸ÖÏñI´QCÑWõȉà°zÈ2_Ǹß(ËÒ¹pi}çkU̇-PV$MÍ)}éžD×q¹{¤aä†.|í+/ýºIë¸××I¥^bü½ö?Ñ/Æ°uCH¥g±•‚|¹MÞ#L²„ææûºÿ 'œîA-÷B¿7<ޙԭ 9ì&Ô¢fÓ1EãÈ/ cE%5ÙO‡£.uK~¨¸;Ëíô°ÿu7uRö¦×³…®= °~£3€0Èö‡ˆpI ìx‚ûŸ)þÏXËyey„o I%Ùý°ðà8­Kð!9¡ƒ‰C£'Ý Y0¶±Ú}îùÛÆze"ߦ[ÌM…ˆ÷qAš?¬;QW–et„ bW¬ýÛ)aW–et„+*˜¾­j¥h8Î +ÉëÎ +~øqù|ô*= DÑW©â +q'$iÇùçÆ®sç4|¶¢USæK|cÅÀ(46?µŠƒ#ËM0÷rYNAP÷r¦á«Ò×{²¶ÁØäpg´e—Ý|¸ìH=MïmÿOn@óå+(÷Ð MI3“ìñÈó倝Óz*^,áÿJ²Úz* [Òíxê|Žfp˜„ûÂûûBŒf4þs*þs*ö [ñ‘bG²ÐnjÄ%Àßñ«Zù\è¬è ‘ K-ÚÈ®”Û“×ΏVyT­´1˜bØüJÅZÕüJÅÚ=}›<ó´nul/6|@{æÔî½àb|é ª³½wTŒAª³uêÀ.¸šNvΘ‘æSUL-÷h›rH7ÙV +VÕ-pL¿ã.PÒ¿êÃÿqÎÁE­k…ÜŒ¦ÙÇéÙ²—l¥(t³p–OYúHp’cÖ¸ó„æÞy5½¶ÎÕ9Ȍ³˜³²ô³2§'6Š­´É‘»_9Ɏ^¿Ï«;ÙËa­&rb—f±_ZØÀª%#ŽÎh?̑;zÀVš‰j¿Ž2lúꋆGÂ^hì +ÊÀþߌðT†Gximü1Oå®&¥ïߗµe‰cË/9/ÍN͟ê¤MÎü´M +ƒ•]Fökj®òJ¤C©K•'îO§•GiÆb6ô¡ØfÒéþ‚h°¶˜À= µfŠaG€Hߵэ¬Ãà ÖO¥*–Ýh·s-#ýtúø*³D¡S6(t,ÀF0(´¡†¿nƒ,Ùádý˜Y@²íۄâÀnhé9ÿ{~@øJèT\†VåԞOó¢üdr턦ür… +¨& +ÝJB¸ŸV%tr­QžóӞO—Våâ SBi¾ÿڐçܘÄ6OLi.p·Å(›Õ“xD¨ +D¨¼¬ ؍ª¼L9¯¨ÌšÓn¤° BªbêtÓ«óÚ;[K°èî¦AÀ—Ο™0ý'ð§|ÌÒÌ÷´rq½ó ̎µazûû8œm´Ê/ŸœV:È&®¬ØÌZ& Uk£‡r¿Ãî9>ÊR‡rѤçśz"ÈBw&!Aúÿ’‘üd˜–&ù¤º¤ûë©ÿ?þlâSwÉ£kl¾2ž9²bÏáz«–­®k€(Ȫ-Ðgýuäƪa­ß÷Ðî5A3*®ÖÖ»­·G‚V@øb1r# éof:&z:ryG~qb˜f“ÜVš\Q°%¿¦cœåþ„¤6á0n"՘a•Õ:¬®{ ™$îN³ºüÜ~=}Ą6¬ÁÛÏÓ¥Y¬ŒÆ€m‚šÝÜÀ,ØAà64æ0kÐÇižL#=MMôk%9ÿ[þ|Ä[Á<0¯ ý¥þÖváÐ\yí¬-zÌOï1ä@ ¤†ÙÙ®»…ÏÈïõ¯•j¢×ì”=}G›þÈÌûû¢r5·5¥„ƒ×ZíõÅä֔þ&žRX*ƒü°Q:ýBÖ.ùžkô){©Ô™B­>´MLZØî’Rf," ŸÊ⟢¢p¦Ò"ìÖ!¡o4•ºÕ††z0ŸŠÎÆm0'$¦g,ˆžòߖö k†bßì°qºU‰L?R©@c‹4¯Öçî쐁 U ú˜Û‹(ZEO DXzÄC=}°×D$ãÞ)é]S²œÄz¿¾ß‚ÝEL·kÍMVañ„%FJ +4ôñ=Mچ™fÒ¶‡AÉÂíÁ|…Ô¤joÁÏ3ÜV5íÇ-:5rKÜ[uò¦\ŽÀÜ]3ނµÂ«âQÙúх åô=}*´TLß8Åî7>Â'±„:LCdh úÕe÷͏M73ëFrԉmÝР“†ÿÀ’ÓM|"6d‡@O…¹‘cs‚Z¡g%n’B³ý£UMŒKÅÖ0Ý8ÒõÏu·ø¶g͜¡ñš¾€ÚÓN Š0þR)h= †þ‘P{nøQ€I3Åß314™’ÐT‘= Ìy”Åy¾–l܅= ÚÃiUCg´WœQ(ŠÁ$^])Ù⑥þ 2­Û·>å˜ms&b¼úªèôQ'Yè¨FüyIr1ýÎmaî0' ÇÈýªÿ’#}^W_W}2QjAÂ*ϐšO¾“rûÿ +Dñöëß_\Z= Â)¡# ú„⟡]ôö¯öx1ÓȒ6γ5_x}']Ö+—ü\c°”O×ì9¯g_³Šd‘óai"©[9ûЀC¹^\:R¢ìŵ‡ýrt‡q׉Çܹ–‰'ÆY[7lÅ4ÓtÒ¡SÛà -W~ üx¯d…ãG@fýÉOsó–Í@O¹ g.æ ,ŒHåÑ?_w‰Š•XÜi~ZS(E¶²ü×¹RJ=M¹Å_=Mø˜ÑL^ålZcþ‹ëÙõðÛà„¸‚õ/ð±Üzé/™whW:ÍÐFÔ #ÉG´ÂÈWŸ²ÿÌM+ínríî#ÌDoñ•„L}ª.–ñ¸ŠçpƒÑ}Ê YW’õ]jãN¬k¸Áç÷F€Lã äªÇÔ)üõ¤ùæÈð} +æ® õ9~%žñõª÷èþæutµØî «ï‚h w3ZyVÖfÛÓÖ/fOP= =M»Ô4ßýa3‘øœ¿„‡$±äšFY\ò[¬)¢Rø¼‘fلO ü71w¿ŠæZlý֋° +ÓÚ? .)fsnW®Æù–«Iò‡Í?Uñš¥A1ƒ¢7+_꼉ßÝÙâÕЦ]s¿îs¿ðS9šÓšŽú ÞWLs_Iu™=M¨6ÛèÝë§bCgт {F¼©¦¶ÌKDZüMv•)Fº»ý»è eI ÏÚëÆ*$&Ù轔Öe´XSX×øØê½ñ4 ÍàG+ôgÒ  ÈVæT[å^~×?|#K·ÇlLÎØi;ùV'j÷œ0ðÀók—=MîAxyvšJbÞà0ŒVHÈA€¤M¸;O¸‘/(SÔ«¦üÆ3؍kU´Jf³)óg»1W¾‚lÄë§1ŠTõù݇’†d%=MaPu™í(pD\0Þ0;7àe“(ÙД´¬µ o6˪@L$Žë¸Ç×cL›Jß¼Òó,]þ&5µÒ‚&ñ´…tÉvâPŠx XŽÔ šÔ8ç‹ÖHëKÊ0]f ê8-rüºªå±­¼(óåНµÒ3äóOôIø—Áþìj# = —bF%4»XîÛð«!@$¹@Bl‘ý’U@O‘EDç/ }òªT3Á$È%nJµ’Æž„[gÊóÀ;=M5»Ã:_ÿq¿BßÀ¥1æ-e ×D¦»6*„Û¿×_ +ÔQ’¦sá0Ž¸êw—Š]ÂÕÌ6Y£>3CëxâF4!y'Èw†±Y¾ŽžñÕõ0ww‹*·ÕX•3ógg5^/’S–Z.´‚ËÆK†9éÓm"Ž}bŽ}âV|‰þPê¬t¤]pbŠ=M»tJ= ^¼-ÄÞ˜=}žWȼ"¾ix=}žØW.¢µ}d=}žÜôwŸ·}S‰!/þ·KÂãŽys}ؚ±Wwþ¿0—˜&)” +±}Èóðí×=M© ~šÜPAŠ9Z†”ƒ*Sό؜ñÑMN·c¶Hï–ëŠB¹]h_þq^&Z=}* „’! ÓœÿÉÊìíƒv‰™“×®>‹BŸŸ²Å2!´4œp_ÊݛÕ!©diŽtÓ7Ȳ¶–®â.;Ù1Ðëb¡º÷ñF"inˆ/­­¯YNÓ(ÐÿÄ<š–›(ÓC–9~õ-­"‹§7LQ‡<1Œ¢‡­ŸAäHÕ«´Dî‹ù–‘PŠ€= ñc{?ýW:Í;Ԃ]cán¿˜¼üÍJŸ£)™Glú!¯dئ:PNÂg½ÍŽ^–JÑjÅ^¿6f(ÄA?anD(°Hsn +$R§Ärkx÷ò­ÅáwۍÈo]?!$"A""÷ð lÄ–ä¾qLô;±óðŒýó;^ð=M,±ð(&²ÀMªªÍü"Kb(6tÒevö&h„ÌD}ÔeŒ¼<}TÑØõnzÞª 7º}óì.$¦‰„1Vå Ç=}5Mþü²!ìm¹™¼6ˆj¦š€›ƒDƒ©èÒ·e § ‚"ŠGœÑÜ%ɶÒÏYa[Jðe#¥ïÔ/;}¿:óöª +¨ÓGÄ< 3mN¢¥¶«Ÿ°XقK—Þ°2ÞŹð¤d|9c¹õ.“J3¼«+™|°*Oôž‘ æï1²„/½©°„ˆ¬·³'´öÛ~ö|õJÛOi¹Ê·û† Ø<¦í>;°HÜVßÒ= &XÀîŽ15 ¦ðöƒÊ0ÊÀùl†=}¯~ñkÄÁ’41qø?ùÕºÏO,K,N¥‘EPÕ§v4‰Íøþú•³¥Êˆ‹k+*5­çr#1DÛ<窣…´¯|{Ù1¬‰ÇÆIje¥Yàkadž°–ÓÊ 3$AßzŽHÇ·Åö­Ë6·Uœl þq5!¢×“EGm*°¥þ6¶ÔùWøJéWÅN$p õ}ÙÕ/}°•,+:Ù +µ'ãŸã²_‰6U˜Á|á÷*c»\ÆU,g*ðÜÆl1ud™ 8¯à“-y÷I ŒÌxÉü.ù†e=Mɔ=Mk«?<çNd2o:Sò°ÑXtÅÝjVZTÃïÎòê;èXúQÉ6ØØ91Ï=M1èpî®Å(è(P I³p¾[Ÿy)$ì$ö®…K÷„¥{®!nP‘Úvä6œÐ’ÈVþCr¶>èqvÉ2ÇtDd@«=M¤c¶ç¨·íi«5A‰›,¸Ôfª/ْX¬‚š×ƒò*2® å¸oô +{ë.»Üy½Ct EâaúÅç¶Gtœd‡nòÔQº5Ñ66/Ë1=}wk*—l$¡J_!JÄ©m,v›À„p£re /7P$Ç.6TAE%8Ō7RÓ]Vú¨µpžPßGOëüâëüÂGO2GO2HO2GO2=Mëe›HI@Kgg,p^›OSü5ÏÆyˤ˜&.‰+&Adëg=MÿšŸ‘O[¦çŒÀ‘mƒØa% É­V­8=MûŽPNfýú’‰0uÝ'|!·‚ÉñjŒÖañ÷eWÂÏÍø4M;ȅ0 À'ú4¨àª¦~F>Dïo6¼.r]“gO:¦]§½„”Xhg†õ(Ýۂ†š‰@´¨¯ß£éD˜üÄF§ƒÃ»y©õŽ@€= ´LM‰ô‚¯[y‡B3솆ÿºw•K)¢w¹ÆFY=MäՉñsÏGBGˆ'÷@Ki=Md¥”Ck¤­3v»Õ=M÷N®fºŠÍ;Çà“³8MG>†Œ(’pÅ ²fÌ ŒÇ$‡€î2Ô.ÿ8ˆ…doÿ£©æ.oŒÏóSÛpX¡ ێzÉw%šUµ¹ÍîԭہMÁ†5fg +ûîBÚþdŸâõ´ñŠB­,ײc‘ÒŽ Pþq×wŠÀ²©Ã= BOµ%DىϪðõ™üQqÀ5 +ÀRÃgèˈ‰øÅ7IÑÜ" eŸø;âqæÞ±g&:ã'sÆk®¨ÏíLàôoªH'÷:'E÷df5*îx= £ñlí®F$«wmٓõîUT¬—»™åÕHFÔ]ò&÷*$Þ)Ê<{\‘ΘÍ?Çâ›V¹"œpžAn·{‘²Îbu¾âaÍ«ªáéĊ9z_KW´ûS§%í ©BÆÝ®¿µt&ƒdùë³ïú7ô£ NÎ'ð µÅ©ïz0´™î¦êUtâKΣ¤=MdTrzU8vM[§óѳ±I„[‚ý¢wIÃÑyn·QÔÝUæµöېðÀ¶{}IC°¶"ë®ó ß›ƒ^vèZ\y}׳BE6’°Èʊ’вJQ$-9Çê~>t‰m w¿²ˆŠ¦¨®®(|nÈÛp».„ÛTÀ^æ©É6•:ݽ獲=}÷ ÕjÛ¼Œ!ÙñI¯Ž.y_ŒüØ5ku–á"P/qžÕ¡>FˆÕëfâ=Mg4‘_’q|= lrö™RCD‘žö™zê]ÚæšKN4_©vÚè}ò͜p[üÊãᮍÓʃ.ÆiñC˜ÿêÀž}2&[»ïmá­rÍ=}ĺ2x>ÄÂéèÖ{ÝÎæ—íǶáN!|Äώd[?9èH^ù—®µ;Z\º¬yI‡ì²p>œÚ¾POŽ?@†º“ý\zþ}53)¡ÐÁÍç6 O#¤ˆáÊcËc£ûÀ¨Û¥Û2¤y£ûVBر«£Š›_ÅcpF£–¤ƒŸËc=M¬ƒ¢g£(þf!yÛãO“í#·ƒm£ºÞñ#Ñ£ï:£áÛ©¢næâ¿ã#/àÁyÏW²C ‡'՟X؈%¦)Ü p¹€ü˜CQõãÓÛ}~¯$OîLàHJ¸cø:«7í­döº¥ºXJæ9Ô÷ÜÀ›ëé0þ“õÕÒÈkîщȺ–õK,þ‰H‡í‘3:?77nóŽKÔæÈÊÖ÷*¯Õw¼=Moøllÿ8#N5£ØÔQ]Vóú1(=}•®ëdɌüŽ×üNihó.ˆF¹Uø†bg…³n ûæyª(…âïþ¨5꯯;Þ_ï «Ö­&tÊBƒ¨Å|’ø ö×P = 4o™Q…”&@²ªÞ= Ê—;·iª*[€/³‰<“{O +ӊ(Ӄ &;Õ8 „8üÂÓoC“ý¢€¸ë€ÓQž;ÖËÖÒß؎Ä)¯Ï«fqF¹ÀÆ1È>›'ô&9(T•j²õÚÙ¡%¸&ù(”F" ”–0†cÿ2S s +{“¦O¦>©Y'®ë=McsԁØÞ©ú;ò}›°1¯‡û{VH!t;¬OÃ?½‡Ü¶q £Î eÃpHXzÿÞ¿žýxÓê¬ P‡\šM'È¿…ë9þÿ„}‚#Î'́ƒ}”÷»é¥¾åzt僵´„ÈAú.C„\}!iÔjÛAڌ¶¾;’ðOŸ ÞÉ(àp’ø;åòXCy¹JùŒÛ51J.Nòvô.=M@ƒo”g1f”ë+³­1 +kË;´«ô7À® Eˆ»¹éôäxT/½1¸xâ{Û¹ûÝysäzü°>;‰{e«j'S†ßMqÊØ>ÔÏzöp‰ýP³5=MX= :¸Š[ð1·u1ºkĒÌï¬v+FÎoŒÍ0Ûçs•2Eî§tävòÐÒ×8Õ/ ž3ùŽ»¤.\i8“{Ô#¤)eÎç³^Hšòtéæ»e½¶g፻¶^À¤g4xŽ³©3èô;¸¹-Ks—Á%ÌÂ>Ð= /³Åas8,}F9ÔRšrsh¿Ø³ú1jé ]Oš+E¶™ÛM94”%k’˜H4v|ß‚ͱÎØÑLN ¤0ãÀØjj®0 i+òwÝeD 缸¶ëûº?k1Zp½r¡ ¢ÍV=}OHV+?®r7šâÔúTÃ\´æ~ÓY^‘É\9<„¹ a§ñ$‘î;òîâ3öuJ?­"ã¡Ãoœ3ÿeKJŠ}T^f[ÞÖQõø|R÷©ìȌĎ랰Y8¦}òþr5IPò„8/0x¼v‰SP—|IëÞ(æ.ɇÕéH#2廗[_¯ÈHÇ·y”-1“c’©ÈQËàH‹(DÛËvšìR Œ„£Ú"/u¡D$uDT˜Â%Þûµ«HÝ>f·!Ô0ŠŸL‡3¼m!_“À÷4{Çýàrá©-¯ú”Êðÿ(jˆè€‹²ÅÎÃÌĖUÒ±T§#i(9í97­öºÛïH¥v•%!ßOWª·Ä~ãÀ½<€A iŽ¶…/%Ýز1PŒÂé Ú/¨Ueiñ$ÛÚOw±·Ü9C±0o»>ôí±ã¥PuÔµ·Ü=}C±0o»ãУ¶åËgí…éð£"}3£Éx{F"S^¼±b^Ù v„wsõ×ý½cë=}P£i,Ðx' S¶¿£aDŠçH‚Oó>.ü,LœD…º‚Lý.œ6Іm…™¹ÿS~©'È3>§,à:FAú4{vìÆùHèâ~ëAú$çpq¬…%=}AúÐûV©¼rf9Ø y’Ñ0\î«ï= ©Hܓ‰±(äâ°6^€ÏOнڀ/}KŒZXÊ]w˜Òbþ/U}Ôn$q2èÚ3¡ˆdüzbP®µPÔ0‰Õ˞í6¡LK¢oЃE¬= Îèt¬ZóÉk+= ¬˜ý À²‚­qAå¬'þä²'þì[¶'2ã[6'㛯'Ӗ›v{Uj˜²^¸¹îŒ,¹<ªJm=MÖ¸ʎ5,ró±Â.Ó=}txHë2ÿë'±: ò>A6Ü3ÚȖùaÈÐ=M%Œmٌ$UÀ°²åœ4“ä ÝÞÇ8èðSæ\ö>qâ¯}ÑxO…üx͒¼É¦;çÒgÿ;»D.úâoÆì:Ñq|Š¨m}'¤@nÍ +Ác[)•Ôph¦ýI”Á­×Åà+(w9¥¨ìGkÁ,Í òש%t’°gX C°C€ ©}½g3¿´‹„Û±F£îÔòêÖú™¼ ÔlÝÂÓª±†FOkéûT•b–l›SOÀ"4²äqǽbê¥,ß*i\ô;S€‘FÀÿž$ªAÉÞF¸l”UKŸÑIè$u¾Oå]É×ʴ̊Œ´Tö>[çƒû ³Ü™¦dÍO =}¿fW,u&Gâþø¦ì>N=}ù»F‰…½.Ö]MÌÔ¡‘c·»vTø02ƃM1†«Mø°=MöÙr /*‚R +ÙÔ ä +\.ô¦Î0ƾ¸êÅé#îmQ‹k;ÞbUä€Jrr ý¬È˜ ¯Mo†,ܲìºBë®N­™äè $*{$ô²ùn]/!§¶­!ª ¢OÖ³©oIz¤4Áeîô[&ãvÃÉÎ{HÍë©Ò\Îú4¥¥ß£»‹œ±WðNÕÔs{BµN+Z‚,ôToI5yhÐ}naO,dèDqU È0q¶œÍRÝÒ4LÜ2¾á{å¬Ï]<º† +}QÅÿ«^pY½ŸŒ.ÓÖv•UÓ\)Œ›ƒ³ I’®ÖŠ¹ q̬vÔ­ "8w¾ñ +I°ÅîHõøì˜úÝwܤßi‹%aƒ0B*;Ó´±ï5Û|ƒK´ú¥²IéëÕ!¾úÆURD՟J7žFEz\û.*zlîAԓ¼…£·”Vä)•«zp!V[á»DBÿx³Ãœ‚Â%:WŽÄËÙ¦ˆ6ó{³Ûyµ2ã ~Ó= !°'c¥‹þóí1ÀëÚ2ÓG„«¦š[ÖÞm3Má0j»)†ÃæÃæMª¼Û]ŠÂ +“1œ½W¬ŸµAEçRè̵·×GÃ;ƒK£ø‹àÇê=}pöÄá²kéâ@§oÎDEÂÈ«™F¡U …v¨bò^ÄÏ×í*Y¿KÝÛގã=}ÆÊ<MâH¡·Ç˜ØßÃ.JpÀ)ñ*,Ë)ШŸÏݘk0-<Òq™ ÿd‘¤7¸¥ƒù¥…W'B.2WKà§Ìëz_و +Žäñ!J^žÇÀf Õ‹Ÿ£…ìµÜ¡ÓÅë Äî€öÉò¥Òimu³Ô(ÌB邒€"‘—,ßà"]!í»1ƒ§=MèCf/WrëF,O(ÄCC1Å*U$J\ŠìV]•AÁ†ò¬ÑÜ'‡Héô2Z>,©ï‰¶Hö¥VڀCJAuĞÿ“ÔKG”;Ë5Þ¿*ðÚûB{½ rèJK+”ˆÿ zþ âÏÔx½†ãõ'»ÞL+0¶Á¯²¸cšG‰:ù=  + +´æVüK¾ C0‚ÎSý‘4Å0= ší⠒-~Šˆ¥ mÒRU…ì8c¹sž\E²¼ïû n¦¸]͈60ÐÃù/Æìâ$Å}}#üÄ°^;¥ŠøKBÍùe…æ!™Ö ˆ lB9Zþ O>£ñop!= Ö)T]ŽÒ"•>(Š¡üâ>?–¾ !zöù†þá›Ta–\‡8 LI¾ºM‹Ä¼ÈÍ%Iãù끞Âz†^Úb{½æ@å@ j¯®[ÖrŸ’Åñú%®d‚m‹FvãüÓÇ},/3ÓÎr¾k:†>Dx²tÁ7_ÔfŸ²ÖJ¿=}òޑÕ|AõEÒ*ù"O& }¥Hˆ0çôWùØ!… +Çs®zã4‘æœr„ ŽPÅgû›lâɝøÑ@ þ~Ê°¶¨ú’퍾Y YÓ·¿~a…²ÆÛіS,‘|Œ6ól\ŸÌ—PË_Vy†Îù ±J¥½„ræΕùìT®G[ù‰BnWôËieºNúûi¨°×@S¤••‘ˆaýP?ÒdçùN:"°”þÅÿŸ[òœp™dÄPÞ02Ùâ¾Þ†אŸŸw +ðƒAŒ, (–<´£é8oŽå¥’ ++»«4D‡¬Ø¥C¹À»ÍÒ°9KÓp*ÖB´¦£¶ÑDfœ’Eƒt8ÔÎÍY˜0ÍEW¢?Ÿ >ø¸ãè#Ûº?b҈®íq¯~Èëî·õ;œ‡î¼P£x~v/Ç!eÌ·5“Šî4_7ÕÊòÄÕËuPÔmN{ô~Ò:!%ªõ±Fc3ÑHüµç°ÅF)wf8—('¬Dc=}ƄH'"må/Á­£7p|© Ò  =}TÍ}œ¨6†jÐáèÔÝyh¸È¬Ôz®1ô2âô«+ùÏ$Ý 0±Á™ +­j›Œ–P–¶™\GB¬žÖ¾šOÂÖù¨½¨¸$%EX,=}ÉÓ(GAӓ=}8Ž%ÅÓmÎ= •Û@2=MÖõ“Ézr¹ý»Z+ïuûGï;ï&#ÇüüH]X•ååd¨r¯Œ*É՞ñéx&*¯ÕX¸ç7NO k™CaàOB„zrSEs®8·Œþ{:4G= —æô‚«Ò„éå©#EÜu]õßºSÑÍ£ðÙs¨G6ú‘!óñ‰gÔ" ÎLiý¦Qð +¤º,DÃ'óö¼Gâ{ü>Á’¬¹(¯â2©ë£ЄèïDI+Àgï k7iï8¥ؔ“í3ÿð(—èsnĨÀ†Ï•êª¦ßHAu-/ë¦ÜX'mÞ=MÎçnKk fõMÓ<–ùŒ +͍ÆEÒ̚®fÌóV\È!”ÓñQq¹^4SyáèÏ-=}<¹×3ýy À’ ·ðmå6·¡­MI¾<Vrº2F}Õ²jAŠ¿«Êðå„w°F+i×?_˜}AÒð(ûï~wÖìzW|Ô]ÇA¥ˆ³l<@gúæ©GŸNà­ß·v›´Ósx)™<›ýޙƒóAЗ|d4hÐ\k +'Î6­óbö¶Eaó-”œdcóöçšë)<7*ãkcëÌ·ãk·ۄ¤ £·q¬²þŒ?Á2ÿ²m †>L š”ú=MxoèÌìŒÎº?€LŽ=M¬V·Â÷¢ñ÷¢0<èäÇå“Áü—ðxîG5,ñi¤Á×׿±\rˆx\.)¿À¾c{žùÀYKž$öGÍmÓa°ÿ)sJÒCÓØw‡øè²1yÞ±µü·æ«wê /¤"+œÅL‰œýÓª*¤.ÄxȂkâw%¦6…eºå¿æÃ.|„sƒ!-07ê¿#ð¸s*IY±'UZ¬@®óõ¦L·Î¢S&k@>øþ8g{O qèª ³ÄE&õ#«CIþóö‘HL)Cd±äbãûÁ úb¾àÇóQòOõ »¿Ç‘|c>/ã—˜H–û7».\ZiéÊÌÔÐ1syìæjƒÓ¼–Ô¤•dŸ—A·Ä;ïpÜÀãזìW7˜€·[~yäÔ=}é3Õ0¡ÀX¾Óqä@>Ä{°÷¯ÕøÃ=}Cã×ÿH»B»ÕB=MåÛ¬o=}ó'Cä®ö +'=}=}¦M…ÞÖ0Ts*he–óÁÃtÛVXÑÔSB+¹f}=}9”íÍþgҚ&ucõ›m6~ + ÀH÷ý:Q¼BÍ)³’#Ì'žDDšI,ˆ¨pDIºUŠ=Mѯ ±w'A¾ì¤ì4K+÷ò¤ˆ¤Û·&æ{«§ô‰:|U®õ"ûÿ~S+Yɋ¶±Z•˜|á,NÐoGIöçJ€-°º úIÉ8·Ù­®¡¹'_,È +Z±LORŠü Vpêh«RÜ IÀÑ¥o騋y㵤†æº ±dÒ@T¨àNۋ¶fòn×ÕçYӄ} Ö¶Ûø° ]_¤€ ¶ˆn ¥š†* °Þú¦=M/ ^N©òߌEŒ®Ý†ZÕs´ +1Wޔ.«:àÛ¶UMÖ'ø*TM̱ö딤Ïv]zœ‡t'I„5=}¶þ‰^cˆì^Q‡Ô—ÃÏöv;ïÂ-rŸ¯" ;»º»1è«[&@»R=M]-„*àç½µ×µä7כ@ ‹¼=}'ÀáŸæÄÃú‹\Näe-^ö•ìý¨8§pöè H ð£º&ËÆdt#Àȁñ²²w5kÊ_^RÓþ%c}ç:—DŠ6ƒN¹ø-+Nuº·‰cºY\kÔp]Ž´"É»K[¬‰=M 0Õÿ‘ÖÖ ×½¦)Dδ{Ôºõ´šÍm7º©l¸<ÞÎdú*Y@ Ž+ÔG;Aº5*L?¬/Ðý˜3ŽšUïUÇkh¬è£Q1 Ó~§ÏççŽ,i¹Åõxs£îþÀPû÷ÀoʑD6¬Ý= È¼DýMË34²C Š^‡¸¾£±¹hÁéKñ€˜ózŽ–¤/StÓ¹weP—ͱÆBů0²Í7ñ1ôfãËßúp[^pô’Ü8Ì2÷It0²‡>Â.Ì46bà†­/”YÀ–=}ŒáånӒ‘ÜûôQɹcœ$dJ´d*+©ytç¾ìVY1´©eo?s;’þÓ|i}kÀëöH‚§‡ZõFÜN(µŸâ¾Ì/C‡B,\¡g®E= z¦G ʁ\ÙZN¡ñ{:w”WQ½%zÎôÆýˆ:XNü®l2¾þï- €¼âë*6_E.²Ö>†¯§Ñ²«lÕ bo”ÿŠ›T¼=Mô¡¥5»Dðs‰ìz”7÷,Ç?]¸vŒÿ±v[Òð­Òù)h?/Xði³Ù¦î7Áš%Ë&ü\×/–Ò6@³xÈ7=}<ÂOR'ŸrJ!3ueh¼#H+[¶Ð!ã¢Ö¼È³ˆ¶»½±_Þ½‰÷Œywñ&C§&Aþ¦bÞe?Ä´jãÍÿ•µÁÇfø¶«ÜèEÀŒöñ(ÀâèèéÝw¼ ¦%(Ó?{Žò¥˜kŠÄ/©yxÓÂ;Š·•æ„74‰Ö5ٍ®jëËÊҍ>#g¬ûkMJOŸËHIÉuTä‚(tZr»“_‚§U‰¥_wßڂGNE‚m e(|¨Š“,=MYz»¿Ù™I*‡èf/k]i jÿ&rpP;~ß]û4Èv¹HCa,Jy1”Y+LÙT((CÀV‰ÙS‰ý1Ð8ÞnÊÉÆ<ÌòÎ'¹&ìT…̸BÖarÄÿîˆÝÇ°¼9“°¨2¬ø—îÛ½³q;Ô5rë\“ù?€ÆÍâ²x©çè v‡÷BA©GE)ãà×›AõüP‡ÌÂe÷jIÔ鈁4pèUv=} þ¾‘¬œ;/ùç4ìôdzóÃÐ&³urÐÙòˆònUmh+ÍlÝEí¶m„¥åó!ë¹gK€áQ¹Š…t­jR…ª÷ÃÊ2Œíd¿bm’ËrÝÎíY­_»o-o½;úÞÔ +DSfí,Lú«&ÂûöW’'€K¨Òâ­IÌ_B†zQh^9šF˵éW:=}ïì×-²ÏðÑær€–c€üù¬Ñ/ԗÃDEù´“i¼QP4%Úíö”ð]¡åŠa–E×òg$€ éÇbÇìUbÔ¯Â×£¦Ã£c™lê¥õ3)yÉØïnÿš—Ùïnÿš—Ùïnÿš—ÙïnB°M]ù£)DÀˆñc±[^ŸÍÜ2ÃÌ8 +v†±l8‹22)vß îóÝÏ?xÎÄwêÚËéÂx÷ù…"¨xÆÑëÞ-ϒ-ü^Çgæ,r‰x"Վ2ípÔ±­Y}QË&¶g + oñÛ²[K(ý ™â8¬—Z¢‡Ò¸Î&\@¿®Å„úÊ Ü‚ª9ƞ÷.8‹VâöémÏ8pO{ÜT1Oâ‘p¬Ïj=M9Â=  *7ɲÐÖñ튕ý} µŠŒº-‰VžSlTüuŠ";M·y +jƹÿn±1=Mꁄ& OŒÛ½‡íf· •â‡T09*ÂD9qôq¾—ž/‡ŽatŠ6x…‚¥”u»™=M²–$€“r&áÛ߁¿fœ †¸{¤[Q ¾ß|±*‰q'¢<Üݐ¥ |‘ {ƒ06ô¡ëM±e0Á«Ì,£œéj}û›*Ksåʤ¶THiý°}xã-¹Ê]ﱅ;+là1gR¿ÎܙŸ‰€g”²*ïY£ÝiÍh+hém{OÿˆJ^:SUú4óˆÉe~ìo‡ÎèDö]ûÁœ÷xDZ¼}4è9±ޜǙ‰= ²î\+ÈzR +Y²šV¡Ÿ¯îv= l}aûiÍ>t(= iWƒæÊÝJØFfM· úCát¶ ;¦¨u<„éQHó–wƒ]=}¯uÚôõZ‡ ƒ£(2òD[]öØ.™‡ti¸Ë9âDÀ®t=}"átnPèwŸ{òý<øòb= >dÎóª&°ƒ·% šY;Úf·‚P]ƒ^ ²Ë®„,a+±½«ß» Š)deë'‡ýe+µ•¯’0ä~I”« .Ø 6ô ݸP"ñj5–Vó‘†#¹0T󐯔.Îïêpêö3ªMúDxÉÂєæZ£Ï Î«¨Z%„Š½-@Òf“ï§õÚç”fË÷wçHÏmð…3~³*w:ÍÀ-jñš§WÙñ' űûM0ä¹½;wƒ\©j=M„7Gh ~ýŸ94ï…[Bk¾j4¶¿FR7Š[ƒK͇»üddNÜ'1î°¿™ð¿¡ìmÇð¢x¡j/øù9n/Ož‚kXAš,Ì.íÌÀ-7-† efzñeTòΨ–öŒ%,î$’˜ÂdHîÊT!Kÿo¾ʑƒÒ±0ûÏ!_Vô_s”3{o¡_ûX“1Ɯ@[֘:ÖbÈÎSf* +ŒŽªÕ‚tiÛYÍ©n€Ø2Ð諾R °ÈîüzÂN<ìZÐÏŒÁ™W¢´™GÌ&ø¼Z ‡TjZ’U°¨HvÙÕO3ØrŸh¥éÙTc“9—= ¤H›XIIטú-v.qéšJo+dw +Œ«}?QCöHçI†¡Éz·Þsϝ8ˆë„‘– ºÝ¡È;9‡èt'ö¬„ˆ¥]Ž\']ùÑJ#xÎìØЌá‚ãœnYsÑY&ŸðÒ’ÆŽ"±4PvÔM¾=}éÝ)ÊLt¼Õ;ìÔ<Üw‘“¸}a˜½ý.éÐ= ê¼®Átêöu“º×=Mt¾mÿ™$ÑOå(¾"óqµ}“-ìNG”Š=MŒwH©U™5^o­ ¬Ô뺕 ‹©¾CÔŸý=}ú¾¯*±À=Mz´’K։m^eX +ם®Ôøàԑð»Ôf,=  OÀ#I\&ƒâw¥¦=}°ä2êæTö14AÆsL‚ˇÁ09O“YØ݌«ëÞÌ$¶äx£ /(‡[€?£†ÜÙ+.ÇRìSŸ×¼¡Ñös™„÷;TŒû" ©I‹r%@rÏmTj6Ü+=}.Ql1˜q¬L@3×h¸´¬~â²vQpj›²‘óùcõz®-ف»‚dªQTws²Ú½ŸsRsÂê©cŒçÃiO¯%RÓ¯®ä«¯(*ݯTFftLÓ¿ÖZé—;ëß ¿¬YË­.^èÆÖƽ·¦h»Gï.(–æ,¿Q‡h¢ ¸ï²Mø² ‹?Î+j—4¬Ë8# BµS÷ÅÑÒí'f ç›ófcô°j=M§¹ëzy½Ë×»7C¤H S™H©9Ýý{°x….= ¹€…Xs~LhHÏп ô•î.É¿Ñ Û_§1?Œ¿½ò1®ô¿nºwPŠ‹>otØ(FÏjŒn¿¬ZÛ¿Á{âŠû”9æ™<†Œ‚1Ј¿Ê"í‚NÛyx'w÷zûÙûØ×…{È š*fòãò•øõÌ 3ŸœRTÒ$ OUÿ G "Þúôž@üM}À¸þ9I/Kõ™= õŒ¡Å| +ö$UèÊ<“ùÊOéÑ;T¶—ž®(Z¹®À7>'úž±¬‡šåžíÄÒìêÿsNP›õæïpÁÇaaÌ1ÂÖäoVAÈ1íºF',1p[›Ñ\;XP QZð5Yú¯ï7!íOyr´"ȺNÅÂ-ºa·€žÏ½qѶþ xAœ±‘eaôòØWÂÎoYY êSÞ ­‰Ô®OI= °.¢Ü(àÌ@p¡x|íòyø£vR² +î÷,Œ¥ùú= ;НY?X¢U5È:R°ÙTۜÙo +ºÞÒOÞkU‰Áüúv{·-pb1 o 2ŽÒVjRTaZ<&Ÿ¤ö‹ˆÖ”ُp¾yŠxêùžˆ¾~zóo/úÓVüxˆ}‚oÇçé¤@þópÀíKòîç´Ðý4¢ Ù‡a^àð2ë‰v^s²õ‘ìsB}a—_£Ü¢ñ=}mÄ×Uìñ4ÉF ëÂη8—f(tF¨Kèm- æþòIR¶JupˆÖ‹€|Wbfÿ…Aq'V÷äÆ¥_ uÀâÕ9ªô“1óØ+G^»íN÷ôè[=}5ÍÛ0më:›TüíÐþÒJ¶Òú3v„ÇM-è}bzóôJ×Eàðn~ë9ÞÙv*KPÖ Þ\°ìà0vE…àg’’ÈF6žLŠ9&õ ;U_2uS jlcÝ8K݀ %Y×ÞèêÍñö|JV‡A=}CvºV̄—è=}ŸÕ|BîÿHØ>QöBXÚ ðu]^Ñ€BùÐë0ŸÔ‰œ—çþã= œ=MƒÒl„½U›Ð(Ò ò@ùGÑÎâö“=}RGV„Þ= ÿëYº6ŒAõ•€ñ²ú33•.uÕMk—ï°¨ytd?‚™íÆ\™*¡ÅffRpƒ›= Üq  $ò(Д_몾-ÿE*ȝ»õ#Þh) '9èi{f§ZaXeÒPô¡T°_®žzÓ–ßu¤‘>%€+á@³6€¼Æq$æ=Múßâs°Šêl]Á+ü™ºIəç ûÒߖ°PæÂ~=}4EÒA±*\@žæê£ú±ãdj$Ëbâ« Ò;ƅ#¨ÁR½/ÔÔæ3†Ý·»â¯ÚªmÔpە¢À1¼œÆ˜oë¯{ß­¬tF4Ünµ)ü„N4¥²aÌSœ-ØÏZÎ4ÁÑCqé' )∯J–«•ðã'š»ç2¿®N †‘¡Û7¾2²A¶So…_žœÚa}:i¹0ÉåRhÇ@'ù.ÊæOÀ5¾“ß­õÆJE^hח¢×°ÚZñêF€¯=}$¤Ê EFžÏ ؾªV +¦!™“Ž#Qü±;†Ôª€˜‰ÅÃÑ =}ÆY8ÞåXy¬*ۆiÀ +/“ä<ÎÙhU› p°âÀŠÄCÁßx–”}kÀ¨~€ÔFbªI?—cHʉe 5ºÂ„qïßmš)F= ²_– óÜfº©š cy²úR›Äë„2ÀŒ¡;¹#0þ¥ƒQÂ+ïøÃ"²ç¾*ÃWr°e.€·/È°d¼%ÖÚæÃZ«h'xDKÏ°h;LÓò¬fü= —¯jl?—Ú0c›ƒ«»‰èÃTÇ©çÅÓÞµèéô‹Œµ¨&ôëP5QFYÕ½°œt{}ЩøÁԋÇ©hÔkÀµ.,Tëq+h@UûÁ¿* ¤Sâ·*Ғ†ƒsè(FÖø›WåÝÖÿùDðH­é¦†ô†¿2ŠN…4ÔÁ=}uïA#±6¶“ÌÙ¤ +šÊ€ §gyÇÃKúÅeÊÉÅÌÏ´ü>¶D6@+3š,K=MQi!=Mç{M´Rii¸=M·Áôƕ)½•Ò=M—I$ö²°Î×óryÃÍç&ön׳6Ø "]ásÖ.©ÎŽHÐi˜xSå®8@¹¹–WS¬z…åЕ٠ԋ#¹ÐòÔÊò®VjýÌ ÐOíXûÿR(&Áêé1ðàܗÏÛ¯Ðܙ1„[â<&àjòÚ11x¿²…Ûq1”nÜwÛßÚw‡žÜ·êÜ­vÛ B‰óÛ‡ kˆ;"&|À1‹ª¿&A¿ÐƒKd•é­ž1†—êÌaÒuž¿"ë-ÛíîÛmÛ¥¡ˆKQƒ“œha5FO-fK)FðÐê6‡üÀjq1ÌbÜíǍ©Ì_ÇW4ÆÕ[,ÆôõÆÔ¢fÒôìYëÕÇúȜ5²5Åì(GýõÒ 7× ‚¢¢ë P½€\4xj~\ùä(ïTC :ÅÞDëtœU<$o|ýt8˜z2Œy1ž®OhÕ +2ñm/ˆR¯Þˆvî¡m‡.8ÀÙ̈z±Ì†oÖ8qΑ͒=  R_HêóÞW¸}'˜³ÉX׺=}ý¬ìžÉ«®2pµ½©«¸¹„%†ÑW"SÿÛI^ñË÷¶‘lš¬vž¿º¥ +A¹]m´‰›‚³]qÇo2ȗ”è;PͺÍÀ¨ ”GZa[!{¢Æ«Á£f£#Ã#pY~Ÿ>¾X~Y~Y~Y8C€^ZtVf Ž.æÇJ%·ªrt4N—žÀ½Îãú%BvS¢Þ]5 8]: Hà"üËM;>uÛ'Œqá ýÌQë*œø,ðï6í,Ök3±Z>u—¼Î½9XF¸°ñˆFÞ¬ºöÝæ£@@²ÄÎBë}m¼êéz5 ~i¯Õ@kDP&(GÜôéF¾=M‹–Xéƒ F’½>ÀCl"÷6Úð\.l^·œú#–=M«˜Pèðt©[jéƒÙŸ–|,Ê!C½aªձfô_>â$i† +3•œß)ØÑ$ژÍÑQa,>‘ãl°{[‹ÄM»AC̆Ü1°¾b‘¶ñ¨!hðŸ'dÂ÷Ô# À®*UF2(¾pœÔõÌ'¦™ƒ9å{à܋RögnüÈ °Mƒ J½oýדRü®‘P݃y&¢+¤º¾fVÌáÂÚ*§ü–¿³X(X<à×EÀtw¯r0K]Š¤´äÒ+[žCMd²ñž¦4žÚ»‚ÀæRÆ¿íúEëÄ©Þ­†cß´î6ÔýA-~ÚÕ3A¨r<¿·V‡†ìäÄ ‘8ÕC¯Zñ÷ÅeQ½Ec|£Š= p{ƒ{­¢vÀó¬%y6g:˜†Ór³Ÿ90%|¿š‘ò¤0֛NÙ2¨%ƒ¤Cu§£cYzY""‘yY~Y ~Y~YÚVMBÛÊÇ;ÂÇ, S×ÒnÓÈô9Zvˆ\œý$=}ýè-yÍVnü(P¦xë\Fñå<©Îz˜ér^²¶•\âmU¦L]ß«P-dˑ½mugÏÀdƒ#4ûÒ½08n»r½ê@€ ñBë9€Ç=M ñ„Cn÷׺ðŠ9¢¯)Ñ@h3'|ÿ‚Ô:\–û¤A5.[¢!Ô¢éæú$?ýWˆ=MV’5Ñ&ø—¼=McèPH ÒF˜­é˜hêzxÏ~U¹1ÿ \$ÝPð»9‰€NPo€qc_À{â~h0\[ù[º–Zž4xÝkžæÏÚße‡ðj;N%æaʔ_=}üöqµø1ªbÒS~È9š= eigNÿÀ^Øڌù^pwFÛ[ÑFʂ†Km z‚Šu¦Ù[àgŸjÆ‚ ï•DÊûQ- ‰Ö…ªß +[›®mnÓØ¡Èø‘R» ›=}[º ]—9ú‚”h)o¢#‹2ÿõžg-‘ò_L›/ñ}º™*HyŒ¾È)‚Ô¢¦˜žâtÜ¢.o bl{“*h"4?¢2춰ÁgÂømÈï~ej­ö=}oSî×ÊÛo%<á9WÛýDp€âً YžÎÞ%Á}–I¦²ÒÝ5u,Þ*?ÜV~¨nÐQïþŠézw˜àÛDŒ,…ÅÒ=MŠv)²ZɞÕ_ÂmF <Øß#ò&¦<àëHad¯¸e´Xóm8f›P»q֜«oÆ :1…]÷×6Èó7+Hb¿h…¹hFÐíCPÊ4‡AÇu5´˜GSÿ7->Ë°¦T¯=}hÕ¬·reýžÊl¿õGô:l†Cá¨OøËG÷T+ÚmhOóÐ#Ìp½‡˜ÑÊ= ¨| ÎL½û(—3=Mi°N˜+ý1MfïàȆ½»ÙT4Ð6>ÞX¼=Mò­ù„ï.Fq§¨\ƒ^y(ò9QËI[UH@¾~\tžP¾pÍ,a‡ñÿ/ªZÓud6¶ÖßÜud.a= _›•m®ž—ñVÚ{ªŽøá络×e:؂œ†jBî!/“j@â(ɤÓÅ#Ž¥K¯g ÃR¥&˵g£p-¨–•ìÓqŒÄû¯„[Ëù墽m>õËÜ© fë>¹iœbWK°ŠtŒ7´A´då0ëe=MżÕ5$‘ôÛï1EzšË¹êZâ=}ayÔ¯½½&z.»iÇhÕ ðÏô†;Î0t(ÛÕ= …üØÔA؆lrï˜)§9É5Ïìi<çiÝʄ×ŬtvǕF5ÔÒ¼n‚l ‡JÓö­Ž]GuRÄ8Ò*õtÕî†åZןÒG´„7q*$Œç‚íÊgÞ&ŽÆÖ³ßoƒúãɦjU‰ìÉà&VØá®0Jü°mIŽ[Ýám (±Òꇉ±<‘\Â3îgHÿù|‡Tð8ê˜tßٌŠVKÙÂBPœwXÑÃË^#X®®ÛŸa#A·÷ÄjRºõþg¯ü´•àè_úï>±oÐi‡èÔ¥’µÙ™Ñ„4µñÌÈÑR7MHûßÎH2þ¼àø«:4€E¿R›y2̊hé®Íø÷dn¸/÷d¬^íù×÷…[]5gZ8•KåÏNåYT­€òø·ú&E=}f¾N˜D=}ðÁØ;ë=MSþöfh:Ø;ÞwuHI9\®=MÜ"J9è)»‰w„Ôa1:ʎ+òûàò]z†[TAQR<€c„E©Éð=MCpL©ïý×ÈC´ìÆáî½\V´pÉW=}ûwF,þ=}{™C,£àÄ”å?¬<= ék‰å‡ýÅQç-[R³ÔT+Š:èßÈ /2;>øqò·ÆÝà †ÞԖïțÉìó:ó_çe~®µ¿¡í‹Çù17n HþÈȉM7ap÷Ýl›;ÈfX2¯€HÕ9B8”ŸN/Î GTY,‡OAÏÊ:?¨²‡oNÃpød=}ï¨Þ^Ìórù“ b% ò·? JÇlèìr×ÈèÈÉΔ}Ig”)5.â8[>K«®øtE÷è2aÍ9oøÔR­F^ÍM¢Ÿ‚ç0ôS=}D¨=M9öÖ-½ð=M­r¢¢0½@±€»—2¤#X¦­¥Ë;ºwYÎMñ~Y€N€}YPCY~Y¢É^üf79)ámÖ°ÖÙ»ý@Ùß=Mk¼Tî\úgõb)ê.¼lÝjÕ&j¬n5ª9íâ϶ÛÑ +ËŒ ø‚WN†³j˜ü1¿Â±ÝÕdžۼ”7=}nÖEGJÀr0™AٍjÕЕ¿‚ƒÌ{vŒMJò™BApT¦Ôv©‡®ÇÇ[ CNc;$œ&¦ޅce$*¦Br§Ñ€±¯!¸›˜Æóá)sÅ<óÔ.“RCÓ½„äØĶÄê„D’„lÄz¿Do­Ä‚©„‹„xÈĊàDgîÄ~º„ŒâÄgõÉô•JúM‰÷í¸ù…9óÙúó­zõiÚù™XôqøY—ö/ËlêÍöÊÍÒ Í ¹ÌùÌLyÌòYͶšÍl1̈́/“U•9•D¡(Ն= õ>•BÐ#¼Õ¯FÓ?~i7U~YŽqY~Y~Y +M¡z“›‘ž„AKö»×Òµýº¦Ð+*]Êä¼öDVŒ”= ±p-%Šé<•F…èó45ËðܗQ5žMFs9©Ëôöw]Èý{¹À+ñèžEÜ ó6¤Ë™³WÛmؽPÁ*ÅïåøJEôÖs]| ‚ßWÄýY§˜1-Påbyj’'ÙäLYFÄÞt8€Ëqá÷…#Áå 2CäjóR¶KtÌ7š/ŠBæÊqFºô-n z¸zæjRÞ"¶Ð= (å€äx’jàžá-³ˆ'MàéPACòô2 +Kfwƒ¨Í¡µH(¥béJe¾÷„Эš±Z)’ôk² Qœ+±ýLò‹/8Ÿ/™þ‰Òqþ‹RK(ÀTǂóÊÞ ôaFMÙfû?ÂK_°Y™wŸçOT°V¸Aý“Ë$Uùn}û¾Ö,PZ{ñ~È^šTàZú™q៝! ha|š"a–" ¢= ÂC†D]Ÿ©ý£H¢×N)®ÙòmdÙaŒàڑš?ÚcÚÎcÆÛD¥ýÛ= ¥/Ûï‡QۆHxÛjÆ£ÜKºÜ)8ÓÜ YîÜãj ܪ‡&Ü ÿ7ÜboHÜ©Ø[Üä8nÜ ‚Üà˜Ü[§d¦#»ë·¨S}Y¾–‚|OP~Y~Y~Y8GO ÁBZÖ°(ftA® (^oҝúÊ Ô¢š¶>ÃgQ¹ÍU¶=}o‰;´þWY²ÂÜր‚ û#Ë.Œ,ay¼ïüF+Tƒ½ùYb@àë½AÖ/b¨,>QƑrþ‹R*^ Š^¦7L/a9ADÑÿIʌÚl02ôp0B ’Bד¤ˆ ¢?7t<‰R‡Ÿ42IºçŒ?ÏÀqöɆµQsI[´ µáåiRϋ#>°ýññúÝz‡ì²\ŸKÔ+-àeŒ^»=MÞ4ëÎIõìXÑ.a¸ˆ6ú ¬ €ôì¢QNÊbžÐgÐvìMÜ × !'OÉ r߄”›¿þ(W,ØsÑþ2Œ‹ð{Œ\¸øYޓ>x_X~*¦ªD¿§)¸j”~—ÌÛ@ߪ: +º”:„¸;º*¡~Ûc²2õ«:ñŸ£ÒB4ܘ¾×§¢åê¢b‹· «0ˆò4”¥Ñڂƒóxõž/Q^¿[Úm06´Ÿ“ôµéqK5éh 7ø%VK­¥§xCÄK$EýÓ:täa]Á­o¯ˆ 'î¬ÉsMÕ&¢'¨™ü†¢’4f 2p‰1{>„/q¼úk‚—ãXCºI¦ sۍª­gGéÓqø'…“œ«7Îé„𯗑hæjÉ´"Òîä±½Ùù vB|k2nh"Z®Cºÿ§ŽH›Ð_¿”Ke °ÝAzf_^´n7ý™ãÞyèeø² ¹S³ÈòꓘۗFÊDÃß&V:ŠDtZ+È%ƒ¤S­££CY~™nY|±Â|YÔ~Y~Y^kàü†y®P¼ ›“_ÒòNžu£Ý%©•€u¯*Ó…ÓIaÀñ’Æ”´/…ÿ  ¥÷iÕK¿–¸ÊÓLŜÍ6õâ(Ùdי˜A$~<ÿBÛ>Hé+ÿÚ2d– Ä x.›–¬z +ÏF|ܵŠùE€S­NR›S!À^uy¶—Æö¹ êo¹Ù+Yu\Þ5sºnڐˆ’j“uäi/ào”‹Æÿ/8*ò³â‡·^Fñ–ƒ–üAÛrh<º Ñ= g°jÎ02Ê!Y§h°5«8éõþ9F‡Œõ´„õªLµóEi:ó°¹ý ”—†¯‚ˆpØĎ²+ÔÿB-²²3”3cª2‹ÎoÿpÊoúmã( +'nßêø¨˜µÒÜҔ-bµR”4&b—þ/‡ÄR—AdÂ žÆH…òϝ<bcZR/t¡é+ŒÑÞ¡ÃošÊ¶E"T֖ڞΡSK†ÒÈ梖]ÙPÎÛ_êºd¨àÁ̎Ƙg#|b°ZØ;ÔE¶Þðt> 1 P¨nòý…Ih—yÿYSäÐe9N8oÁ„#šÍêy?…ac¼¢ÑŸ¯èY0DßTh6·ÌÒ]væ DÒÍF Ì|Ác8ãџ®… ӆ^åSþ´i,GaèÚ×5Žôïÿ¾&aåÏà—…û=}¹r5Ô,WÀì…z.©œJS꽺ë +&Ó&å:ïÇýUS;æSúšßG^•Ì|ükIŠè?²Ò ŠKõýq:ÄuÑÊO[%e8ø'®N´ªysÕU1úŽÜ ycE9éŚØVˆ®èPK +vN±åvÞ¼ÜD>£¯¥¤£™?~_Ä~ûQÐ~V~qØ~Yþ¢*¤o­½#€$@Ó«žCä[â;@6*ƒ»Ù'ªò)Û»…)ĕÛúº2‰«¹Í1gfËA82ep×^š/äM¯‰q¥?ÞÒkîäÄ}¥'ë Å/{¥´_Uç“óºè^»ô7^1艍Û;ÛA:†´9>c“P¶Ü‚ß´:ìo5†i߂juÀžT³à#„¤ÁG“fÄ/²Âii´Ž2ƒ¿·P×à1}}¶_½µ‚/6+^?užvëÁàfr=œu_’3éʉÏP9ŠSÁ> ºK>´(y..a'(xIb_åˆDN¯?Ò&R¯LU«]†'íÒàðm(I•¹dZ< *¾Ò‘u"—Á¨î’Ög¦ºâV¢æÓ'm.jo۔'‘8ò<ě¯ˆ +,qý*ºß4ì*oÎåG6ݎ°îªaÈEƒÐq¤’ëSr9ò œ4ªù¼Н,krB¡'6 +õâÆWž®r!DtúOðaêã1¶Â‡[sË*E/ßìL•~FãgÒ¨AÑöçõ4híO8ú…Jµ18´Ìói‹[­µ„ͯ~EíX^͘Vx³Ôó†{ _áKV_è@k>½qxã;É*eØ\_ø®°ÒЉVn÷>û´ÀÁ̋S]úª„Ê]/Ow‰Èà1HXBÔä ×@*¤p–¯,ÔØÇóo+çÆ©½Æ˜m»>© ×d҂'Jðü¼m[€ /˜Ú|©Ñ;†]c¥ôî®WÉýUën5O4ù{ YåT~-RòPÔOXɗ]°F­[Äîñ5ÐZ±{Ä» ê7[à‰âôý†#->¦Y®¿ìXm÷ÊŒÞôΏo®¸ +ɱđF®É€Ñ_‘mVéúá‚fQ0N‘Ïqêø}&äLÁK”/Êè^= ±™åä>ºb= ^dªÂŸú›uŠ…n4(!›|TyÚ!; †‡‡’¯Ø›ñ[{²ïØ"“üƒÊ7"Åڟºù"´Ú“Ú¥Â!DŠÚ¢ žÂ_B¡úvÍŸfO™Âú’›VauBlÚ¥³Ï°ãçÉ3\($]·g.Ã!?® ùÓU +åëﮢlóõ5æ\ϯ„gÓ%²*–ØÑ?ªÆû¹¶d[ 4/1-ç·vÅE{[Þ=}Ît‹/ÄÐÇ)|Þ¶¿îa÷¿*&…+5/Š ß赬«ëaçż¡,‹-Êqâ,H…l¯šòI=}·U¡H3çÍl¢õlÆ0I" «,JVqâÀ¼”¯/ Ök·r ãɲÈU«²kaÙM$|쉓ÓË*½LÿÁ©ŠuXà²83ß ’oÊÃ×Â|¨«)º“ÄòÅ­üaZ+kş™¸–Ä];fªí;¾þå’dõU†íœ[¯~'Õw29–ÑL?ªjÎ3ðLƒelíGv¡¢¸ŠÕM[ÏppO=MÁBN„ ñ°=Mº=MÔrNh–õÀĨLJTKAôU°oÌ#Ž0ë:ڕ— ¶ý8ð[€ÉM]}óu1øîÞâA³zyät.ñýÝݼ•]®t‘ÑIb Ú΁šSYb*˜ÂüÞS28BžûÛ[B žô›2D£+&¤c nYz]:Ö!oY8Ô~Y>€pYþvt…ú)¹ Wë®,AÙêUW,=Ms y´°‹3FæF±dj ÕwˆQÑ,f¼ÿ:þé'‰ŽÔ'Ážñ‹DEòù$áXƘ|€OrÞ¶¦®°³1ÕCõcµöæ1¸½• 7Ä}½æ¿o/«ÍÕÅ!W´ /k×ÖÉVk»d Øi›<<ezôÉÿú>S€w7”(IWÁí'}7òÉ ÒÓF’ëÖå=}p[ñaÜÄÆo áñF¹oäðö®¯2XClc¥P¿®ÚûŽS¨<9ÏkW‡PȜªÐF _Ìÿ’p4ºçªœÓT@²2¹›³!n*ñqaï6›UEÒÊòœtg2ÖÜ!P˜‘â"–ÆԁBZ= Ÿëñ©èTR·– +ètŽå·ô‰tp1I]Õ½.t/Àpš#»G¢jS ¯Zqg¿.Ïd&”}³.„vûQiÔò¹êœ›ÛÀ.F‰¿b††Œ}àAFî…T_¼òQ¥€­¶KLðCê¥ ‚Çó´3+žb,ÿ@?ë0Ý´ª@Ç ÈɁÆ,Qî5{à,²ùl[‰È¶<BÈFŠ««ãc(á¥]Òº“„)#±ù×¾O©æ¡ý‹²Ív»ÿ3é>m×µÚI”ˆ½î?u´·JKÝ@{pŒ/"?q¸Š¯oîÕ«Plé´2ÖSq‡ñôÉÖNÕì~;l<ª<·ÞTÜâ®fDüRpEj@ύOVLnmI¢U9 +F"Ð>°I܇H‰sà*'œÜ/Þ>fÜ@ÿ!j‡…sѺŠL\»ÀnˆqŠÿ¹¨‰ôÑr†Œ+ß]o†mVßŕŒ;rĜü˜šz–üýã¢7¥…^· + ¤ >®šX£èóÄ(ßÍgþÄA2­Y΅ú ´=}èwÒËe¢ï´¾! ûÔ°mñ0ËéÔQ2-”YÐdÍF' +àíûàJ§<]'ú틍mèÌ¡ØHWýþ톘õɼ8ÔÕQ/ÑPn+Àúi†wàÌfkQ?¤Œˆ?ù1ho519ö—Q?ÿš­å®Í›yR%°®ÍËâüdXÒ¸·œ­üý͇|ęî('A¸µŸæ+ø#+ވŠ¯¸4_ïg„VŅ"+;‹Ò‰õ<»@Ä¿:+TâЉjºÔ½¬èµžîûàò%ž¬¬¦‚ì{ŒÅˆ97ҀG;͈máÌîAëôûl›5ÇFUX/ºÆ5ôi­ÜÞ凳m<Óç14¿öX?h’‡›Üλ:Qƒê¨Ñ†¸«YÌk^HÃè, K5N¸í½©Ì?R•ôÈËlUôÔÓìh7¢÷o ót[í(•O­jr͙[¡;?¥ˆ­#»ý~Y^àXP pP~Y~Y~†ì•¾ª0Þ{º3/¦’骆QÜû•~deñ)zu5_6¸>@7_„qÂÊB^˂èêdèO.Q¾÷= Å™òºŽU^Lnpb>Ef€—ÿˆ  üڊá[\=}qf4-Á ™s\2*{–à/â‹Ät¶²D'y¹™J,:ÒU:r†_ƒxÈÒaÒ… —ÔM52@WŒˆuÂ^š›Û zj4"á9‰•Fw•6nŽ +jãâø¹œükr:ê!íi®sÝ#Q¦×šªËßÛcŽè¤IÀ­ó—ô#%Çp¶GæPÃFä9š®U3ü%‹Z&7Ÿ¸‡=M‰³$íÄZ²ôx±Ôõ+p¾å?ܺ…>³~6)¼üÛï= ©›fû¦™_;"ò;ôÚf›¹‰ÿ]«Šíê{}ßeÓÔåp„´ÁÏ1ÃÀ+’ñ'MâƗnÃS]/cD‰¥ª§$¸æ¨Ï·Ë^úƒm°¤šº$nLçí¸¶,Ù³ü…¸|€êr4>ç?¸Ì¢å‡ ÿ,±,8Ç ÆÔÿ9{Ø¥,þ^Çñÿ4û“ñe6ûÉ <xUY:¼Cmÿ»Ñ6DïTÕ8Ñn·"Æ6Ï$eˆ¿­æÎ͇LS×O3!#%Õæ ©(~Ê·áß+ŒÂ­ÚöNOÌOÅwõìxì$I5[í¢͊YMÇßNõ‡,½†Ê8… Øõ·t[ôöª0µ-i™d½è + /C”;éŸ^½â·ìE6à:q2•Ýª=}‹»Á@7€I1yì[ójZð +ˆ @~²×È֛;nƒÿ& 0°½\¯A<©}&êV°=MÓ» gƒc°&ª¯jÙ7‡ë6,xð½ZÕŽÚŒv€‡žË¶Šß6¾ðÂÔì"fG“Ž;¡ºJT@rè® +.’НL¹Â©ûW?ÔLhœ¹Âòýga”ŽPhfûÙþu{ûœùS|ycI=MÊ>ø%ðEÙPi{¯>V@p™ Ù>Ñ{o F U̶Òm ôˆ2µZå7‘˜F ÂHóË-bþ7tfEÏ?™B#%¦.¤ƒ|YžL~Ùâ6}Y~Y~YXÔBjÓ7DjÏæ€ø)7^¯Úñ½×Ÿ•?Ÿ^92aXכjUÃ@ˆýnÐâwT‚{•Ênh¤9<†ÐxŠüû¼]~•xÌ9–òÞ¡"nôŽ®êr‚¿öÛÉ ¯Í ÷t4Fa†HI1¼@¿\©Û0‡?;ae”eÙê™f1)º¿¦’É¡Ašïjv’î2 p6Ñ\ŠÉr‹ŸAWßÇ“¬¹ßò–¿‡4AÄVßÖ9‰ퟒ–܈Ì"p–fÆra:¥°‚®_”øC~R$Š5©MG²or¸÷דhãdÝ$MµKxÍóQÖ#c +F¦jJ)§ž$WN¦Zɪ¥ªñ¨Õ°¯= Â[›ÿ‡ŠÚÆèá1[Æ!2Wð½44uÜÅ¢¹-ÒÀ<ùØõ_!‡9é=}X-ß|Èõ^nSkÚ¶Å8 +é©Ñ-OÿÀÜ_È!‡Tk–4œÚÅ@Âé­áÒ_™)ӊ¦Dƒ:elÂ(m"¬ž6¬Œu¬ V¬J–¬‚ºº­:ºG]sPaÓã'áp(ِ(=M€(× (º*®Îi¬˜J¬’p¶iÒ¸¡üÎïÅTê û{훝5ó‰Is˜$ӎ¼–nm$Zµ¡DUˆ®qšír‚7-¢ÍâHó1HøÝô¡kPOËK‡ÏDOÒJGMM/KPß8MI FŽG}öœJb5K5 W•¢çˆNŒmö8.7ÄÎ7uŽ7ZË®ÍșÎú2˕;„££å.‹Z~ylY~nY~Y~Yr]v0#b~$i2ޝèªu= ®}B,Ö.«â-Eb¬¢ÐÉè³NF-@Tg›êµTÅH»BñWæ·oÑ¡qÆñ·³ --Hg\E”ø0“ùêzóKL[xõ‡ÊIýߌÅu™·P-ܦh”D…xë”~Û´ 5ëÓðgBÈEp¶bϵr:-ÆÎh/XE|é4ƒ{ÄI’´è±'–t¢[+ݓ@¶¬Ò(}N…b>EŽÀ†akû¯ÉÑa·Ô-mÂgsroIÒp˜oºâ;¢"ٔ!Öl¡¯–¶¢"¶Ô5œÉM\¾€-ijØ°µ;7,p–G ä­öÐXœÜ@ü9d·Æw ÔÀUªÿÑvÒ2œábŒõsßÐ͸Öþí;KHð>U ò–Èöa-¼öp¿N´XðM]Iå †)„= Tüw×Áþ=}&Pp$YdòVñ ñêÖWV €x7µš×B˜Ù*;ô ¥r£¶óƒÁ°Ø *>ÎÆïrôÉq»VOÌÞá›A³ßðÕBñÔJ=}rJ*¿Öo†<þe‚ù>\T€ß~á×‘ْԐ°á™¨@ӒÚÂìZª3\f£#fÐz»u…~™9 i|±V~Y~~ +~y6pR]›"¼"’(W™ò™ûšha‹pF˜‰±=Mút?¶[¢v;bOèQ”jþqs6¢Ün¢Vò.Á·=M¡î(ô„ivGÒÓû:q.ـÛýú +7é@dUû= ýJáç P­¶×ÝɝHð5EA 3흘ÕÕ¼6M¼vÍp¿K3Q–%ý¼‘ªÄWxX򫔻 ¦D×qyi舿À@FgvËú:ŠÕ ×t= Ê5M‰þ¾N[ê½x®ø9(Ó%(ycûy(‚7õö¾’lçyAšÇ¶Ë2‹i»¹U@®_Ç'%ȑàPŒpëIT'¿ŠÝrœénj7³ËRKì¶Ýs0¤¯ß6;hÅ9T1P„³ÊÛj¸2jߊ°Â›o!”2óe,Å)µìÉ8×ÕkÇ>¿lÆB¥­äj*s–¤à£ןt.Õ£1£¢+®Ø:kdØóŒàÙZš?ÙэÙB~Y ~Y~Y~À®)±…}ŽdŸë¼%‹¡ŸX¼q ÜÆüÎÖ-5at+"ÁnwMkß=}ãqç‚?oj—ÌËB)‰7‘q‡ö”Ïà*9\k«o +m›(+‹ÑÅ" µ9§Ðˆ…˜ˆ_Kþdî”øÿ_)¸¡Ì\^ÁLa°oÕ'…tÐ×ø2m’~,xѷ׃’,= ÿ‘}jÇ^¦ßág”Üê“òÜm(†ßÞµXTJT.à&= eÜwRƒ|îoÒ= /R ø1Ÿ\1vL>/?>X¸2²œ¡´*òá‹U +{¯!¤èÒ¯!pۖZÿ3rRrÂ4]–¼d†\Z˜Þ.9ŸÇ=Mm±¢îzâIŸ +þ.£Ê¾¨ÜÒg,ÝYy,Ó\75©ÔÏ=}h\zÑd+Áì/ªìV G¯1¶üÝö±L蕷Qôp±èAÂùå˜%ÕQš÷+NíoØ%ˆwTÙsãoü6î„ýïotÉCô**Ƙ üCrr@©×ÿׇi¾Æ¿h0ŒÔÙ +CÌr•p³ÖêaȍžP3 pÞ´¾±þ{ÕÍy1#ÉosäÊGÝÖï|(²aQg÷[Ô=}ªz±ŕd®Æ ¿… »‹*¡ "¡â°ƒ_ ö…%«»µe&€D×!¯†FøÛèh´¿=}Â(up 5÷»?¿nEì”_3c©(ç‰ÈÉtàÜ(+NL+ ITÔÒ@‹È<ÿ 8è_ÙÞ]3â?êMáÕ®{SO+æB-ܵóeY¬ï= ñ4 ¨&MÛH=}–½šwõTAF¹° ¯ ŖÖÆ¿þûÝI0°r}ü¼wd›¨·¢Œi×^*Ηtò³ß£é¤££·Y~‰òzU~Y>~Y„ ~فžï'@àōfmþ \ÓaÂP´ »˜LâΩ?Ú# ½¦¼!³ç©mÓ9ò)“óÿv*•ë»ý*)V= ‚«qž0C‡ëMú0…œËQ<2ɕi»ÏI2HæÛ9«˜¯ÝàA#t¶Ëò;#_Ÿço2ï “l1>/V·/2ñƒK ‡ÉðX=}rqp7<‡ +qS”Ïo‘qi Œ¿Yxq6jÿOaoæäÓBUD®“ìüã2qåÒEœ·>º¸W'ðUî|¼9ÿX?¡áÎk3ûleÖN˛3È ÂD÷ +*¼2.eî2J»<àZqŸ$“ˆîµa†Lz/µ_„…Zïÿ›±d‡š‡² ãæzªy ‡ätʅuá5“(usR»"Æ}”Þñ¢cքª² ‡gò™ŸEòƒÒ/Y ‰&zGÙ+4Åilx…¯³J<§®Ç3ûÏlî썺ët Éh1œu<í– A¯ +ïÚò9öür:(\¿Îºm–cõA—h­ŸYôƒqèaÝË5Sý,Qíï}Koëúdßó¨$ã8L4MÏÝOivçØmòNÃ÷ê‰M©™œ½¯C,8ÉZ^𮀒}¯ÀK&6?±­†ÔxtH 1ÁRO˜ûaéB¼+‘Ùcß}#J¯¼?k+Þé¿KÕeŠ@ÔZèï³k; _Ì9©IÏþéTN¼eÀ‰+g; O¨ð\¹§âTÇOx5ÔÎA0T _$Øî‹’fÔôþµõ´bjn=MÞwb{öàw$}±«¹ÞUvv4úž?뢎g*^q¿‰%[®ý¶ÿ¼„-’éگŀ“[ƒñ¢Gâû„lýæòÑO”eØw.= Š_¼ù‰pCÒἈ.4,Ò—/Â6RœYBË,£ﮇ;öÃÚè&Sì²t.uª'³E¾å‘=}óyé(…Ð/šðß3®ê§¸á´ܸlA•ë›¸h6LtÇ^疋¯þ­{™Ø9z¶†k/†Šdq¸g–¥)6µ¼Š5+’4Ç×î5S8ïx/·‘JӄÍPXL/7¢fhÈ"´)¢è‘y⚟æô’âÜþ'¡Ò}~ש0î +DÝB1 +sZݦ"\ï:4·6hEê|[¯l„(±Z±‰Åø9îaÁr:‰VÍÙBò­Û×E³*åyBËELQ+xiτG¼ùÏð~ ]÷;÷Ðf” Õ mu~ÅnZ¶U/Ô6&áÜçÒn‚äANæ‡úÁ¨Â¨…ÉËS“H¥M„-ïBF+šÚèÉÎTHµîÇ7üÿKoQm_÷ödHä(j÷3ÕöbÍ!z÷œ‘Mi|t=MIi¡ó‘oôê*Ý(Œ½¿ÿJä#_ɊÎ،ÞKì#4¹}= ýkÿxåxð¾zT}w_w G±i.;ÆO.þaњYŽUëö2"þ˜ ÜJêB&áVê‹œLṄ©/ÿ#/SÄ•°,| +E1´rÝ/[!l+ªQi+/ÚižPÔ&†ðau'üöaù<ՒKÌè9 ±ÙfŸ4¼yD‰'ÑÓJg%àè¹»VE4Èé¢ý“1È ¤ùgrTe0î$Ø´O ÕW âÙkXDÖs9B<ÅêTÀ-ª=Mr= $™_2ÿ'¦ÄîÀ»Îkj=Mêñݕn,œ?k"Š'ö-Öý>?tsdIn(w¾Ñïc8 +ÿ춆íL¾˜êsXŠ™ëY_ +UªnÚón62àA›”õSºµ.= »Ì0ø@ÚzrÖrp*aLáן—DFs:,ª ܈*eËâ=MœΰmY&+D⾪›vžwBO[£Ezª3O¼ë Ѩ•Ûß+C8äOú±iNÆ;–Ú&œâÛ'“HÄ»Ì'Es½-•˜óO¦fë¿i÷\×*6 Ð?Ž¤´wÒ1ãîá5nÐ4Ï= Ál;CËéÏ0I×~Fœ!Î=}kS»9R/dûÐ98àTgß+賶j4˜Ïs, +…¢”ïÁr‡§‘òc{×41´, 3ë!µ²ÉÔ|á$‰C¯qbq! É&™ +u:hClüϳm‰Ù}:U ­k„‰U¿w€Bn' +\gI‰D= Ê2Ryœ_;;rÞä¤ÕبÿÑCàK3ήsÍl#Åÿ‚·Ñ^;¤*.,Ìd¬ÕÕHëY:Eb·B›Õ½Z:TpE¿X¬ˆŸÒKãGY%lúΓ°N‡†]홻÷Ôç¸ÆbùÔIO=}(ŠNÜ´ó&›M½5=MüÖG¹FòXÏËQfŽÀL +}-mW¾«¼u#ŽJ©ßí½,ázëã¶îúؔZ<'„p}/è®JÒXs¿}-ˆ)ù¾Z¾Ž¨OQ +ŠPiÃbV&§~ñ¹ñ’çýI.¢]]3!yh%oµ1Ɗx–*„#’åW›YPX‡‚Y0rnY–M|Y~Y~ib†æapڟ‰ j¡z¿a¶•‹ª¸Üâû÷/þa·oµ +}ìñm+FÁËñ JÛ~ÙìC˜ã%äƬA +îóÜø5ªêÇ µ:çú5œ5›R6…d̉¼ë4AÄ(y ‘¿5 .;×v GŒ4IP¯üëz:ÄÊæ)mlU—À5Q[Ï^ uÉ.lB·žlFB_m¡ìwWÏâ™.‚õb‹±›hVQw-¡‰ˆ†‚¯; éXƒÚ>‰k4!ý±T‚±:Fã±2P•+b5Š%@ßE¸•oûEcƒ¨=}ø¸K0úC!WÅ@¥èiôç{æ´ä6ŲzËDê,ç¦ÈÉÎ]Õij8Œ7ô‰+ÕA m+ôä H­ö÷óQóaSÍYúø|NDíì5ÍU†x{ã0}Â=MûM”[†rºø +o¶-Ðw¬Ñùj$©ýºØ3útƒú_©­×t|N,€Úðe= ØÌv…ÉÐßy…H(¹™hý×Ðs žÈ‡÷XÌîs ’ÛÀOšŽÓöï*B!ݟxH&‡â]ïQ.Q0ÑE¸]g®Ž”…]Á–W˜+ìvæ-á^2ŒÖåùBgó /©˜œ[f#.’°{ˆ=Mcé¦‰RØi âÙØDÄΉ¾TE´ +÷08 'ye²½vÈ/ 3ÔҞ/”{=Meðے%æ= ¬¶z;•A̜ÉI?Ï×Èa¼žÑ<´H i|ÖíÊ=}–”+ÜVo”­ØŠ‰3Ü6ü³™t%’Á¹XwÉîõAû lz­îQ-ûTž)¸N˜P;>hG|y/à +pÛIÉûV _°>±|ëm)Äٟ½WdJÐhÂ<ˆ nLÙþ·èë" jÞÞAú~´aeÁ„Þ™+¼~öør|¨±'*I¦A ±@‰ëƒìê1mÞ5º†kŽÔñïáƒ'6'¶º&@³½eJÖ²âÜVn<• +¢„€îº±Û]Ób(™ØQGàhne¸6Õú79]Äf0r©ožv–´b‡1%)Þ) ‘[ð–Ô7ª°­Áï.šjòÁÁ™çòi6wòÉ2“…wÁÒw"ô»ƒh)!Òg6 t>p v¢R²šž3„f„ÉÂ%ڛUŽLÒ¤YaP”È8Âú‘žô‘*ç<Â.矬[ŒrðÆ"i¹†TÓ#z¥;R¾ãý£m¥ÜÔ3ܽ$·J¨…Ûà«TÖäÿ÷±å×Ì+l6& áÁïIÿÓdTdÛ1®d›«=}UÄËê)CÛÍ·ˆ D ë)·½-= ó»¤%Óõ"'ôº»i;dÓù Ûja)–}Ò¿fµYêӗÔ%p´_‹-c!îÇ°’ÅœjÆ5r4矾hoÚôß0ÅCÈ-|"ꖉ× ñt-i¯Bj“Ä)M»QP2ȘìÏʏ,N0ˆ›}ÛõÁhhÉ1UÛP1ŠR*” ýhVTÍA‹8¬^BcËn§͵ÛXÅ4A^,û;Êaß4tn6“^6g¾VÇÙ9ÈôÊ5/ ;ÉV\×uìqLñÁËu4,gWÑIAeãX.X p‹ ¢œ5x^\Atð˜OÁëV*üào?~.„ˆùŒ—…?vn™œ 'ÿ†+Âl ÿý{òd˜!¾—öøsr6h¦× ¢£R0© Ò½ƒ= “Ã@Úªœ•Jî©ÊÕ玐Ä;°ôÙg&ˆf7À‰=}hӌ¸fûq³µ7¡ÎÇûëŒP3ãè볎ÑÇ»4ÄÇ{|ÄÇ֎Ò$‰b§¢ Á#/ñޜJY™‘N~YÙ~Y4@}Ù¹XŸé’í洙äÅ=Mñ³”n©´¾q-[žõ'ºö^[;B¾.üÞûoX,o>g7ÞÒI’ÔÖ¿µ±:3“],'“ó¬XÊî»]üe?®µ±= 4“oÌhøí­pG½ÉˆBÌõù4…E7ŠHû€Õ-99tS\/¤f/<Íhù¼*UʆVþ1V/?%Ї÷ ññŽ ©IˆŸ@Ð +}ÜÎà­Y:ôE(%n‚ÌÛðd^W¥è1¥–ª¨%CÇ=}DÇîÈèaúõ=M]5Õ87pôõ„&5-%--¼ÍmZ÷ô™¸¼vø7k÷ôËêè•&-CCi4Dé~õpŠ’wß>I‰ýpŒØނNb¡½I9t“ê®°= bvs=})/W)Y©|°Z€ý±7yU[\9â* âønZþXÛUWßNbîj—õݝÙvô!;±n·Àzڎû äj²÷Ô8wD,Aüæ—;GÊYìr@¹= uvlÃ÷ãƦ¶ª¼Kàd#&$p +¯"cØK+4D°/ç©»ÜÙykƒXé'öéáØutåkg%]É͗Ø7,Nï;1>û—kç)oT }wՖKJ<’¡ÚFû —Ö1ž KЧõ¹‰ùV³ y‚¹ß.Ðï{ä~eÎRFù]ùUDF8mbþüÞr- +Z8‚Qü\ÎUôÖ ü¾]YÙ]¢’ᄀÙ©ˆ}ۗd)úSvN/@o‰7fwB +m Þ:r~¬= Yvt‘Ôª:¾Ý²ó"?&bzÝëB““,¦d‹Gý¤Ê¦=MÞ.[lÉŽªÚŠJ^Ü8Ënp™ZyÝàoЄ²‚°•s_*Žß‡âˆd“wfæ&cdŸ¥¬¯ŽÌóŽâ¤¢õ$*ª§Y¹›ð‘Hw.aE:†= /ŸˆN +4šUØzŸÁÂêywê +2¦¶—û’æ}juJáͼ ‡‰švb9Âüò↢˜¶ÿ… +|!â(œ|Ë°óÕ¿\£(~©û#c/£†u¤Çü¤a¬3²õQû3¨°%kžªŒÛÝk™8ä¿ê&—_Â>Í'Ñ´Eüˆ³Þ)[–+ßå—{Áô+N’æۀ=M7¯–†Ó.ª)ÜàÙo~x_jfžÓï®ð'dl«ÆMç;vÌe=}‚ÅB¶„ƒ™«ÊÆ?„OÎmLµô1×-Uã7ðEÏý¸ŸtËâ2uì¸F=MßéEõ¯ *»\âjûÚÖiB<Ô½h«a,z¹ +4Û Žhl‚Êq€¢†o½J„$ÛuþyíµW«É³4!n§¸)Ê[ßÌds°¬WJ¶o5Ã2álißìÕMÈ|tܬbÊìrìõ5ÈÜì’5Ÿë”O - ˜ö™ìÄ/ëÔ -¶žöçI»ÇqfÑ"ÁW»³ª£C¹»-!Ö~™V€lY~Y~Y~Y^ÝµËøìï$v§PµkÿCf¶$¨¢-?lǕÈZë}=}Å\],÷BÏu&kSVÅv,Ï0ÐTä7{ùó +à,= ÈÙp3û\ø‘È,ԒÈÇ3LRl·ÑÉÆ×9œ#o0ÜÕm—\Éö•VïUÇ­zï(ŒÈ¸M×öëÛû„s(q·U÷ۜøDÕ8g1m=}uÍèþK½EuDmҞ÷©õ,˜û0¢0á×áWs Û÷†å0ž× €u›^阇ݎ¿@¢m‹÷=MDÖ+qv¨Ý@‰Ú¹ x\ß1Zf7GÑ"þÑðî[¿Å™ŒÞnˆ…ù:Ϻýb|·~†îÙ²ÒBêN=}Á–]?¾’”Ž=M2Ïjß­à—K¢*ò2wáB| 9X“üÖ{ +€ßBxÞ ¹”<¡ +û_#±õãX¥¤w±ëÐƒ˜'#= N¦i¿®KÓÀÂÄZõ(ï˜åm7¬œVkM$Ä8Š'~™b»…_¬ÜµÎE=My+ª\Æ=}ú.|ÄE|‹+žîÆÍÂ+ÛÎe›½ ó¿£{«#Ãû~Y^<ƒwY~Y~Y~YÿÔÐ#’è¦÷Õ¥Oµƒúb£o£†Úª[Ù㕺¤/»sÒ¶OΦÚà+u½U±(-–Duñ ¼Ýa(A&—pÁGhÃM© táë04ä'h䕐°µ’3jÂ%í³Q³Ø%ÄUy«”K+œFæ÷½Å„¡³Þ' —Ìç?ç9‰±¶­(\õÆï6ñ2±v ;že_´‰hjÓضéÛ=}Îå„'”xÆ_Œ³ä¿¸}…«ò™çËÐ垚´yi¶9Ãæè¤= -ãJY¥v*¬>Ò*c6.¥¬ç,ȵ֖¢ÐƵh¸œÿ‡l£4–ÖèYµ<ýGf¶´ˆRë}×µ2.ëO÷ÈýØ3kvö…qç,TNÇmÂ9þƒº,o,Y‡94&ÕºNlw Ò[ïX¨Õ +k§ÒmOÌ6ß)åJ¯­Šîˇٸ]ÚùÛþÄ¡·¨0e“Æ­’Ë_žE3ÄTíÛZíæ÷ùø<ùló8˜øY b +øÙßøü™úý=M0$(Ø=}l½í0Øa1tÛ]çŸÁ0mú×õ_tÛ¶ö +ãé@EÈ»Ý ½Œ‹HVE1UõŠ•¤@}Ú‘= ¢¢†½ÿqÃ:Ô{ ¾G=MsN2dXl©(ù¾—à…Iä52©ñß64üð™8Ô +>¢VÉ$áÉXÊ>ü՟Clw¾É–>Ï/ÔÒVhwõ¹fð.Ñ0Ï=MšW«ïSë^…. úÏé_YS{Çñ_KðhÉ">î@]1Uì\s x¾q|?† Y\;dqŠۋóUMfy±Ô©Ü×?„S@æ5F±ÞÏ ‹“ßQîQÏÑXî\/ØÿÙ\‚pȍû:¯.-¬AôB];b—‡Á4_ÁȔkù|Æî2º ß1ï”;\mFˆ$¢Ø2ÂRàYêš ByÊ +B®%òš½áŒ~œÀq—\VqJ|´BzÚ ý= šµNµxÖ÷… tí·5ÎÔ¡Â&…£Ãç£çt~Y| ~Y~Y~Y€“n³väCmÓ$lV¨‘³kïcbÐӞ&c\»Å"u-ÒÏõF]+R.— ÒÕ+k7<ÅÖy,ïâÏՔ)k3ZÅÀFí LÇmˆ:ëøü…PÇTZ8g“¡µJëgÌRçzîµüBî?ÐtÝTïo¿ÕVnk‡ÃVx:Ìv ~ <%®¹4ìõI–<ÎÂÆW;­&vÍÿÍK3QM%PËoÀdžÎç>Eó(%[êD–¨(Tò·¹¡óûºþ 8ÀhøYwõüMoCÕ+ín¼ÍØzKáC•ŒUm¡ŠÍf!NwëG´È>iY5½ðŽ ÇWØÙw Ó÷†Œ¯0×EÁs»!üƉ0¤b×áÊs”U‹?ËQ6Z9qÓæŠÞ@&]ñx|\办@%ŠêÝa-Ü@d~%©ôM»Ç5¯õXÓëv~ÈÛ&›®°¡Ù!oƒ—ð&ø¯©èÕ45=}l•E7l-ÝÉ$5M&lTäɐJ=}ÇüU:ì˜VÉz‘>[BïAéZ«öüptK_h©¹|ß.Ä@Б²XËáf“Í.Öϵ@ZKšÓ%"Y{µf‰!Í>‹U8ẐcIDU¬*{×@ †QS4|÷ÞöƒAð5²Ù’EÛ¿ð‰ÓÐ*&úmÄú* +]fŸÙ±åâ*k¶¿9ÒKX|„l´*Az¿…_;—vÄèß:lŒ…¹ŒötˆÆ:an=}qÑÔ)] vÿ9ÐZfHsÌ:yZ¹a\“zˆúå2¯<àm¸”³M'%ê*sFþz+À‰t!]êSeF”ñ2ÕJßu€™›™}†oÂ2ܬ=}—lþkŠèèB¨0=}á¢ÝÏ¼‡öUr há@Z›ç܍֕MòŸ +áÆaè©Wò¸Öúã(u¤=M¨ø¦=MY¯;uփyk#V¥‡ø¤F¨ß¸“‹ùã/z¤ü"ª7,ºõÊ+7ÒçÒºTæ§ÆGéŽ3…= æu0±œÞ·iM3^åA­LÎǁN36âf¢¯9¦[¸ô³#V~YŽrYy^ª||Y~Y~ّûBê¯-óË(d ©æ.¾L CLäÆnƒô¦;d~L©‘õ&*þ¯½¿Ô{|Æ&äB¯èÓl6cGò6æœïÅ:ØÌþjGþ6HalœáÉBê<Ç;5HìŒÀÉx1>w= ¹…JÉØa'Æü‡ð 4Ï&hR¹ÚSû~óÎ.}AèE¹:êû/@ô!éÙ0Ý}vÙ,©}ۆpT,U}Iiñ>ôj±Xœ¡{ÉnÊ>}„Àÿ*ÔLÀ¹ˆ{ÿ¸*f0åd2{s‚»*V¿?¸±8ZÜGᅓ™Efqž± ‚Ý“~„ê:Øìý,÷süü:-XX”:€ÿ*\Ÿ>ƒuAn~xÑP]¿Þ‰5†-n“*њ‚\D"lã¯2FìàE™KócFÊÁÎ=Mÿ=M‰”€“ë}hƂã2-àr• aFˆ¹2 ªàA–Ë~ƚ®2'àáè“ܾz +ôÍÂ@€J÷´B¸ŸsžŒ6f™\yxJ¢ÃB—†™o• [Šl ÂcŠ~<ó€ðB2ò oòáú¢œ70¸SÛüãgo¤Ð•ªOÎÀ“JýãHi¤f}ª¿±¥!š­kàÏCœG#¤–¥¹O­{VЃuD#ꊥ­›—ÏgB¥Ÿ®»öÄL +éd_%aȨ0M«UÚ³Ð{Š«©¶Óˇ2´O;Ä _ç“ += äl†%.:§À‘¬= · Í›½EîAÈ6×pÇÜ7ú5PGçlF5PÇfÐlhq5G ÈðéíÉï4¿<Ҝ_í5 >GŸäì^Ö5zȆÁì‰_8ϞÄü¸ìTÅQ­hwo-S­bGEè- иZ¯híHs ·è)ÌáòóO7•~8 ª7‘Ê8¡:7Z8´ò%!N—žI R×,=M70 +íçîßlo/Î wÎ +ÇNRÏ wŒ 猎 +×Âç?ÏB· ÿü W€} ›!†r”CÒ§¢)t‘D˜Hô•6T•:´Ü%vÍ)VÖ$¶ÆLvÓPªAqV_ñ]ñ–UqoMqT]qJIñEñtñæ²é2 ò9qyÏyQù¹I‘)¿Ý@–)h¤}±Ã©EX~Y~þ~Ya€ÇX~Y~YžÂ/*-:¥a¸ÃŸF£š©{›Ôã¢B¤m*ç,ƒÛ¾€ÔÅaF jj1œ|¿–܁ƒ»i4Y-ê8i1~P¿PÝÜ—WŽßkô"7†—çê¡<1(®¿ù§yޙg€ºWލ{i”’UF‡jƒê1 ÒÀâÛu¢w¬‰¼³yV×-Š rFçABÌàôÕµˆ“Gp’lÿw6H/ÊöòMýA›°à„þáZ–|ŽßVž¥æ߸êµò•_?„|ׂ†9•Q˜üTnv“Cʅ¬r9A©úàb ᕇŒÜ™öš>Š”Âòjóež«å:‹en[åVÇeH÷å”/eå/årÅe(5å)­åb=MeœÝå…iåN9em™äbQå½Ê׍Æï»Ç¿»Ä_ÁÊg?͇<ÐwýËѯÎo{ÏÏ~ÌÒâ͏ßÀgº&Ÿœ‰{îD~DqúĂÄñ«ˆûˆë=MïHÈõˆöÍ©HþÙÈÁˆ!äÔ(Õm.-m{íY]m|éí>¹mBYm[qí…AmÕô՚öy°±…4m•í5Ìm'íkžítÊ-_÷yaö‘ ùÅ¢÷•¡ö½(՝(ډçÔYj×qhÖÁÈÙáIÓ¡IØeÙŇÓʼnØe¸Õ%ºÖa8Õ7ÚQøÔ±w×9zÖéØÙÝWÓmWØ5ÕáÖ±ª/, ׋ÇÂßÁϽ×Ag<??‹¥Æõ†ŸíÆSq+Z|{Y€Kajc‹w+m;që|û€kjû Ã‹šw;šu+œq»Ÿl››p ›~Û·OÚ åš ë›ß/>¢¯9¦»¸ã³'V~YŽrY~_@~Y~YYqVò6Ò=}åÏ¢1Šk5{tÒ\†=Mꋯ1uü¿fÛ±‡΃ëúgôZ'¼j)­1ø¿â=Mۅw‡ ‹KswtWHFþjƒ11ÀvÝ šo8bªœ¢Ó22¯½Š˜É  9ZBŠ+Vk”L†íjIX14¾À°ZÝՒŸ|†Û!cÔ¡+F}¬ê/Ö1nŠÀÒÞíÿŸ_ˆû rôšDƇöj}º1M¿B‚ÞÉ"‘"ܽŸAF¾ æ¼„f¼öŽ”F½–»,…»V†¾Ô¶¾5¼äõ<ŠºéÃ&ôÑ.TÜ^´ÊZtÚBVC4N+Ôa[”G74J?ÔUEœA»ì»P¾Ž¼’½½<=}½þ».þ^é'ñédQÁŽï +ß7´34Kô'ôWt?ÔEÔ-T]T9TQT"$ T‚n/¦Î0€/pŽ0¸>/|þ0tÞ/^0 ž/Uª0Ïê/ j0²Ê0 +/8Š0¤:/½ú0wÚ/Z/Æ0¤2àՅàؑßډ_Ս_ØE"Óñ"•Ù0â/êb/¬"0ŸähFƒË4ÄOÛ¶W0Çå踖E.?Tg,ÛE”ßD‹AçŸðÉ)I·„u+Ԍç'/áV«4ð[¿…ÑtÅR[9·ÒÇÙ·¹”-,$ÈgvõEüTE_«üì×QÅù¶4- Xhy]Åç¦ôH4;€çß=MÉuY·þ.XhŽ™ÅðÊtW.;xðÿ‹Ä%š¹|žo,µÞ©+côKzÀÑm/·Pê.ÄhŸ”žç U«ï¸ÂI.ÃVçgÜÅqß4$ëXëGþÅÕϖ(ë^íGÿƵP¶Ôڛ]Ú,â>h~P…qJ{_þoŽ’âËíÀ¹L2-3jèƒ'{íïßÆý@¶œÑ+«:gnhøt]Ëö'[Ðñ´‘,­¤R–ó?Ç)á´(A,ÒrhM:E„´œP{šñW¢Ã"µªâ.yèG’v²ô :›—þïŸÎÕ¨Œr,C  ¿_â‚å— +K¡Ç­vB˜žö‹âš³¨k$¨ó×ñ}Y~Y~Y~Y~Y~ º’ÉŠ×îb=Mž!ªu©y=}©xj Ž(sfŒ >*ye +þ(vŒ +>éwn ét´ .é’9J9ÙX9 GAYþߐ Ðå^2¶|?ç6;÷JRpNR@6õn=}ýÎ;iá†W™¯JZ¾Û&Àk~c< !¹ðù@Ù"ÞÅJˆu¼‰T>ÛRáØBQØbéUCáS3_(CÍê–˜8z†±ÎD"ÐLzvˆÉÏFNÏN ÏzÐ~ ‘­Qúì¶üüªýèöûx”xßrþf˜ÜIYQX1 +ONÞOJw‘Ñ=MZU!šÆrt{Ÿ=}‹ü>Œ÷>^Ҟ=}¶Zw$YU>ür>^0ùvþG@9ôzþ_¦ÀpM6 j@àöº^*^=M^n9ÿŠ¾Z*ñ\JžH²wžáŠýÂÐ ֌ +Q ØFâZÀ ٝ‚ÿ&X<½ƒãæ€Öê) Ú*BÆ_Z4¡7iü¶Ÿ9Æ¿R°‘Óq /ÚÆ “®ïwGñalA-q4„¡v–ž4Øf:;Ùv¿WfT +>ÀV‰›˜ÿŠ<Šr¼Ö‚‚CZxµHpŊV­fhÜ:!;\o¯9ÇüSåî_õÆl_ø1<_iÿy]± zi˜aúӎ0QðYðQî>p‰HäAî9P®°YÜâfZØlNlPîáèð•ÖݐR‡´ç}Ž?ÇuÌRÇ != Ôܱ›!kóŽíaæ!Ç2!º|2 &ìNäeA2+j>‚ñ[±rKLáæö÷…Œ>j•.i\ÈúÌKæ—ýõ„_’0º×R_C/™à4ќÑ\å¹À—·áô„-³M;&gy舿lWt \åM¾Qh¸õ‹Gž1§ýŒ1¿Å5 ]ľ› 1Ù?¼ßÄ= !]ºê«JFQqA4ùò€(ŸDLÑoñ¶ñþ$,ÿÅUi]Õ6[ëPüé/ãòoBA‰ÐlWz˜Ç›cÒ¶=M®Ù—¤^·±º±ùô>né7i/9Z•çÉüô3à³ð_Ãóö˽÷PÖ]÷ÃÝ¡ ßýyÄ¿ÉiF”@ Âý0eèS¡¤7aâ¬= ›A$weZó‚šr4…ÚWT$-‚؅RnpÛvj¶ÚXŒÁ+= :«ß‰ù«›_¶qn¿#†:0jž*†· +ßA»ŒgR¯Ní= P{SÂò>9'¢ÛA$ +Š™ŠÛ"eÚ?ùèÈQ|éõÓmï- –ànüìŠj¤‡ñÒä?NåkZp@돣Õ]= Û¼ÔßAç5ݚú´†þksž¬7ç=MrqÎF5^&A6ژa4Zâ·}OV49IqsÆrÐqQ£]t͋\aÄWfƒñ×Ul£ÝwÛAMre 0é”ð Ê@T +¸À3= (QJH5‹ƒi¼wp!6©ø"ñâaŸí= ÑÎOKB4¨÷ÕVÀîàûœæ¹í£>œMath.atan(t),a:t=>Math.cos(t),d:t=>Math.exp(t),e:t=>Math.log(t),f:(t,s)=>Math.pow(t,s),c:t=>Math.sin(t),g:(t,s,i)=>r.copyWithin(t,s,s+i),h(t){r.length,(()=>{throw"OOM"})()}}};this.u=s=>{t.u(pe,s)},this.U=()=>t.U(pe),this.instantiate=()=>(this.U().then((t=>WebAssembly.instantiate(t,d))).then((t=>{const i=t.exports;var e;h=i.k,o=i.l,a=i.m,l=i.n,c=i.o,u=i.p,U=i.q,e=(n=i.i).buffer,r=new Uint8Array(e),(t=>{t.j()})(i),s()})),this.ready=new Promise((t=>{s=t})).then((()=>{this.I=n.buffer,this.V=o,this.free=U,this.Fs=h,this._s=a,this.Vs=l,this.ks=c,this.Ss=u})),this)}function Me(){return this.qs=()=>(new this.Hs).instantiate(this.$s,this.Cs).then((t=>{this.Is=t,this.js=this.Is._(this.Js,Uint8Array),this.xs=!0,this.Ps=this.Is._(1,Uint32Array),this.Ds=this.Is._(1,Uint32Array),this.Es=this.Is._(1,Uint32Array),this.Os=this.Is._(1,Uint32Array),this.Ns=this.Is._(1,Uint32Array),this.Zs=this.Is._(256,Uint32Array),this.zs=this.Is._(1,Int32Array),this.gs=0,this.Ks=0,this.Qs=0,this.Rs=this.Is.M.Fs(this.js.H,this.Ps.H,this.Ds.H,this.Es.H,this.Os.H,this.Ns.H,this.Zs.H,this.zs.H,256)})),Object.defineProperty(this,"ready",{enumerable:!0,get:()=>this.Gs}),this.reset=()=>(this.free(),this.qs()),this.free=()=>{this.Is.M.Ss(this.Rs),this.Is.free()},this.sendSetupHeader=t=>{this.js.C.set(t),this.Ps.C[0]=t.length,this.Is.M._s(this.Rs,this.xs),this.xs=!1},this.initDsp=()=>{this.Is.M.Vs(this.Rs)},this.decodePackets=t=>{let s=[],i=0,e=[];for(let r=0;r{if("vorbis"!==t)throw Error("@wasm-audio-decoders/ogg-vorbis does not support this codec "+t)},new r,this.qs(),this[ye](Me)}qs(){this.Ls=!0,this.Ws=0,this.ut=new oe("audio/ogg",{os:this.zt,Us:!1})}[ye](t){if(this.Rs){const t=this.Rs;t.ready.then((()=>t.free()))}this.Rs=new t,this.Gs=this.Rs.ready}get ready(){return this.Gs}async reset(){return this.qs(),this.Rs.reset()}free(){this.Rs.free()}async ti(t){const s=[];for(let r=0;rt[le])))}const i=await this.Rs.decodePackets(s);this.Ws+=i.samplesDecoded;const e=t[t.length-1];if(e&&e[ue]){const t=this.Ws-e[fe];if(t>0){for(let s=0;s>> 0; - [arr[n], arr[random]] = [arr[random], arr[n]]; - } - return arr; - } - music.addEventListener("mousedown", function (e) { - e.stopPropagation(); - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor((e.clientX - left) / core.domStyle.scale), - py = Math.floor((e.clientY - top) / core.domStyle.scale); - core.ui.music.mousedown(px * 3, py * 3); - }); - music.addEventListener("mousemove", function (e) { - e.stopPropagation(); - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor((e.clientX - left) / core.domStyle.scale), - py = Math.floor((e.clientY - top) / core.domStyle.scale); - core.ui.music.mousemove(px * 3, py * 3); - }); - music.addEventListener("mouseup", function (e) { - e.stopPropagation(); - ischange = false; - isvolume = false; - if (!main.core.ui.music.stop) audio.play(); - }); - music.addEventListener("mouseleave", function (e) { - e.stopPropagation(); - ischange = false; - isvolume = false; - if (!main.core.ui.music.stop) audio.play(); - }); - music.addEventListener("touchstart", function (e) { - e.preventDefault(); - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor( - (e.touches[0].clientX - left) / core.domStyle.scale - ), - py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale); - core.ui.music.mousedown(px * 3, py * 3); - }); - music.addEventListener("touchmove", function (e) { - e.stopPropagation(); - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor( - (e.touches[0].clientX - left) / core.domStyle.scale - ), - py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale); - core.ui.music.mousemove(px * 3, py * 3); - }); - music.addEventListener("touchend", function (e) { - e.stopPropagation(); - ischange = false; - isvolume = false; - if (!main.core.ui.music.stop) audio.play(); - }); - music.addEventListener("touchcancel", function (e) { - e.stopPropagation(); - ischange = false; - isvolume = false; - }); + function shuffle(arr) { + let n = arr.length, + random; + while (n) { + random = (Math.random() * n--) >>> 0; + [arr[n], arr[random]] = [arr[random], arr[n]]; + } + return arr; + } + music.addEventListener("mousedown", function (e) { + e.stopPropagation(); + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor((e.clientX - left) / core.domStyle.scale), + py = Math.floor((e.clientY - top) / core.domStyle.scale); + core.ui.music.mousedown(px * 3, py * 3); + }); + music.addEventListener("mousemove", function (e) { + e.stopPropagation(); + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor((e.clientX - left) / core.domStyle.scale), + py = Math.floor((e.clientY - top) / core.domStyle.scale); + core.ui.music.mousemove(px * 3, py * 3); + }); + music.addEventListener("mouseup", function (e) { + e.stopPropagation(); + ischange = false; + isvolume = false; + if (!main.core.ui.music.stop) audio.play(); + }); + music.addEventListener("mouseleave", function (e) { + e.stopPropagation(); + ischange = false; + isvolume = false; + if (!main.core.ui.music.stop) audio.play(); + }); + music.addEventListener("touchstart", function (e) { + e.preventDefault(); + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor( + (e.touches[0].clientX - left) / core.domStyle.scale + ), + py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale); + core.ui.music.mousedown(px * 3, py * 3); + }); + music.addEventListener("touchmove", function (e) { + e.stopPropagation(); + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor( + (e.touches[0].clientX - left) / core.domStyle.scale + ), + py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale); + core.ui.music.mousemove(px * 3, py * 3); + }); + music.addEventListener("touchend", function (e) { + e.stopPropagation(); + ischange = false; + isvolume = false; + if (!main.core.ui.music.stop) audio.play(); + }); + music.addEventListener("touchcancel", function (e) { + e.stopPropagation(); + ischange = false; + isvolume = false; + }); - audio.addEventListener("ended", function () { - switch (main.core.ui.music.type) { - case "danqu": - audio.currentTime = 0; - if (!main.core.ui.music.stop) audio.play(); - main.core.ui.music.stop = false; - page = main.core.ui.music.selection[0]; + audio.addEventListener("ended", function () { + switch (main.core.ui.music.type) { + case "danqu": + audio.currentTime = 0; + if (!main.core.ui.music.stop) audio.play(); + main.core.ui.music.stop = false; + page = main.core.ui.music.selection[0]; - break; - case "xunhuan": - if ( - main.core.ui.music.selection[1] === - main.core.ui.music.musicMx[main.core.ui.music.selection[0]].length - - 1 - ) { - if ( - main.core.ui.music.selection[0] === - main.core.ui.music.musicMx.length - 1 - ) { - main.core.ui.music.selection[0] = 0; - main.core.ui.music.selection[1] = 0; - } else { - main.core.ui.music.selection[0] += 1; - main.core.ui.music.selection[1] = 0; - } - } else { - main.core.ui.music.selection[1] += 1; - } - main.core.ui.music.randomList.indexOf( - (v) => - v === - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ] - ); - page = main.core.ui.music.selection[0]; - audio.src = - "project/bgms/" + - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ]; + break; + case "xunhuan": + if ( + main.core.ui.music.selection[1] === + main.core.ui.music.musicMx[main.core.ui.music.selection[0]].length - + 1 + ) { + if ( + main.core.ui.music.selection[0] === + main.core.ui.music.musicMx.length - 1 + ) { + main.core.ui.music.selection[0] = 0; + main.core.ui.music.selection[1] = 0; + } else { + main.core.ui.music.selection[0] += 1; + main.core.ui.music.selection[1] = 0; + } + } else { + main.core.ui.music.selection[1] += 1; + } + main.core.ui.music.randomList.indexOf( + (v) => + v === + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); + page = main.core.ui.music.selection[0]; + audio.src = + "project/bgms/" + + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ]; - if (!main.core.ui.music.stop) audio.play(); - main.core.ui.music.stop = false; - break; - case "suiji": - if ( - main.core.ui.music.random < - main.core.ui.music.randomList.length - 1 - ) { - main.core.ui.music.random += 1; - } else { - main.core.ui.music.random = 0; - } - main.core.ui.music.selection[0] = - main.core.ui.music.musicMx.findIndex((v) => - v.includes( - main.core.ui.music.randomList[main.core.ui.music.random] - ) - ); - main.core.ui.music.selection[1] = main.core.ui.music.musicMx[ - main.core.ui.music.selection[0] - ].indexOf(main.core.ui.music.randomList[main.core.ui.music.random]); - audio.src = - "project/bgms/" + - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ]; - page = main.core.ui.music.selection[0]; + if (!main.core.ui.music.stop) audio.play(); + main.core.ui.music.stop = false; + break; + case "suiji": + if ( + main.core.ui.music.random < + main.core.ui.music.randomList.length - 1 + ) { + main.core.ui.music.random += 1; + } else { + main.core.ui.music.random = 0; + } + main.core.ui.music.selection[0] = + main.core.ui.music.musicMx.findIndex((v) => + v.includes( + main.core.ui.music.randomList[main.core.ui.music.random] + ) + ); + main.core.ui.music.selection[1] = main.core.ui.music.musicMx[ + main.core.ui.music.selection[0] + ].indexOf(main.core.ui.music.randomList[main.core.ui.music.random]); + audio.src = + "project/bgms/" + + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ]; + page = main.core.ui.music.selection[0]; - if (!main.core.ui.music.stop) audio.play(); - main.core.ui.music.stop = false; - break; - } - }); + if (!main.core.ui.music.stop) audio.play(); + main.core.ui.music.stop = false; + break; + } + }); - class musicclass { - constructor() { - //music列表 - //需全塔属性注册并保存在bgms文件夹,每个数组为显示的一页内容 - this.musicMx = [ - ["Asphodelus_Ceui.mp3", "Blind_Alley.mp3"], - ["Crawler.mp3", "op.mp3", "theme.mp3"], - ]; - //音乐别名(将在播放器内显示的音乐名,music列表内的都要有对应歌名) - this.musicname = { - "Asphodelus_Ceui.mp3": "Asphodelus", - "Blind_Alley.mp3": "Blind", - "Crawler.mp3": "Crawler", - "op.mp3": "op", - "theme.mp3": "theme", - }; - this.selection = [0, 0]; - this.stop = false; - this.type = "xunhuan"; - this.randomList = []; - this.random = 0; - } + class musicclass { + constructor() { + //music列表 + //需全塔属性注册并保存在bgms文件夹,每个数组为显示的一页内容 + this.musicMx = [ + ["Asphodelus_Ceui.mp3", "Blind_Alley.mp3"], + ["Crawler.mp3", "op.mp3", "theme.mp3"], + ]; + //音乐别名(将在播放器内显示的音乐名,music列表内的都要有对应歌名) + this.musicname = { + "Asphodelus_Ceui.mp3": "Asphodelus", + "Blind_Alley.mp3": "Blind", + "Crawler.mp3": "Crawler", + "op.mp3": "op", + "theme.mp3": "theme", + }; + this.selection = [0, 0]; + this.stop = false; + this.type = "xunhuan"; + this.randomList = []; + this.random = 0; + } - //更新 - update() { - this.background(); - this.drawUI(); - } - background() { - //画布大小设置 - if (core.domStyle.isVertical) { - music.width = 1248; - music.height = 2028; - } else { - music.width = 2028; - music.height = 1248; - } - } - mousedown(px, py) { - //鼠标按下时 - //console.log(px, py) - const makeBox = ([x, y], [w, h]) => { - return [ - [x, y], - [x + w, y + h], - ]; - }; - const inRect = ([x, y], [[sx, sy], [dx, dy]]) => { - return sx <= x && x <= dx && sy <= y && y <= dy; - }; - const pos = [px, py]; - const backbox = makeBox([15, 35], [210, 90]); - if (inRect(pos, backbox)) { - //离开按钮是一致的,其余的记区分横竖屏 - music.style.display = "none"; - core.clearMap(ctx); - core.unregisterAnimationFrame("music"); - audio.src = ""; - core.restart(); + //更新 + update() { + this.background(); + this.drawUI(); + } + background() { + //画布大小设置 + if (core.domStyle.isVertical) { + music.width = 1248; + music.height = 2028; + } else { + music.width = 2028; + music.height = 1248; + } + } + mousedown(px, py) { + //鼠标按下时 + //console.log(px, py) + const makeBox = ([x, y], [w, h]) => { + return [ + [x, y], + [x + w, y + h], + ]; + }; + const inRect = ([x, y], [ + [sx, sy], + [dx, dy] + ]) => { + return sx <= x && x <= dx && sy <= y && y <= dy; + }; + const pos = [px, py]; + const backbox = makeBox([15, 35], [210, 90]); + if (inRect(pos, backbox)) { + //离开按钮是一致的,其余的记区分横竖屏 + music.style.display = "none"; + core.clearMap(ctx); + core.unregisterAnimationFrame("music"); + audio.src = ""; + core.restart(); - return; - } - if (core.domStyle.isVertical) { - //竖屏 + return; + } + if (core.domStyle.isVertical) { + //竖屏 - const pageupbox = makeBox([100, 1230], [200, 100]); - const pagedownbox = makeBox([950, 1230], [200, 100]); - const musicbox = makeBox( - [100, 200], - [1048, this.musicMx[page].length * 100] - ); - const beforebox = makeBox([120, 1720], [100, 100]); - const afterbox = makeBox([780, 1720], [100, 100]); - const playbox = makeBox([420, 1680], [200, 200]); - const typebox = makeBox([1040, 1700], [120, 120]); - const changebox = makeBox([100, 1590], [1048, 20]); - const volumebox = makeBox([250, 1940], [1050, 20]); - if (inRect(pos, pageupbox)) { - if (page !== 0) page -= 1; - return; - } - if (inRect(pos, pagedownbox)) { - if (page !== this.musicMx.length - 1) page += 1; - return; - } - if (inRect(pos, playbox)) { - if (this.stop) { - this.stop = !this.stop; - audio.play(); - } else { - this.stop = !this.stop; - audio.pause(); - } - return; - } - if (inRect(pos, beforebox)) { - switch (this.type) { - case "danqu": - audio.currentTime = 0; - if (!this.stop) audio.play(); - this.stop = false; - page = this.selection[0]; + const pageupbox = makeBox([100, 1230], [200, 100]); + const pagedownbox = makeBox([950, 1230], [200, 100]); + const musicbox = makeBox( + [100, 200], + [1048, this.musicMx[page].length * 100] + ); + const beforebox = makeBox([120, 1720], [100, 100]); + const afterbox = makeBox([780, 1720], [100, 100]); + const playbox = makeBox([420, 1680], [200, 200]); + const typebox = makeBox([1040, 1700], [120, 120]); + const changebox = makeBox([100, 1590], [1048, 20]); + const volumebox = makeBox([250, 1940], [1050, 20]); + if (inRect(pos, pageupbox)) { + if (page !== 0) page -= 1; + return; + } + if (inRect(pos, pagedownbox)) { + if (page !== this.musicMx.length - 1) page += 1; + return; + } + if (inRect(pos, playbox)) { + if (this.stop) { + this.stop = !this.stop; + audio.play(); + } else { + this.stop = !this.stop; + audio.pause(); + } + return; + } + if (inRect(pos, beforebox)) { + switch (this.type) { + case "danqu": + audio.currentTime = 0; + if (!this.stop) audio.play(); + this.stop = false; + page = this.selection[0]; - break; - case "xunhuan": - if (this.selection[1] === 0) { - if (this.selection[0] === 0) { - this.selection[0] = this.musicMx.length - 1; - this.selection[1] = - this.musicMx[this.selection[0]].length - 1; - } else { - this.selection[0] -= 1; - this.selection[1] = - this.musicMx[this.selection[0]].length - 1; - } - } else { - this.selection[1] -= 1; - } - this.randomList.indexOf( - this.musicMx[this.selection[0]][this.selection[1]] - ); - page = this.selection[0]; - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + break; + case "xunhuan": + if (this.selection[1] === 0) { + if (this.selection[0] === 0) { + this.selection[0] = this.musicMx.length - 1; + this.selection[1] = + this.musicMx[this.selection[0]].length - 1; + } else { + this.selection[0] -= 1; + this.selection[1] = + this.musicMx[this.selection[0]].length - 1; + } + } else { + this.selection[1] -= 1; + } + this.randomList.indexOf( + this.musicMx[this.selection[0]][this.selection[1]] + ); + page = this.selection[0]; + audio.src = + "project/bgms/" + + this.musicMx[this.selection[0]][this.selection[1]]; - if (!this.stop) audio.play(); - this.stop = false; - break; - case "suiji": - if (this.random > 0) { - this.random -= 1; - } else { - this.random = this.randomList.length - 1; - } - this.selection[0] = this.musicMx.findIndex((v) => - v.includes(this.randomList[this.random]) - ); - this.selection[1] = this.musicMx[this.selection[0]].indexOf( - this.randomList[this.random] - ); - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; - page = this.selection[0]; + if (!this.stop) audio.play(); + this.stop = false; + break; + case "suiji": + if (this.random > 0) { + this.random -= 1; + } else { + this.random = this.randomList.length - 1; + } + this.selection[0] = this.musicMx.findIndex((v) => + v.includes(this.randomList[this.random]) + ); + this.selection[1] = this.musicMx[this.selection[0]].indexOf( + this.randomList[this.random] + ); + audio.src = + "project/bgms/" + + this.musicMx[this.selection[0]][this.selection[1]]; + page = this.selection[0]; - if (!this.stop) audio.play(); - this.stop = false; - break; - } - return; - } - if (inRect(pos, afterbox)) { - switch (this.type) { - case "danqu": - audio.currentTime = 0; - if (!this.stop) audio.play(); - this.stop = false; - page = this.selection[0]; - break; - case "xunhuan": - if ( - this.selection[1] === - this.musicMx[this.selection[0]].length - 1 - ) { - if (this.selection[0] === this.musicMx.length - 1) { - this.selection[0] = 0; - this.selection[1] = 0; - } else { - this.selection[0] += 1; - this.selection[1] = 0; - } - } else { - this.selection[1] += 1; - } - this.random = this.randomList.indexOf( - this.musicMx[this.selection[0]][this.selection[1]] - ); - page = this.selection[0]; - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + if (!this.stop) audio.play(); + this.stop = false; + break; + } + return; + } + if (inRect(pos, afterbox)) { + switch (this.type) { + case "danqu": + audio.currentTime = 0; + if (!this.stop) audio.play(); + this.stop = false; + page = this.selection[0]; + break; + case "xunhuan": + if ( + this.selection[1] === + this.musicMx[this.selection[0]].length - 1 + ) { + if (this.selection[0] === this.musicMx.length - 1) { + this.selection[0] = 0; + this.selection[1] = 0; + } else { + this.selection[0] += 1; + this.selection[1] = 0; + } + } else { + this.selection[1] += 1; + } + this.random = this.randomList.indexOf( + this.musicMx[this.selection[0]][this.selection[1]] + ); + page = this.selection[0]; + audio.src = + "project/bgms/" + + this.musicMx[this.selection[0]][this.selection[1]]; - if (!this.stop) audio.play(); - this.stop = false; - break; - case "suiji": - if (this.random < this.randomList.length - 1) { - this.random += 1; - } else { - this.random = 0; - } - this.selection[0] = this.musicMx.findIndex((v) => - v.includes(this.randomList[this.random]) - ); - this.selection[1] = this.musicMx[this.selection[0]].indexOf( - this.randomList[this.random] - ); - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + if (!this.stop) audio.play(); + this.stop = false; + break; + case "suiji": + if (this.random < this.randomList.length - 1) { + this.random += 1; + } else { + this.random = 0; + } + this.selection[0] = this.musicMx.findIndex((v) => + v.includes(this.randomList[this.random]) + ); + this.selection[1] = this.musicMx[this.selection[0]].indexOf( + this.randomList[this.random] + ); + audio.src = + "project/bgms/" + + this.musicMx[this.selection[0]][this.selection[1]]; - page = this.selection[0]; - if (!this.stop) audio.play(); - this.stop = false; - break; - } - return; - } - if (inRect(pos, typebox)) { - switch (this.type) { - case "danqu": - this.type = "xunhuan"; - break; - case "xunhuan": - this.type = "suiji"; - break; - case "suiji": - this.type = "danqu"; - break; - } - return; - } - if (inRect(pos, musicbox)) { - const index = Math.floor((py - 200) / 100); - if (page !== this.selection[0] || index !== this.selection[1]) { - this.selection[0] = page; - this.selection[1] = index; - this.randomList.indexOf( - this.musicMx[this.selection[0]][this.selection[1]] - ); - audio.src = "project/bgms/" + this.musicMx[page][index]; + page = this.selection[0]; + if (!this.stop) audio.play(); + this.stop = false; + break; + } + return; + } + if (inRect(pos, typebox)) { + switch (this.type) { + case "danqu": + this.type = "xunhuan"; + break; + case "xunhuan": + this.type = "suiji"; + break; + case "suiji": + this.type = "danqu"; + break; + } + return; + } + if (inRect(pos, musicbox)) { + const index = Math.floor((py - 200) / 100); + if (page !== this.selection[0] || index !== this.selection[1]) { + this.selection[0] = page; + this.selection[1] = index; + this.randomList.indexOf( + this.musicMx[this.selection[0]][this.selection[1]] + ); + audio.src = "project/bgms/" + this.musicMx[page][index]; - if (!this.stop) audio.play(); - this.stop = false; - } else { - if (this.stop) { - this.stop = !this.stop; - audio.play(); - } else { - this.stop = !this.stop; - audio.pause(); - } - } - return; - } - if (inRect(pos, changebox)) { - const time = Math.floor(((px - 100) / 1000) * audio.duration); + if (!this.stop) audio.play(); + this.stop = false; + } else { + if (this.stop) { + this.stop = !this.stop; + audio.play(); + } else { + this.stop = !this.stop; + audio.pause(); + } + } + return; + } + if (inRect(pos, changebox)) { + const time = Math.floor(((px - 100) / 1000) * audio.duration); - audio.pause(); - audio.currentTime = time; + audio.pause(); + audio.currentTime = time; - ischange = true; - } - if (inRect(pos, volumebox)) { - const time = Math.min(Math.max((px - 250) / 800, 0), 1); - audio.volume = time; - isvolume = true; - } - } else { - //横屏 - const pageupbox = makeBox([1050, 1100], [200, 100]); - const pagedownbox = makeBox([1550, 1100], [200, 100]); - const musicbox = makeBox( - [900, 100], - [1000, this.musicMx[page].length * 100] - ); - const beforebox = makeBox([135, 740], [50, 50]); - const afterbox = makeBox([450, 740], [50, 50]); - const playbox = makeBox([250, 700], [200, 200]); - const typebox = makeBox([600, 700], [100, 100]); - const changebox = makeBox([100, 590], [600, 20]); - const volumebox = makeBox([100, 990], [600, 20]); - if (inRect(pos, pageupbox)) { - if (page !== 0) page -= 1; - return; - } - if (inRect(pos, pagedownbox)) { - if (page !== this.musicMx.length - 1) page += 1; - return; - } - if (inRect(pos, playbox)) { - if (this.stop) { - this.stop = !this.stop; - audio.play(); - } else { - this.stop = !this.stop; - audio.pause(); - } - return; - } - if (inRect(pos, beforebox)) { - switch (this.type) { - case "danqu": - audio.currentTime = 0; - if (!this.stop) audio.play(); - this.stop = false; - page = this.selection[0]; + ischange = true; + } + if (inRect(pos, volumebox)) { + const time = Math.min(Math.max((px - 250) / 800, 0), 1); + audio.volume = time; + isvolume = true; + } + } else { + //横屏 + const pageupbox = makeBox([1050, 1100], [200, 100]); + const pagedownbox = makeBox([1550, 1100], [200, 100]); + const musicbox = makeBox( + [900, 100], + [1000, this.musicMx[page].length * 100] + ); + const beforebox = makeBox([135, 740], [50, 50]); + const afterbox = makeBox([450, 740], [50, 50]); + const playbox = makeBox([250, 700], [200, 200]); + const typebox = makeBox([600, 700], [100, 100]); + const changebox = makeBox([100, 590], [600, 20]); + const volumebox = makeBox([100, 990], [600, 20]); + if (inRect(pos, pageupbox)) { + if (page !== 0) page -= 1; + return; + } + if (inRect(pos, pagedownbox)) { + if (page !== this.musicMx.length - 1) page += 1; + return; + } + if (inRect(pos, playbox)) { + if (this.stop) { + this.stop = !this.stop; + audio.play(); + } else { + this.stop = !this.stop; + audio.pause(); + } + return; + } + if (inRect(pos, beforebox)) { + switch (this.type) { + case "danqu": + audio.currentTime = 0; + if (!this.stop) audio.play(); + this.stop = false; + page = this.selection[0]; - break; - case "xunhuan": - if (this.selection[1] === 0) { - if (this.selection[0] === 0) { - this.selection[0] = this.musicMx.length - 1; - this.selection[1] = - this.musicMx[this.selection[0]].length - 1; - } else { - this.selection[0] -= 1; - this.selection[1] = - this.musicMx[this.selection[0]].length - 1; - } - } else { - this.selection[1] -= 1; - } - this.indexOf( - this.musicMx[this.selection[0]][this.selection[1]] - ); - page = this.selection[0]; - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + break; + case "xunhuan": + if (this.selection[1] === 0) { + if (this.selection[0] === 0) { + this.selection[0] = this.musicMx.length - 1; + this.selection[1] = + this.musicMx[this.selection[0]].length - 1; + } else { + this.selection[0] -= 1; + this.selection[1] = + this.musicMx[this.selection[0]].length - 1; + } + } else { + this.selection[1] -= 1; + } + this.random = this.randomList.indexOf( + this.musicMx[this.selection[0]][this.selection[1]] + ); + page = this.selection[0]; + audio.src = + "project/bgms/" + + this.musicMx[this.selection[0]][this.selection[1]]; - if (!this.stop) audio.play(); - this.stop = false; - break; - case "suiji": - if (this.random > 0) { - this.random -= 1; - } else { - this.random = this.randomList.length - 1; - } - this.selection[0] = this.musicMx.findIndex((v) => - v.includes(this.randomList[this.random]) - ); - this.selection[1] = this.musicMx[this.selection[0]].indexOf( - this.randomList[this.random] - ); - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; - page = this.selection[0]; + if (!this.stop) audio.play(); + this.stop = false; + break; + case "suiji": + if (this.random > 0) { + this.random -= 1; + } else { + this.random = this.randomList.length - 1; + } + this.selection[0] = this.musicMx.findIndex((v) => + v.includes(this.randomList[this.random]) + ); + this.selection[1] = this.musicMx[this.selection[0]].indexOf( + this.randomList[this.random] + ); + audio.src = + "project/bgms/" + + this.musicMx[this.selection[0]][this.selection[1]]; + page = this.selection[0]; - if (!this.stop) audio.play(); - this.stop = false; - break; - } - return; - } - if (inRect(pos, afterbox)) { - switch (this.type) { - case "danqu": - audio.currentTime = 0; - if (!this.stop) audio.play(); - this.stop = false; - page = this.selection[0]; - break; - case "xunhuan": - if ( - this.selection[1] === - this.musicMx[this.selection[0]].length - 1 - ) { - if (this.selection[0] === this.musicMx.length - 1) { - this.selection[0] = 0; - this.selection[1] = 0; - } else { - this.selection[0] += 1; - this.selection[1] = 0; - } - } else { - this.selection[1] += 1; - } - this.randomList.findIndex( - (v) => - v === this.musicMx[this.selection[0]][this.selection[1]] - ); - page = this.selection[0]; - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + if (!this.stop) audio.play(); + this.stop = false; + break; + } + return; + } + if (inRect(pos, afterbox)) { + switch (this.type) { + case "danqu": + audio.currentTime = 0; + if (!this.stop) audio.play(); + this.stop = false; + page = this.selection[0]; + break; + case "xunhuan": + if ( + this.selection[1] === + this.musicMx[this.selection[0]].length - 1 + ) { + if (this.selection[0] === this.musicMx.length - 1) { + this.selection[0] = 0; + this.selection[1] = 0; + } else { + this.selection[0] += 1; + this.selection[1] = 0; + } + } else { + this.selection[1] += 1; + } + this.randomList.findIndex( + (v) => + v === this.musicMx[this.selection[0]][this.selection[1]] + ); + page = this.selection[0]; + audio.src = + "project/bgms/" + + this.musicMx[this.selection[0]][this.selection[1]]; - if (!this.stop) audio.play(); - this.stop = false; - break; - case "suiji": - if (this.random < this.randomList.length - 1) { - this.random += 1; - } else { - this.random = 0; - } - this.selection[0] = this.musicMx.findIndex((v) => - v.includes(this.randomList[this.random]) - ); - this.selection[1] = this.musicMx[this.selection[0]].indexOf( - main.core.ui.music.randomList[main.core.ui.music.random] - ); - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + if (!this.stop) audio.play(); + this.stop = false; + break; + case "suiji": + if (this.random < this.randomList.length - 1) { + this.random += 1; + } else { + this.random = 0; + } + this.selection[0] = this.musicMx.findIndex((v) => + v.includes(this.randomList[this.random]) + ); + this.selection[1] = this.musicMx[this.selection[0]].indexOf( + main.core.ui.music.randomList[main.core.ui.music.random] + ); + audio.src = + "project/bgms/" + + this.musicMx[this.selection[0]][this.selection[1]]; - page = this.selection[0]; - if (!this.stop) audio.play(); - this.stop = false; - break; - } - return; - } - if (inRect(pos, typebox)) { - switch (this.type) { - case "danqu": - this.type = "xunhuan"; - break; - case "xunhuan": - this.type = "suiji"; - break; - case "suiji": - this.type = "danqu"; - break; - } - return; - } - if (inRect(pos, musicbox)) { - const index = Math.floor((py - 100) / 100); - if (page !== this.selection[0] || index !== this.selection[1]) { - this.selection[0] = page; - this.selection[1] = index; - this.randomList.indexOf( - (v) => v === this.musicMx[this.selection[0]][this.selection[1]] - ); - audio.src = "project/bgms/" + this.musicMx[page][index]; + page = this.selection[0]; + if (!this.stop) audio.play(); + this.stop = false; + break; + } + return; + } + if (inRect(pos, typebox)) { + switch (this.type) { + case "danqu": + this.type = "xunhuan"; + break; + case "xunhuan": + this.type = "suiji"; + break; + case "suiji": + this.type = "danqu"; + break; + } + return; + } + if (inRect(pos, musicbox)) { + const index = Math.floor((py - 100) / 100); + if (page !== this.selection[0] || index !== this.selection[1]) { + this.selection[0] = page; + this.selection[1] = index; + this.randomList.indexOf( + (v) => v === this.musicMx[this.selection[0]][this.selection[1]] + ); + audio.src = "project/bgms/" + this.musicMx[page][index]; - if (!this.stop) audio.play(); - this.stop = false; - } else { - if (this.stop) { - this.stop = !this.stop; - audio.play(); - } else { - this.stop = !this.stop; - audio.pause(); - } - } - return; - } - if (inRect(pos, changebox)) { - const time = Math.floor(((px - 100) / 600) * audio.duration); + if (!this.stop) audio.play(); + this.stop = false; + } else { + if (this.stop) { + this.stop = !this.stop; + audio.play(); + } else { + this.stop = !this.stop; + audio.pause(); + } + } + return; + } + if (inRect(pos, changebox)) { + const time = Math.floor(((px - 100) / 600) * audio.duration); - audio.pause(); - audio.currentTime = time; + audio.pause(); + audio.currentTime = time; - ischange = true; - } - if (inRect(pos, volumebox)) { - const time = Math.min(Math.max((px - 100) / 600, 0), 1); - audio.volume = time; - isvolume = true; - } - } - } - mousemove(px, py) { - if (ischange) { - if (core.domStyle.isVertical) { - const time = Math.min( - Math.max(Math.floor(((px - 100) / 600) * audio.duration), 0), - audio.duration - ); + ischange = true; + } + if (inRect(pos, volumebox)) { + const time = Math.min(Math.max((px - 100) / 600, 0), 1); + audio.volume = time; + isvolume = true; + } + } + } + mousemove(px, py) { + if (ischange) { + if (core.domStyle.isVertical) { + const time = Math.min( + Math.max(Math.floor(((px - 100) / 600) * audio.duration), 0), + audio.duration + ); - audio.currentTime = time; - } else { - const time = Math.min( - Math.max(Math.floor(((px - 100) / 600) * audio.duration), 0), - audio.duration - ); + audio.currentTime = time; + } else { + const time = Math.min( + Math.max(Math.floor(((px - 100) / 600) * audio.duration), 0), + audio.duration + ); - audio.currentTime = time; - } - } - if (isvolume) { - if (core.domStyle.isVertical) { - const time = Math.min(Math.max((px - 250) / 800, 0), 1); - audio.volume = time; - } else { - const time = Math.min(Math.max((px - 100) / 600, 0), 1); - audio.volume = time; - } - } - } + audio.currentTime = time; + } + } + if (isvolume) { + if (core.domStyle.isVertical) { + const time = Math.min(Math.max((px - 250) / 800, 0), 1); + audio.volume = time; + } else { + const time = Math.min(Math.max((px - 100) / 600, 0), 1); + audio.volume = time; + } + } + } - drawUI() { - //绘制页面 - core.clearMap(music); - const bgVertical = core.material.images.images["bg_2010.webp"]; //竖屏背景 - const bg = core.material.images.images["bg_5043.webp"]; //竖屏背景 - if (core.domStyle.isVertical) { - //竖屏 + drawUI() { + //绘制页面 + core.clearMap(music); + const bgVertical = core.material.images.images["bg_2010.webp"]; //竖屏背景 + const bg = core.material.images.images["bg_5043.webp"]; //竖屏背景 + if (core.domStyle.isVertical) { + //竖屏 - core.fillRect(ctx, 0, 0, 1248, 2028, "#000000"); //黑色背景 - ctx.globalAlpha = 0.3; //透明度 - if (bgVertical) - ctx.drawImage(bgVertical, 0, 0, 1280, 1500, 0, 0, 1248, 2028); //绘制半透明背景图片 - ctx.globalAlpha = 1; //恢复为不透明 + core.fillRect(ctx, 0, 0, 1248, 2028, "#000000"); //黑色背景 + ctx.globalAlpha = 0.3; //透明度 + if (bgVertical) + ctx.drawImage(bgVertical, 0, 0, 1280, 1500, 0, 0, 1248, 2028); //绘制半透明背景图片 + ctx.globalAlpha = 1; //恢复为不透明 - core.setTextAlign(ctx, "center"); - core.fillBoldText1( - ctx, - "◀离开", - 110, - 100, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); + core.setTextAlign(ctx, "center"); + core.fillBoldText1( + ctx, + "◀离开", + 110, + 100, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); - ctx.strokeStyle = "#FFFFFF"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(100, 200); - ctx.lineTo(1148, 200); + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(100, 200); + ctx.lineTo(1148, 200); - ctx.stroke(); - let posy = 300; - const indexList = this.musicMx[page]; - core.setTextAlign(ctx, "left"); - for (let i = 0; i < indexList.length; i++) { - const text = this.musicname[indexList[i]]; - core.fillBoldText1( - ctx, - text, - 150, - posy - 30, - page === this.selection[0] && i === this.selection[1] - ? "#FFFFFF" - : "#444444", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - ctx.strokeStyle = "#FFFFFF"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(100, posy); - ctx.lineTo(1148, posy); - ctx.stroke(); - posy += 100; - } - ctx.beginPath(); - ctx.moveTo(100, 1210); - ctx.lineTo(1148, 1210); - ctx.moveTo(100, 1200); - ctx.lineTo(1148, 1200); - ctx.stroke(); + ctx.stroke(); + let posy = 300; + const indexList = this.musicMx[page]; + core.setTextAlign(ctx, "left"); + for (let i = 0; i < indexList.length; i++) { + const text = this.musicname[indexList[i]]; + core.fillBoldText1( + ctx, + text, + 150, + posy - 30, + page === this.selection[0] && i === this.selection[1] ? + "#FFFFFF" : + "#444444", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(100, posy); + ctx.lineTo(1148, posy); + ctx.stroke(); + posy += 100; + } + ctx.beginPath(); + ctx.moveTo(100, 1210); + ctx.lineTo(1148, 1210); + ctx.moveTo(100, 1200); + ctx.lineTo(1148, 1200); + ctx.stroke(); - core.fillBoldText1( - ctx, - "上一页", - 100, - 1300, - page === 0 ? "#444444" : "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); + core.fillBoldText1( + ctx, + "上一页", + 100, + 1300, + page === 0 ? "#444444" : "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); - core.fillBoldText1( - ctx, - page + 1 + "/" + this.musicMx.length, - 580, - 1300, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - core.fillBoldText1( - ctx, - "下一页", - 950, - 1300, - page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); + core.fillBoldText1( + ctx, + page + 1 + "/" + this.musicMx.length, + 580, + 1300, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + core.fillBoldText1( + ctx, + "下一页", + 950, + 1300, + page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(100, 1600); - ctx.lineTo(1148, 1600); - ctx.stroke(); - ctx.fillStyle = "#ffffff"; - ctx.font = "bold 96px Verdana"; - ctx.fillText("|", 100, 1797); - ctx.fillText("◀", 115, 1800); - ctx.beginPath(); - ctx.arc(505, 1770, 80, 0, 3 * Math.PI); - ctx.stroke(); - ctx.fillText("|", 835, 1797); - ctx.fillText("▶", 785, 1800); - if (this.stop) { - ctx.fillText("▶", 473, 1797); - } else { - ctx.fillText("||", 453, 1794); - } + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(100, 1600); + ctx.lineTo(1148, 1600); + ctx.stroke(); + ctx.fillStyle = "#ffffff"; + ctx.font = "bold 96px Verdana"; + ctx.fillText("|", 100, 1797); + ctx.fillText("◀", 115, 1800); + ctx.beginPath(); + ctx.arc(505, 1770, 80, 0, 3 * Math.PI); + ctx.stroke(); + ctx.fillText("|", 835, 1797); + ctx.fillText("▶", 785, 1800); + if (this.stop) { + ctx.fillText("▶", 473, 1797); + } else { + ctx.fillText("||", 453, 1794); + } - const img = core.material.images.images[this.type + ".webp"]; - if (img) ctx.drawImage(img, 1000, 1655, 200, 200); - core.setTextAlign(ctx, "center"); - ctx.font = "bold 52px Verdana"; - ctx.fillText("当前歌曲", 625, 1397); - ctx.fillText( - this.musicname[this.musicMx[this.selection[0]][this.selection[1]]], - 625, - 1507 - ); + const img = core.material.images.images[this.type + ".webp"]; + if (img) ctx.drawImage(img, 1000, 1655, 200, 200); + core.setTextAlign(ctx, "center"); + ctx.font = "bold 52px Verdana"; + ctx.fillText("当前歌曲", 625, 1397); + ctx.fillText( + this.musicname[this.musicMx[this.selection[0]][this.selection[1]]], + 625, + 1507 + ); - ctx.font = "bold 36px Verdana"; - const thistime = audio.currentTime; + ctx.font = "bold 36px Verdana"; + const thistime = audio.currentTime; - if (thistime) { - const timetext = - Math.floor(thistime / 60) - .toString() - .padStart(2, "0") + - ":" + - Math.floor(thistime % 60) - .toString() - .padStart(2, "0"); - ctx.fillText(timetext, 960, 1650); - } else { - const timetext = "00:00"; - ctx.fillText(timetext, 960, 1650); - } - ctx.fillText("/", 1030, 1650); - const fulltime = audio.duration; + if (thistime) { + const timetext = + Math.floor(thistime / 60) + .toString() + .padStart(2, "0") + + ":" + + Math.floor(thistime % 60) + .toString() + .padStart(2, "0"); + ctx.fillText(timetext, 960, 1650); + } else { + const timetext = "00:00"; + ctx.fillText(timetext, 960, 1650); + } + ctx.fillText("/", 1030, 1650); + const fulltime = audio.duration; - if (fulltime) { - const timetext = - Math.floor(fulltime / 60) - .toString() - .padStart(2, "0") + - ":" + - Math.floor(fulltime % 60) - .toString() - .padStart(2, "0"); - ctx.fillText(timetext, 1100, 1650); - } else { - const timetext = "00:00"; - ctx.fillText(timetext, 1100, 1650); - } - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 9; - ctx.fillStyle = "rgba(255,255,255,0.5)"; - const pointx = (1048 * thistime) / fulltime + 100; - if (fulltime && thistime) { - ctx.beginPath(); - ctx.moveTo(100, 1600); - ctx.lineTo(pointx, 1600); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(pointx, 1600, 10, 0, 2 * Math.PI); - ctx.fill(); - } else { - ctx.beginPath(); - ctx.arc(100, 1600, 10, 0, 2 * Math.PI); - ctx.fill(); - } + if (fulltime) { + const timetext = + Math.floor(fulltime / 60) + .toString() + .padStart(2, "0") + + ":" + + Math.floor(fulltime % 60) + .toString() + .padStart(2, "0"); + ctx.fillText(timetext, 1100, 1650); + } else { + const timetext = "00:00"; + ctx.fillText(timetext, 1100, 1650); + } + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 9; + ctx.fillStyle = "rgba(255,255,255,0.5)"; + const pointx = (1048 * thistime) / fulltime + 100; + if (fulltime && thistime) { + ctx.beginPath(); + ctx.moveTo(100, 1600); + ctx.lineTo(pointx, 1600); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(pointx, 1600, 10, 0, 2 * Math.PI); + ctx.fill(); + } else { + ctx.beginPath(); + ctx.arc(100, 1600, 10, 0, 2 * Math.PI); + ctx.fill(); + } - ctx.fillStyle = "#ffffff"; - ctx.font = "bold 48px Verdana"; - ctx.fillText("音量", 150, 1970); - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(250, 1950); - ctx.lineTo(1050, 1950); - ctx.stroke(); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 9; - ctx.fillStyle = "rgba(255,255,255,0.5)"; + ctx.fillStyle = "#ffffff"; + ctx.font = "bold 48px Verdana"; + ctx.fillText("音量", 150, 1970); + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(250, 1950); + ctx.lineTo(1050, 1950); + ctx.stroke(); + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 9; + ctx.fillStyle = "rgba(255,255,255,0.5)"; - ctx.beginPath(); - ctx.moveTo(250, 1950); - ctx.lineTo(800 * audio.volume + 250, 1950); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(800 * audio.volume + 250, 1950, 10, 0, 2 * Math.PI); - ctx.fill(); - core.fillBoldText1( - ctx, - Math.floor(100 * audio.volume), - 1120, - 1970, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(48, true) - ); - } else { - //横屏 - core.fillRect(ctx, 0, 0, 2028, 1248, "#000000"); //黑色背景 - ctx.globalAlpha = 0.5; //透明度 - if (bg) ctx.drawImage(bg, 0, 0, 1280, 720, 0, 0, 2028, 1248); //绘制半透明背景图片 - ctx.globalAlpha = 1; //恢复为不透明 - core.setTextAlign(ctx, "center"); + ctx.beginPath(); + ctx.moveTo(250, 1950); + ctx.lineTo(800 * audio.volume + 250, 1950); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(800 * audio.volume + 250, 1950, 10, 0, 2 * Math.PI); + ctx.fill(); + core.fillBoldText1( + ctx, + Math.floor(100 * audio.volume), + 1120, + 1970, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(48, true) + ); + } else { + //横屏 + core.fillRect(ctx, 0, 0, 2028, 1248, "#000000"); //黑色背景 + ctx.globalAlpha = 0.5; //透明度 + if (bg) ctx.drawImage(bg, 0, 0, 1280, 720, 0, 0, 2028, 1248); //绘制半透明背景图片 + ctx.globalAlpha = 1; //恢复为不透明 + core.setTextAlign(ctx, "center"); - core.fillBoldText1( - ctx, - "◀离开", - 110, - 100, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - //core.fillRect(ctx, 440, 760, 50, 50) - ctx.strokeStyle = "#FFFFFF"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(800, 100); - ctx.lineTo(800, 1148); - ctx.moveTo(900, 100); - ctx.lineTo(1900, 100); - ctx.stroke(); - let posy = 200; - const indexList = this.musicMx[page]; - core.setTextAlign(ctx, "left"); - for (let i = 0; i < indexList.length; i++) { - const text = this.musicname[indexList[i]]; - core.fillBoldText1( - ctx, - text, - 950, - posy - 30, - page === this.selection[0] && i === this.selection[1] - ? "#FFFFFF" - : "#444444", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - ctx.strokeStyle = "#FFFFFF"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(900, posy); - ctx.lineTo(1900, posy); - ctx.stroke(); - posy += 100; - } - core.fillBoldText1( - ctx, - "上一页", - 1050, - 1200 - 30, - page === 0 ? "#444444" : "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); + core.fillBoldText1( + ctx, + "◀离开", + 110, + 100, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + //core.fillRect(ctx, 440, 760, 50, 50) + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(800, 100); + ctx.lineTo(800, 1148); + ctx.moveTo(900, 100); + ctx.lineTo(1900, 100); + ctx.stroke(); + let posy = 200; + const indexList = this.musicMx[page]; + core.setTextAlign(ctx, "left"); + for (let i = 0; i < indexList.length; i++) { + const text = this.musicname[indexList[i]]; + core.fillBoldText1( + ctx, + text, + 950, + posy - 30, + page === this.selection[0] && i === this.selection[1] ? + "#FFFFFF" : + "#444444", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(900, posy); + ctx.lineTo(1900, posy); + ctx.stroke(); + posy += 100; + } + core.fillBoldText1( + ctx, + "上一页", + 1050, + 1200 - 30, + page === 0 ? "#444444" : "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); - core.fillBoldText1( - ctx, - page + 1 + "/" + this.musicMx.length, - 1350, - 1200 - 30, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - core.fillBoldText1( - ctx, - "下一页", - 1550, - 1200 - 30, - page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(100, 600); - ctx.lineTo(700, 600); - ctx.stroke(); - ctx.fillStyle = "#ffffff"; - ctx.font = "bold 48px Verdana"; - ctx.fillText("|", 130, 797); - ctx.fillText("◀", 140, 800); - ctx.beginPath(); - ctx.arc(310, 780, 50, 0, 2 * Math.PI); - ctx.stroke(); - if (this.stop) { - ctx.fillText("▶", 295, 797); - } else { - ctx.fillText("||", 285, 794); - } + core.fillBoldText1( + ctx, + page + 1 + "/" + this.musicMx.length, + 1350, + 1200 - 30, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + core.fillBoldText1( + ctx, + "下一页", + 1550, + 1200 - 30, + page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(100, 600); + ctx.lineTo(700, 600); + ctx.stroke(); + ctx.fillStyle = "#ffffff"; + ctx.font = "bold 48px Verdana"; + ctx.fillText("|", 130, 797); + ctx.fillText("◀", 140, 800); + ctx.beginPath(); + ctx.arc(310, 780, 50, 0, 2 * Math.PI); + ctx.stroke(); + if (this.stop) { + ctx.fillText("▶", 295, 797); + } else { + ctx.fillText("||", 285, 794); + } - ctx.fillText("|", 470, 797); - ctx.fillText("▶", 450, 800); - ctx.fillText("音量", 350, 900); - ctx.beginPath(); - ctx.moveTo(100, 1000); - ctx.lineTo(700, 1000); - ctx.stroke(); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 9; - ctx.fillStyle = "rgba(255,255,255,0.5)"; + ctx.fillText("|", 470, 797); + ctx.fillText("▶", 450, 800); + ctx.fillText("音量", 350, 900); + ctx.beginPath(); + ctx.moveTo(100, 1000); + ctx.lineTo(700, 1000); + ctx.stroke(); + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 9; + ctx.fillStyle = "rgba(255,255,255,0.5)"; - ctx.beginPath(); - ctx.moveTo(100, 1000); - ctx.lineTo(600 * audio.volume + 100, 1000); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(600 * audio.volume + 100, 1000, 10, 0, 2 * Math.PI); - ctx.fill(); - core.fillBoldText1( - ctx, - Math.floor(100 * audio.volume), - 720, - 1010, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(32, true) - ); - const img = core.material.images.images[this.type + ".webp"]; - if (img) ctx.drawImage(img, 580, 730, 100, 100); - core.setTextAlign(ctx, "center"); - ctx.font = "bold 48px Verdana"; - ctx.fillText("当前歌曲", 400, 297); - ctx.fillText( - this.musicname[this.musicMx[this.selection[0]][this.selection[1]]], - 400, - 397 - ); + ctx.beginPath(); + ctx.moveTo(100, 1000); + ctx.lineTo(600 * audio.volume + 100, 1000); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(600 * audio.volume + 100, 1000, 10, 0, 2 * Math.PI); + ctx.fill(); + core.fillBoldText1( + ctx, + Math.floor(100 * audio.volume), + 720, + 1010, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(32, true) + ); + const img = core.material.images.images[this.type + ".webp"]; + if (img) ctx.drawImage(img, 580, 730, 100, 100); + core.setTextAlign(ctx, "center"); + ctx.font = "bold 48px Verdana"; + ctx.fillText("当前歌曲", 400, 297); + ctx.fillText( + this.musicname[this.musicMx[this.selection[0]][this.selection[1]]], + 400, + 397 + ); - ctx.font = "bold 36px Verdana"; - const thistime = audio.currentTime; + ctx.font = "bold 36px Verdana"; + const thistime = audio.currentTime; - if (thistime) { - const timetext = - Math.floor(thistime / 60) - .toString() - .padStart(2, "0") + - ":" + - Math.floor(thistime % 60) - .toString() - .padStart(2, "0"); - ctx.fillText(timetext, 510, 650); - } else { - const timetext = "00:00"; - ctx.fillText(timetext, 510, 650); - } - ctx.fillText("/", 580, 650); - const fulltime = audio.duration; + if (thistime) { + const timetext = + Math.floor(thistime / 60) + .toString() + .padStart(2, "0") + + ":" + + Math.floor(thistime % 60) + .toString() + .padStart(2, "0"); + ctx.fillText(timetext, 510, 650); + } else { + const timetext = "00:00"; + ctx.fillText(timetext, 510, 650); + } + ctx.fillText("/", 580, 650); + const fulltime = audio.duration; - if (fulltime) { - const timetext = - Math.floor(fulltime / 60) - .toString() - .padStart(2, "0") + - ":" + - Math.floor(fulltime % 60) - .toString() - .padStart(2, "0"); - ctx.fillText(timetext, 650, 650); - } else { - const timetext = "00:00"; - ctx.fillText(timetext, 650, 650); - } - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 9; - ctx.fillStyle = "rgba(255,255,255,0.5)"; - const pointx = (600 * thistime) / fulltime + 100; - if (fulltime && thistime) { - ctx.beginPath(); - ctx.moveTo(100, 600); - ctx.lineTo(pointx, 600); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(pointx, 600, 10, 0, 2 * Math.PI); - ctx.fill(); - } else { - ctx.beginPath(); - ctx.arc(100, 600, 10, 0, 2 * Math.PI); - ctx.fill(); - } - } - } - } - core.ui.music = new musicclass(); - main.dom.musicMode.onclick = function () { - //点击开始页面的CG MODE进入cg回廊 - main.core.control.checkBgm(); - main.core.control.pauseBgm(); - audio.src = - "project/bgms/" + - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ]; - const arr = main.core.ui.music.musicMx.flat(Infinity); - main.core.ui.music.randomList = shuffle(arr); - main.core.ui.music.random = main.core.ui.music.randomList.indexOf( - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ] - ); - page = 0; - music.style.display = "block"; - let time = 0; - core.registerAnimationFrame("music", null, (temptime) => { - if (temptime > time + 1000 / 60) { - time = temptime; - main.core.ui.music.update(); - } - }); - }; - }, - 横屏切换: function () { + if (fulltime) { + const timetext = + Math.floor(fulltime / 60) + .toString() + .padStart(2, "0") + + ":" + + Math.floor(fulltime % 60) + .toString() + .padStart(2, "0"); + ctx.fillText(timetext, 650, 650); + } else { + const timetext = "00:00"; + ctx.fillText(timetext, 650, 650); + } + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 9; + ctx.fillStyle = "rgba(255,255,255,0.5)"; + const pointx = (600 * thistime) / fulltime + 100; + if (fulltime && thistime) { + ctx.beginPath(); + ctx.moveTo(100, 600); + ctx.lineTo(pointx, 600); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(pointx, 600, 10, 0, 2 * Math.PI); + ctx.fill(); + } else { + ctx.beginPath(); + ctx.arc(100, 600, 10, 0, 2 * Math.PI); + ctx.fill(); + } + } + } + } + core.ui.music = new musicclass(); + main.dom.musicMode.onclick = function () { + //点击开始页面的CG MODE进入cg回廊 + main.core.control.checkBgm(); + main.core.control.pauseBgm(); + audio.src = + "project/bgms/" + + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ]; + const arr = main.core.ui.music.musicMx.flat(Infinity); + main.core.ui.music.randomList = shuffle(arr); + main.core.ui.music.random = main.core.ui.music.randomList.indexOf( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); + page = 0; + music.style.display = "block"; + let time = 0; + core.registerAnimationFrame("music", null, (temptime) => { + if (temptime > time + 1000 / 60) { + time = temptime; + main.core.ui.music.update(); + } + }); + }; +}, + "横屏切换": function () { // 在此增加新插件 this.triggerFullscreen = async function (full) { if (!!document.fullscreenElement && !full) { @@ -15390,7 +15394,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { } }; }, - 图片压缩webp导出: function () { + "图片压缩webp导出": function () { // 在此增加新插件 //使用方法:进入游戏后开始游戏,F12打开控制台,输入core.towebp(image),image为已在全塔属性中注册过的图片名字,需要""括起来 this.towebp = function (image) { @@ -15421,7 +15425,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { }); }; }, - "帧动画特效(游戏界面)": function () { + "帧动画特效(游戏界面)": function () { // 在此增加新插件 const animate2 = document.createElement("canvas"); //画布设置 animate2.style.zIndex = 71; @@ -15615,4 +15619,1434 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { } }); }, -}; + "音频系统": function () { + // 在此增加新插件 + /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行: + + + + + + + */ + + const { OggOpusDecoder } = window["ogg-opus-decoder"]; + const { OggVorbisDecoder } = window["ogg-vorbis-decoder"]; + const { CodecParser } = window.CodecParser; + + const audio = new Audio(); + + const supportMap = new Map(); + const AudioType = { + Mp3: "audio/mpeg", + Wav: 'audio/wav; codecs="1"', + Flac: "audio/flac", + Opus: 'audio/ogg; codecs="opus"', + Ogg: 'audio/ogg; codecs="vorbis"', + Aac: "audio/aac", + }; + /** + * 检查一种音频类型是否能被播放 + * @param type 音频类型 AudioType + */ + function isAudioSupport(type) { + if (supportMap.has(type)) return supportMap.get(type); + else { + const support = audio.canPlayType(type); + const canPlay = support === "maybe" || support === "probably"; + supportMap.set(type, canPlay); + return canPlay; + } + } + + const typeMap = new Map([ + ["ogg", AudioType.Ogg], + ["mp3", AudioType.Mp3], + ["wav", AudioType.Wav], + ["flac", AudioType.Flac], + ["opus", AudioType.Opus], + ["aac", AudioType.Aac], + ]); + + /** + * 根据文件名拓展猜测其类型 + * @param file 文件名 string + */ + function guessTypeByExt(file) { + const ext = /\.[a-zA-Z\d]+$/.exec(file); + if (!ext?.[0]) return ""; + const type = ext[0].slice(1); + return typeMap.get(type.toLocaleLowerCase()) ?? ""; + } + + isAudioSupport(AudioType.Ogg); + isAudioSupport(AudioType.Mp3); + isAudioSupport(AudioType.Wav); + isAudioSupport(AudioType.Flac); + isAudioSupport(AudioType.Opus); + isAudioSupport(AudioType.Aac); + + function isNil(value) { + return value === void 0 || value === null; + } + + function sleep(time) { + return new Promise((res) => setTimeout(res, time)); + } + class AudioEffect { + constructor(ac) {} + /** + * 连接至其他效果器 + * @param target 目标输入 IAudioInput + * @param output 当前效果器输出通道 Number + * @param input 目标效果器的输入通道 Number + */ + connect(target, output, input) { + this.output.connect(target.input, output, input); + } + + /** + * 与其他效果器取消连接 + * @param target 目标输入 IAudioInput + * @param output 当前效果器输出通道 Number + * @param input 目标效果器的输入通道 Number + */ + disconnect(target, output, input) { + if (!target) { + if (!isNil(output)) { + this.output.disconnect(output); + } else { + this.output.disconnect(); + } + } else { + if (!isNil(output)) { + if (!isNil(input)) { + this.output.disconnect(target.input, output, input); + } else { + this.output.disconnect(target.input, output); + } + } else { + this.output.disconnect(target.input); + } + } + } + } + + class StereoEffect extends AudioEffect { + constructor(ac) { + super(ac); + const panner = ac.createPanner(); + this.input = panner; + this.output = panner; + } + + /** + * 设置音频朝向,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 朝向x坐标 Number + * @param y 朝向y坐标 Number + * @param z 朝向z坐标 Number + */ + setOrientation(x, y, z) { + this.output.orientationX.value = x; + this.output.orientationY.value = y; + this.output.orientationZ.value = z; + } + /** + * 设置音频位置,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 位置x坐标 Number + * @param y 位置y坐标 Number + * @param z 位置z坐标 Number + */ + setPosition(x, y, z) { + this.output.positionX.value = x; + this.output.positionY.value = y; + this.output.positionZ.value = z; + } + end() {} + + start() {} + } + class VolumeEffect extends AudioEffect { + constructor(ac) { + super(ac); + const gain = ac.createGain(); + this.input = gain; + this.output = gain; + } + + /** + * 设置音量大小 + * @param volume 音量大小 Number + */ + setVolume(volume) { + this.output.gain.value = volume; + } + + /** + * 获取音量大小 Number + */ + getVolume() { + return this.output.gain.value; + } + + end() {} + + start() {} + } + class ChannelVolumeEffect extends AudioEffect { + /** 所有的音量控制节点 */ + + constructor(ac) { + super(ac); + /** 所有的音量控制节点 */ + this.gain = []; + const splitter = ac.createChannelSplitter(); + const merger = ac.createChannelMerger(); + this.output = merger; + this.input = splitter; + for (let i = 0; i < 6; i++) { + const gain = ac.createGain(); + splitter.connect(gain, i); + gain.connect(merger, 0, i); + this.gain.push(gain); + } + } + + /** + * 设置某个声道的音量大小 + * @param channel 要设置的声道,可填0-5 Number + * @param volume 这个声道的音量大小 Number + */ + setVolume(channel, volume) { + if (!this.gain[channel]) return; + this.gain[channel].gain.value = volume; + } + + /** + * 获取某个声道的音量大小,可填0-5 + * @param channel 要获取的声道 Number + */ + getVolume(channel) { + if (!this.gain[channel]) return 0; + return this.gain[channel].gain.value; + } + + end() {} + + start() {} + } + class DelayEffect extends AudioEffect { + constructor(ac) { + super(ac); + + const delay = ac.createDelay(); + this.input = delay; + this.output = delay; + } + + /** + * 设置延迟时长 + * @param delay 延迟时长,单位秒 Number + */ + setDelay(delay) { + this.output.delayTime.value = delay; + } + + /** + * 获取延迟时长 + */ + getDelay() { + return this.output.delayTime.value; + } + + end() {} + + start() {} + } + class EchoEffect extends AudioEffect { + constructor(ac) { + super(ac); + const delay = ac.createDelay(); + const gain = ac.createGain(); + gain.gain.value = 0.5; + delay.delayTime.value = 0.05; + delay.connect(gain); + gain.connect(delay); + /** 延迟节点 */ + this.delay = delay; + /** 反馈增益节点 */ + this.gainNode = gain; + /** 当前增益 */ + this.gain = 0.5; + /** 是否正在播放 */ + this.playing = false; + this.input = gain; + this.output = gain; + } + + /** + * 设置回声反馈增益大小 + * @param gain 增益大小,范围 0-1,大于等于1的视为0.5,小于0的视为0 Number + */ + setFeedbackGain(gain) { + const resolved = gain >= 1 ? 0.5 : gain < 0 ? 0 : gain; + this.gain = resolved; + if (this.playing) this.gainNode.gain.value = resolved; + } + + /** + * 设置回声间隔时长 + * @param delay 回声时长,范围 0.01-Infinity,小于0.01的视为0.01 Number + */ + setEchoDelay(delay) { + const resolved = delay < 0.01 ? 0.01 : delay; + this.delay.delayTime.value = resolved; + } + + /** + * 获取反馈节点增益 + */ + getFeedbackGain() { + return this.gain; + } + + /** + * 获取回声间隔时长 + */ + getEchoDelay() { + return this.delay.delayTime.value; + } + + end() { + this.playing = false; + const echoTime = Math.ceil(Math.log(0.001) / Math.log(this.gain)) + 10; + sleep(this.delay.delayTime.value * echoTime).then(() => { + if (!this.playing) this.gainNode.gain.value = 0; + }); + } + + start() { + this.playing = true; + this.gainNode.gain.value = this.gain; + } + } + + class StreamLoader { + constructor(url) { + /** 传输目标 Set*/ + this.target = new Set(); + this.loading = false; + } + + /** + * 将加载流传递给字节流读取对象 + * @param reader 字节流读取对象 IStreamReader + */ + pipe(reader) { + if (this.loading) { + console.warn( + "Cannot pipe new StreamReader object when stream is loading." + ); + return; + } + this.target.add(reader); + reader.piped(this); + return this; + } + + async start() { + if (this.loading) return; + this.loading = true; + const response = await window.fetch(this.url); + const stream = response.body; + if (!stream) { + console.error("Cannot get reader when fetching '" + this.url + "'."); + return; + } + // 获取读取器 + /** 读取流对象 */ + this.stream = stream; + const reader = response.body?.getReader(); + const targets = [...this.target]; + // try { + await Promise.all(targets.map((v) => v.start(stream, this, response))); + + // 开始流传输 + while (true) { + const { value, done } = await reader.read(); + await Promise.all(targets.map((v) => v.pump(value, done, response))); + if (done) break; + } + + this.loading = false; + targets.forEach((v) => v.end(true)); + // } catch (e) { + // logger.error(26, this.url, String(e)); + // } + } + + cancel(reason) { + if (!this.stream) return; + this.stream.cancel(reason); + this.loading = false; + this.target.forEach((v) => v.end(false, reason)); + } + } + const fileSignatures = [ + [AudioType.Mp3, [0x49, 0x44, 0x33]], + [AudioType.Ogg, [0x4f, 0x67, 0x67, 0x53]], + [AudioType.Wav, [52, 0x49, 0x46, 0x46]], + [AudioType.Flac, [0x66, 0x4c, 0x61, 0x43]], + [AudioType.Aac, [0xff, 0xf1]], + [AudioType.Aac, [0xff, 0xf9]], + ]; + const oggHeaders = [ + [AudioType.Opus, [0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64]], + ]; + + const mimeTypeMap = { + [AudioType.Aac]: "audio/aac", + [AudioType.Flac]: "audio/flac", + [AudioType.Mp3]: "audio/mpeg", + [AudioType.Ogg]: "application/ogg", + [AudioType.Opus]: "application/ogg", + [AudioType.Wav]: "application/ogg", + }; + + function isOggPage(data) { + return !isNil(data.isFirstPage); + } + class AudioStreamSource { + /** + * 注册一个解码器 + * @param type 要注册的解码器允许解码的类型 + * @param decoder 解码器对象 + */ + static registerDecoder(type, decoder) { + if (!this.decoderMap) this.decoderMap = new Map() + if (this.decoderMap.has(type)) { + console.warn( + "Audio stream decoder for audio type '" + + type + + "' has already existed." + ); + return; + } + this.decoderMap.set(type, decoder); + } + + constructor(context) { + this.decoderMap = new Map(); + this.output = context.createBufferSource(); + /** 是否已经完全加载完毕 */ + this.loaded = false; + /** 已经缓冲了多长时间,如果缓冲完那么跟歌曲时长一致 */ + this.buffered = 0; + /** 已经缓冲的采样点数量 */ + this.bufferedSamples = 0; + /** 歌曲时长,加载完毕之前保持为 0 */ + this.duration = 0; + /** 在流传输阶段,至少缓冲多长时间的音频之后才开始播放,单位秒 */ + this.bufferPlayDuration = 1; + /** 音频的采样率,未成功解析出之前保持为 0 */ + this.sampleRate = 0; + //是否循环播放 + this.loop = false; + /** 开始播放时刻 */ + this.lastStartTime = 0; + /** 上一次播放的缓存长度 */ + this.lastBufferSamples = 0; + + /** 是否已经获取到头文件 */ + this.headerRecieved = false; + /** 音频类型 */ + this.audioType = ""; + /** 每多长时间组成一个缓存 Float32Array */ + this.bufferChunkSize = 10; + /** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */ + this.audioData = []; + + this.errored = false; + } + + /** + * 设置每个缓存数据的大小,默认为10秒钟一个缓存数据 + * @param size 每个缓存数据的时长,单位秒 + */ + setChunkSize(size) { + if (this.controller?.loading || this.loaded) return; + this.bufferChunkSize = size; + } + + piped(controller) { + this.controller = controller; + } + + async pump(data, done) { + if (!data || this.errored) return; + if (!this.headerRecieved) { + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + for (const [type, value] of fileSignatures) { + if (value.every((v, i) => toCheck[i] === v)) { + this.audioType = type; + break; + } + } + if (this.audioType === AudioType.Ogg) { + // 如果是ogg的话,进一步判断是不是opus + for (const [key, value] of oggHeaders) { + const has = toCheck.some((_, i) => { + return value.every((v, ii) => toCheck[i + ii] === v); + }); + if (has) { + this.audioType = key; + break; + } + } + } + if (!this.audioType) { + console.error( + "Unknown audio type. Header: '" + [...toCheck] + .map((v) => v.toString().padStart(2, "0")) + .join(" ") + .toUpperCase() + + "'" + ); + return; + } + // 创建解码器 + const Decoder = AudioStreamSource.decoderMap.get(this.audioType); + if (!Decoder) { + this.errored = true; + console.error( + "Cannot decode stream source type of '" + + this.audioType + + "', since there is no registered decoder for that type." + ); + return Promise.reject( + `Cannot decode stream source type of '${this.audioType}', since there is no registered decoder for that type.` + ); + } + this.decoder = new Decoder(); + // 创建数据解析器 + const mime = mimeTypeMap[this.audioType]; + const parser = new CodecParser(mime); + this.parser = parser; + await this.decoder.create(); + this.headerRecieved = true; + } + + const decoder = this.decoder; + const parser = this.parser; + if (!decoder || !parser) { + this.errored = true; + return Promise.reject( + "No parser or decoder attached in this AudioStreamSource" + ); + } + + await this.decodeData(data, decoder, parser); + if (done) await this.decodeFlushData(decoder, parser); + this.checkBufferedPlay(); + } + + /** + * 检查采样率,如果还未解析出采样率,那么将设置采样率,如果当前采样率与之前不同,那么发出警告 + */ + checkSampleRate(info) { + for (const one of info) { + const frame = isOggPage(one) ? one.codecFrames[0] : one; + if (frame) { + const rate = frame.header.sampleRate; + if (this.sampleRate === 0) { + this.sampleRate = rate; + break; + } else { + if (rate !== this.sampleRate) { + console.warn("Sample rate in stream audio must be constant."); + } + } + } + } + } + + /** + * 解析音频数据 + */ + async decodeData(data, decoder, parser) { + // 解析音频数据 + const audioData = await decoder.decode(data); + if (!audioData) return; + // @ts-expect-error 库类型声明错误 + const audioInfo = [...parser.parseChunk(data)]; + + // 检查采样率 + this.checkSampleRate(audioInfo); + // 追加音频数据 + this.appendDecodedData(audioData, audioInfo); + } + + /** + * 解码剩余数据 + */ + async decodeFlushData(decoder, parser) { + const audioData = await decoder.flush(); + if (!audioData) return; + // @ts-expect-error 库类型声明错误 + const audioInfo = [...parser.flush()]; + + this.checkSampleRate(audioInfo); + this.appendDecodedData(audioData, audioInfo); + } + + /** + * 追加音频数据 + */ + appendDecodedData(data, info) { + const channels = data.channelData.length; + if (channels === 0) return; + if (this.audioData.length !== channels) { + this.audioData = []; + for (let i = 0; i < channels; i++) { + this.audioData.push([]); + } + } + // 计算出应该放在哪 + const chunk = this.sampleRate * this.bufferChunkSize; + const sampled = this.bufferedSamples; + const pushIndex = Math.floor(sampled / chunk); + const bufferIndex = sampled % chunk; + const dataLength = data.channelData[0].length; + let buffered = 0; + let nowIndex = pushIndex; + let toBuffer = bufferIndex; + while (buffered < dataLength) { + const rest = toBuffer !== 0 ? chunk - bufferIndex : chunk; + + for (let i = 0; i < channels; i++) { + const audioData = this.audioData[i]; + if (!audioData[nowIndex]) { + audioData.push(new Float32Array(chunk)); + } + const toPush = data.channelData[i].slice(buffered, buffered + rest); + + audioData[nowIndex].set(toPush, toBuffer); + } + buffered += rest; + nowIndex++; + toBuffer = 0; + } + + this.buffered += + info.reduce((prev, curr) => prev + curr.duration, 0) / 1000; + this.bufferedSamples += info.reduce( + (prev, curr) => prev + curr.samples, + 0 + ); + } + + /** + * 检查已缓冲内容,并在未开始播放时播放 + */ + checkBufferedPlay() { + if (this.playing || this.sampleRate === 0) return; + const played = this.lastBufferSamples / this.sampleRate; + const dt = this.buffered - played; + if (this.loaded) { + this.playAudio(played); + return; + } + if (dt < this.bufferPlayDuration) return; + console.log(played, this.lastBufferSamples, this.sampleRate); + this.lastBufferSamples = this.bufferedSamples; + // 需要播放 + this.mergeBuffers(); + if (!this.buffer) return; + if (this.playing) this.output.stop(); + this.createSourceNode(this.buffer); + this.output.loop = false; + this.output.start(0, played); + this.lastStartTime = this.ac.currentTime; + this.playing = true; + this.output.addEventListener("ended", () => { + this.playing = false; + this.checkBufferedPlay(); + }); + } + + mergeBuffers() { + const buffer = this.ac.createBuffer( + this.audioData.length, + this.bufferedSamples, + this.sampleRate + ); + const chunk = this.sampleRate * this.bufferChunkSize; + const bufferedChunks = Math.floor(this.bufferedSamples / chunk); + const restLength = this.bufferedSamples % chunk; + for (let i = 0; i < this.audioData.length; i++) { + const audio = this.audioData[i]; + const data = new Float32Array(this.bufferedSamples); + for (let j = 0; j < bufferedChunks; j++) { + data.set(audio[j], chunk * j); + } + if (restLength !== 0) { + data.set( + audio[bufferedChunks].slice(0, restLength), + chunk * bufferedChunks + ); + } + + buffer.copyToChannel(data, i, 0); + } + this.buffer = buffer; + } + + async start() { + delete this.buffer; + this.headerRecieved = false; + this.audioType = ""; + this.errored = false; + this.buffered = 0; + this.sampleRate = 0; + this.bufferedSamples = 0; + this.duration = 0; + this.loaded = false; + if (this.playing) this.output.stop(); + this.playing = false; + this.lastStartTime = this.ac.currentTime; + } + + end(done, reason) { + if (done && this.buffer) { + this.loaded = true; + delete this.controller; + this.mergeBuffers(); + // const played = this.lastBufferSamples / this.sampleRate; + // this.playAudio(played); + this.duration = this.buffered; + this.audioData = []; + this.decoder?.destroy(); + delete this.decoder; + delete this.parser; + } else { + console.warn( + "Unexpected end when loading stream audio, reason: '" + + (reason ?? "") + + "'" + ); + } + } + + playAudio(when) { + if (!this.buffer) return; + this.lastStartTime = this.ac.currentTime; + if (this.playing) this.output.stop(); + this.emit("play"); + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.playing = true; + console.log(when); + + this.output.addEventListener("ended", () => { + this.playing = false; + this.emit("end"); + if (this.loop && !this.output.loop) this.play(0); + }); + } + /** + * 开始播放这个音频源 + */ + play(when) { + if (this.playing || this.errored) return; + if (this.loaded && this.buffer) { + this.playing = true; + this.playAudio(when); + } else { + this.controller?.start(); + } + } + + createSourceNode(buffer) { + if (!this.target) return; + const node = this.ac.createBufferSource(); + node.buffer = buffer; + if (this.playing) this.output.stop(); + this.playing = false; + this.output = node; + node.connect(this.target.input); + node.loop = this.loop; + } + /** + * 停止播放这个音频源 + * @returns 音频暂停的时刻 number + */ + stop() { + if (this.playing) this.output.stop(); + this.playing = false; + return this.ac.currentTime - this.lastStartTime; + } + /** + * 连接到音频路由图上,每次调用播放的时候都会执行一次 + * @param target 连接至的目标 IAudioInput + */ + connect(target) { + this.target = target; + } + /** + * 设置是否循环播放 + * @param loop 是否循环 boolean) + */ + setLoop(loop) { + this.loop = loop; + } + } + class AudioElementSource { + + constructor(context) { + const audio = new Audio(); + audio.preload = 'none'; + this.output = context.createMediaElementSource(audio); + this.audio = audio; + audio.addEventListener('play', () => { + this.playing = true; + this.emit('play'); + }); + audio.addEventListener('ended', () => { + this.playing = false; + this.emit('end'); + }); + } + + /** + * 设置音频源的路径 + * @param url 音频路径 + */ + setSource(url) { + this.audio.src = url; + } + + play(when = 0) { + if (this.playing) return; + this.audio.currentTime = when; + this.audio.play(); + } + + stop() { + this.audio.pause(); + this.playing = false; + this.emit('end'); + return this.audio.currentTime; + } + + connect(target) { + this.output.connect(target.input); + } + + setLoop(loop) { + this.audio.loop = loop; + } + } + class AudioBufferSource { + + + + constructor(context) { + this.output = context.createBufferSource(); + /** 是否循环 */ + this.loop = false; + + /** 播放开始时刻 */ + this.lastStartTime = 0; + + } + + /** + * 设置音频源数据 + * @param buffer 音频源,可以是未解析的 ArrayBuffer,也可以是已解析的 AudioBuffer + */ + async setBuffer(buffer) { + if (buffer instanceof ArrayBuffer) { + this.buffer = await this.ac.decodeAudioData(buffer); + } else { + this.buffer = buffer; + } + } + + play(when) { + if (this.playing || !this.buffer) return; + this.playing = true; + this.lastStartTime = this.ac.currentTime; + this.emit('play'); + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.output.addEventListener('ended', () => { + this.playing = false; + this.emit('end'); + if (this.loop && !this.output.loop) this.play(0); + }); + } + + createSourceNode(buffer) { + if (!this.target) return; + const node = this.ac.createBufferSource(); + node.buffer = buffer; + this.output = node; + node.connect(this.target.input); + node.loop = this.loop; + } + + stop() { + this.output.stop(); + return this.ac.currentTime - this.lastStartTime; + } + + connect(target) { + this.target = target; + } + + setLoop(loop) { + this.loop = loop; + } + } + class AudioPlayer { + constructor() { + /** 音频播放上下文 */ + this.ac = new AudioContext(); + /** 音量节点 */ + this.gain = this.ac.createGain(); + this.gain.connect(this.ac.destination); + this.audioRoutes = new Map() + } + + /** + * 设置音量 + * @param volume 音量 + */ + setVolume(volume) { + this.gain.gain.value = volume; + } + + /** + * 获取音量 + */ + getVolume() { + return this.gain.gain.value; + } + + /** + * 创建一个音频源 + * @param Source 音频源类 + */ + createSource(Source) { + return new Source(this.ac); + } + + /** + * 创建一个兼容流式音频源,可以与流式加载相结合,主要用于处理 opus ogg 不兼容的情况 + */ + createStreamSource() { + return new AudioStreamSource(this.ac); + } + + /** + * 创建一个通过 audio 元素播放的音频源 + */ + createElementSource() { + return new AudioElementSource(this.ac); + } + + /** + * 创建一个通过 AudioBuffer 播放的音频源 + */ + createBufferSource() { + return new AudioBufferSource(this.ac); + } + + /** + * 获取音频目的地 + */ + getDestination() { + return this.gain; + } + + /** + * 创建一个音频效果器 + * @param Effect 效果器类 + */ + createEffect(Effect) { + return new Effect(this.ac); + } + + /** + * 创建一个修改音量的效果器 + * ```txt + * |----------| + * Input ----> | GainNode | ----> Output + * |----------| + * ``` + */ + createVolumeEffect() { + return new VolumeEffect(this.ac); + } + + /** + * 创建一个立体声效果器 + * ```txt + * |------------| + * Input ----> | PannerNode | ----> Output + * |------------| + * ``` + */ + createStereoEffect() { + return new StereoEffect(this.ac); + } + + /** + * 创建一个修改单个声道音量的效果器 + * ```txt + * |----------| + * -> | GainNode | \ + * |--------------| / |----------| -> |------------| + * Input ----> | SplitterNode | ...... | MergerNode | ----> Output + * |--------------| \ |----------| -> |------------| + * -> | GainNode | / + * |----------| + * ``` + */ + createChannelVolumeEffect() { + return new ChannelVolumeEffect(this.ac); + } + + /** + * 创建一个延迟效果器 + * |-----------| + * Input ----> | DelayNode | ----> Output + * |-----------| + */ + createDelay() { + return new DelayEffect(this.ac); + } + + /** + * 创建一个回声效果器 + * ```txt + * |----------| + * Input ----> | GainNode | ----> Output + * ^ |----------| | + * | | + * | |------------| ↓ + * |-- | Delay Node | <-- + * |------------| + * ``` + */ + createEchoEffect() { + return new EchoEffect(this.ac); + } + + /** + * 创建一个音频播放路由 + * @param source 音频源 + */ + createRoute(source) { + return new AudioRoute(source, this); + } + + /** + * 添加一个音频播放路由,可以直接被播放 + * @param id 这个音频播放路由的名称 + * @param route 音频播放路由对象 + */ + addRoute(id, route) { + if (!this.audioRoutes) this.audioRoutes = new Map() + if (this.audioRoutes.has(id)) { + console.warn( + "Audio route with id of '" + + id + + "' has already existed. New route will override old route." + ); + } + this.audioRoutes.set(id, route); + } + + /** + * 根据名称获取音频播放路由对象 + * @param id 音频播放路由的名称 + */ + getRoute(id) { + return this.audioRoutes.get(id); + } + + /** + * 播放音频 + * @param id 音频名称 + * @param when 从音频的哪个位置开始播放,单位秒 + */ + play(id, when) { + this.getRoute(id)?.play(when); + } + + /** + * 暂停音频播放 + * @param id 音频名称 + * @returns 当音乐真正停止时兑现 + */ + pause(id) { + const route = this.getRoute(id); + if (!route) return Promise.resolve(); + else return route.pause(); + } + + /** + * 停止音频播放 + * @param id 音频名称 + * @returns 当音乐真正停止时兑现 + */ + stop(id) { + const route = this.getRoute(id); + if (!route) return Promise.resolve(); + else return route.stop(); + } + + /** + * 继续音频播放 + * @param id 音频名称 + */ + resume(id) { + this.getRoute(id)?.resume(); + } + + /** + * 设置听者位置,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 位置x坐标 + * @param y 位置y坐标 + * @param z 位置z坐标 + */ + setListenerPosition(x, y, z) { + const listener = this.ac.listener; + listener.positionX.value = x; + listener.positionY.value = y; + listener.positionZ.value = z; + } + + /** + * 设置听者朝向,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 朝向x坐标 + * @param y 朝向y坐标 + * @param z 朝向z坐标 + */ + setListenerOrientation(x, y, z) { + const listener = this.ac.listener; + listener.forwardX.value = x; + listener.forwardY.value = y; + listener.forwardZ.value = z; + } + + /** + * 设置听者头顶朝向,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 头顶朝向x坐标 + * @param y 头顶朝向y坐标 + * @param z 头顶朝向z坐标 + */ + setListenerUp(x, y, z) { + const listener = this.ac.listener; + listener.upX.value = x; + listener.upY.value = y; + listener.upZ.value = z; + } + } + const AudioRouteEvent = { + updateEffect: [], + play: [], + stop: [], + pause: [], + resume: [], + }; + class AudioRoute { + constructor(source, player) { + this.output = source.output; + /** 效果器路由图 */ + this.effectRoute = []; + + /** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */ + this.endTime = 0; + + /** 是否已暂停,注意停止播放是不算暂停的 */ + this.paused = false; + /** 暂停时刻 */ + this.pauseTime = 0; + this.source = source + this.player = player + } + + /** + * 设置结束时间,暂停或停止时,会经过这么长时间才终止音频的播放,这期间可以做一下音频淡出的效果。 + * @param time 暂停或停止时,经过多长时间之后才会结束音频的播放 + */ + setEndTime(time) { + this.endTime = time; + } + + /** + * 当音频播放时执行的函数,可以用于音频淡入效果 + * @param fn 音频开始播放时执行的函数 + */ + onStart(fn) { + this.audioStartHook = fn; + } + + /** + * 当音频暂停或停止时执行的函数,可以用于音频淡出效果 + * @param fn 音频在暂停或停止时执行的函数,不填时表示取消这个钩子。 + * 包含两个参数,第一个参数是结束时长,第二个参数是当前音频播放路由对象 + */ + onEnd(fn) { + this.audioEndHook = fn; + } + + /** + * 开始播放这个音频 + * @param when 从音频的什么时候开始播放,单位秒 + */ + play(when = 0) { + if (this.source.playing) return; + this.link(); + if (this.effectRoute.length > 0) { + const first = this.effectRoute[0]; + this.source.connect(first); + } else { + this.source.connect({ input: this.player.getDestination() }); + } + this.source.play(when); + this.paused = false; + this.pauseTime = 0; + this.audioStartHook?.(this); + this.startAllEffect(); + + } + + /** + * 暂停音频播放 + */ + async pause() { + if (this.paused || !this.source.playing) return; + if (this.audioEndHook) { + this.audioEndHook(this.endTime, this); + await sleep(this.endTime); + } + const time = this.source.stop(); + this.pauseTime = time; + this.paused = true; + this.endAllEffect(); + + } + + /** + * 继续音频播放 + */ + resume() { + if (this.source.playing) return; + if (this.paused) { + this.play(this.pauseTime); + } else { + this.play(0); + } + this.paused = false; + this.pauseTime = 0; + this.audioStartHook?.(this); + this.startAllEffect(); + + } + + /** + * 停止音频播放 + */ + async stop() { + if (!this.source.playing) return; + if (this.audioEndHook) { + this.audioEndHook(this.endTime, this); + await sleep(this.endTime); + } + this.source.stop(); + this.paused = false; + this.pauseTime = 0; + this.endAllEffect(); + + } + + /** + * 添加效果器 + * @param effect 要添加的效果,可以是数组,表示一次添加多个 + * @param index 从哪个位置开始添加,如果大于数组长度,那么加到末尾,如果小于0,那么将会从后面往前数。默认添加到末尾 + */ + addEffect(effect, index) { + if (isNil(index)) { + if (effect instanceof Array) { + this.effectRoute.push(...effect); + } else { + this.effectRoute.push(effect); + } + } else { + if (effect instanceof Array) { + this.effectRoute.splice(index, 0, ...effect); + } else { + this.effectRoute.splice(index, 0, effect); + } + } + this.setOutput(); + if (this.source.playing) this.link(); + + } + + /** + * 移除一个效果器 + * @param effect 要移除的效果 + */ + removeEffect(effect) { + const index = this.effectRoute.indexOf(effect); + if (index === -1) return; + this.effectRoute.splice(index, 1); + effect.disconnect(); + this.setOutput(); + if (this.source.playing) this.link(); + + } + + setOutput() { + const effect = this.effectRoute.at(-1); + if (!effect) this.output = this.source.output; + else this.output = effect.output; + } + + /** + * 连接音频路由图 + */ + link() { + this.effectRoute.forEach((v) => v.disconnect()); + this.effectRoute.forEach((v, i) => { + const next = this.effectRoute[i + 1]; + if (next) { + v.connect(next); + } + }); + } + + startAllEffect() { + this.effectRoute.forEach((v) => v.start()); + } + + endAllEffect() { + this.effectRoute.forEach((v) => v.end()); + } + } + + core.plugin.audioPlayer = new AudioPlayer(); + const audioPlayer = core.plugin.audioPlayer; + + class VorbisDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggVorbisDecoder(); + await this.decoder.ready; + } + /** + * 摧毁这个解码器 + */ + destroy() { + this.decoder?.free(); + } + /** + * 解码流数据 + * @param data 流数据 + */ + + async decode(data) { + return this.decoder?.decode(data); + } + /** + * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 + */ + async flush() { + return await this.decoder?.flush(); + } + } + class OpusDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggOpusDecoder(); + await this.decoder.ready; + } + /** + * 摧毁这个解码器 + */ + destroy() { + this.decoder?.free(); + } + /** + * 解码流数据 + * @param data 流数据 + */ + async decode(data) { + return this.decoder?.decode(data); + } + /** + * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 + */ + async flush() { + return await this.decoder?.flush(); + } + } + + function loadAllBgm() { + + const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + for (const bgm of data.main.bgms) { + const type = guessTypeByExt(bgm); + if (!type) continue; + if (isAudioSupport(type)) { + const source = audioPlayer.createElementSource(); + source.setSource(`project/bgms/${bgm}`); + source.setLoop(true); + const route = new AudioRoute(source, audioPlayer); + audioPlayer.addRoute(`bgms.${bgm}`, route); + } else { + const source = audioPlayer.createStreamSource(); + const stream = new StreamLoader(`project/bgms/${bgm}`); + stream.pipe(source); + source.setLoop(true); + const route = new AudioRoute(source, audioPlayer); + audioPlayer.addRoute(`bgms.${bgm}`, route); + } + } + + } + loadAllBgm(); + AudioStreamSource.registerDecoder(AudioType.Ogg, VorbisDecoder); + AudioStreamSource.registerDecoder(AudioType.Opus, OpusDecoder); + + core.plugin.audioSystem = { + AudioType, + isAudioSupport, + guessTypeByExt, + EchoEffect, + DelayEffect, + ChannelVolumeEffect, + VolumeEffect, + StereoEffect, + AudioEffect, + AudioPlayer, + AudioRoute, + AudioStreamSource, + AudioElementSource, + AudioBufferSource, + loadAllBgm, + StreamLoader, + }; + + +} +} \ No newline at end of file