Explorar el Código

优化频谱的柱子数量和宽度,更加美观
优化LocalMusic

onecold hace 6 meses
padre
commit
e7fdb32a0a

+ 1 - 1
entry/src/main/ets/dialog/RemoteDriveAccountDialog.ets

@@ -325,7 +325,7 @@ export struct RemoteDriveAccountDialog {
         }
         }
         .alignItems(VerticalAlign.Center)
         .alignItems(VerticalAlign.Center)
         .visibility(Visibility.Visible)
         .visibility(Visibility.Visible)
-        // this.buildHelperText('NAS 上共享根目录的名称,例如 music、share')
+        this.buildHelperText('NAS 上共享根目录的名称,例如 music、share,华为家庭存储共享名称填是“家庭共享”')
 
 
         Row({ space: 8 }) {
         Row({ space: 8 }) {
           Text('域/工作组')
           Text('域/工作组')

+ 1 - 1
entry/src/main/ets/pages/SettingPage.ets

@@ -1744,7 +1744,7 @@ export struct SettingPage {
             // 均衡器设置入口
             // 均衡器设置入口
             Button({ type: ButtonType.Normal, stateEffect: true }) {
             Button({ type: ButtonType.Normal, stateEffect: true }) {
               Row() {
               Row() {
-                SymbolGlyph($r('sys.symbol.waveform'))
+                SymbolGlyph($r('sys.symbol.slider_vertical_3'))
                   .fontSize(20)
                   .fontSize(20)
                   .fontColor([this.themeColor])
                   .fontColor([this.themeColor])
                   .alignSelf(ItemAlign.Center)
                   .alignSelf(ItemAlign.Center)

+ 1 - 2
entry/src/main/ets/view/AIVoiceSeparation.ets

@@ -630,8 +630,7 @@ async function separateVoiceAndAccompaniment(
         ];
         ];
 
 
         await FFmpeg.execute(backupVocalCommands,  {
         await FFmpeg.execute(backupVocalCommands,  {
-          logCallback: (logLevel, logMessage) =>
-            console.log(`[${logLevel}]      备用方案:    ${logMessage}`),
+          logCallback: (level: number, msg: string) => console.log(`[log][${level}]${msg}`),
         });
         });
 
 
         console.info(`onecold      备用方案成功`);
         console.info(`onecold      备用方案成功`);

+ 23 - 49
entry/src/main/ets/view/LocalMusic.ets

@@ -7819,7 +7819,9 @@ export struct LocalMusic {
       tempOutPath  = ''
       tempOutPath  = ''
     }
     }
     const isWav:boolean = newInput.toLowerCase().endsWith('.wav')
     const isWav:boolean = newInput.toLowerCase().endsWith('.wav')
-      ||newInput.toLowerCase().endsWith('.ape');
+      ||newInput.toLowerCase().endsWith('.ape')
+      ||newInput.toLowerCase().endsWith('.dsf')
+      ||newInput.toLowerCase().endsWith('.dff');
     //内嵌下标签的值,设置overwrite为true会直接修改原文件
     //内嵌下标签的值,设置overwrite为true会直接修改原文件
     const result:boolean = await repairAudioMetadata(
     const result:boolean = await repairAudioMetadata(
       newInput,
       newInput,
@@ -7860,7 +7862,7 @@ export struct LocalMusic {
         if (result) {
         if (result) {
           console.log(" onecold 编辑信息成功,数据库已同步");
           console.log(" onecold 编辑信息成功,数据库已同步");
           if(isWav){
           if(isWav){
-            ToastUtil.showToast('wav或者ape格式有可能内嵌失败,但是编辑信息成功')
+            ToastUtil.showToast('dsf、wav、dff或者ape格式有可能内嵌失败,但是编辑信息成功')
           }else{
           }else{
             ToastUtil.showToast('内嵌音乐标签成功')
             ToastUtil.showToast('内嵌音乐标签成功')
           }
           }
@@ -10242,7 +10244,6 @@ export struct LocalMusic {
     this.loadLyricTimeOffset()
     this.loadLyricTimeOffset()
     this.lyricController.setLyric(null)
     this.lyricController.setLyric(null)
     this.lyricControllerSingle.setLyric(null)
     this.lyricControllerSingle.setLyric(null)
-    let jiaMilyricPath = lyricPath.substring(0, lyricPath.lastIndexOf('.')) + '.lrcc'
     console.log("onecold lyricPath =" + lyricPath)
     console.log("onecold lyricPath =" + lyricPath)
 
 
     // 1. 检查内嵌歌词
     // 1. 检查内嵌歌词
@@ -10307,34 +10308,23 @@ export struct LocalMusic {
     }
     }
 
 
     // 3. 尝试在线歌词API
     // 3. 尝试在线歌词API
-    if (isOnLineAndToast || ((!FileUtil.accessSync(lyricPath) && !FileUtil.accessSync(jiaMilyricPath))
+    if (isOnLineAndToast || ((!FileUtil.accessSync(lyricPath))
       && !Utility.isVideoByExtension(this.videoUrl))) {
       && !Utility.isVideoByExtension(this.videoUrl))) {
       this.fetchOnlineLyric(lyricPath, isOnLineAndToast || false, isApi2);
       this.fetchOnlineLyric(lyricPath, isOnLineAndToast || false, isApi2);
       return;
       return;
     }
     }
 
 
     // 4. 从本地文件读取歌词
     // 4. 从本地文件读取歌词
-    await this.loadLyricFromFile(lyricPath, jiaMilyricPath, isLocal || false);
+    await this.loadLyricFromFile(lyricPath, isLocal || false);
   }
   }
 
 
   /**
   /**
    * 从本地文件加载歌词
    * 从本地文件加载歌词
    */
    */
-  private async loadLyricFromFile(lyricPath: string, jiaMilyricPath: string, isLocal: boolean): Promise<void> {
+  private async loadLyricFromFile(lyricPath: string,  isLocal: boolean): Promise<void> {
     try {
     try {
-      let isJiaMi = false;
       let realLyricPath = lyricPath;
       let realLyricPath = lyricPath;
 
 
-      if (FileUtil.accessSync(jiaMilyricPath)) {
-        isJiaMi = true;
-        realLyricPath = jiaMilyricPath;
-        console.info("onecold 找到加密本地歌词 ");
-      } else {
-        isJiaMi = false;
-        realLyricPath = lyricPath;
-        console.info("onecold 找到本地歌词 ");
-      }
-
       // 读取文件内容
       // 读取文件内容
       let file = fs.openSync(realLyricPath, fs.OpenMode.READ_ONLY);
       let file = fs.openSync(realLyricPath, fs.OpenMode.READ_ONLY);
       const stat = await fileIo.stat(file.fd);
       const stat = await fileIo.stat(file.fd);
@@ -10360,10 +10350,6 @@ export struct LocalMusic {
             let fileContent = textDecoder.decode(buf);
             let fileContent = textDecoder.decode(buf);
             console.info(`onecoldT The content of file1: ${fileContent}`);
             console.info(`onecoldT The content of file1: ${fileContent}`);
 
 
-            // 解密
-            if (isJiaMi) {
-              fileContent = StrUtil.unit8ArrayToStr(Base64Util.decodeSync(fileContent));
-            }
 
 
             console.info(`onecold The content of file2: ${fileContent}`);
             console.info(`onecold The content of file2: ${fileContent}`);
             this.lyricContent = fileContent;
             this.lyricContent = fileContent;
@@ -10401,8 +10387,14 @@ export struct LocalMusic {
   detect(data: ArrayBuffer): string {
   detect(data: ArrayBuffer): string {
     //创建检测对象
     //创建检测对象
     let detector: UniversalDetector = new UniversalDetector();
     let detector: UniversalDetector = new UniversalDetector();
+
+    // 限制检测数据大小,避免内存溢出
+    // 通常只需要检测文件前16KB就足够判断编码格式
+    const MAX_DETECT_SIZE = 16 * 1024; // 16KB
+    const detectSize = Math.min(data.byteLength, MAX_DETECT_SIZE);
+
     // 传入检测数据并实时判断编码格式
     // 传入检测数据并实时判断编码格式
-    detector.handleData(data, 0, data.byteLength);
+    detector.handleData(data, 0, detectSize);
     // 标记检测数据 读取结束
     // 标记检测数据 读取结束
     detector.dataEnd();
     detector.dataEnd();
     // 获取检测结果
     // 获取检测结果
@@ -10410,6 +10402,8 @@ export struct LocalMusic {
     // 释放资源
     // 释放资源
     detector.reset();
     detector.reset();
 
 
+    Logger.info(TAG, "heanup detect encoding: " + detected + ", data size: " + data.byteLength + ", detect size: " + detectSize);
+
     return detected;
     return detected;
   }
   }
 
 
@@ -10435,7 +10429,7 @@ export struct LocalMusic {
 
 
     // 如果提供了保存路径,保存歌词到本地
     // 如果提供了保存路径,保存歌词到本地
     if (savePath) {
     if (savePath) {
-      this.saveDataToFile(lyricText, savePath, false);
+      this.saveDataToFile(lyricText, savePath);
     }
     }
   }
   }
 
 
@@ -10535,18 +10529,8 @@ export struct LocalMusic {
     try {
     try {
       let filePath = item.filePath;
       let filePath = item.filePath;
       let lyricPath = filePath.substring(0, filePath.lastIndexOf('.')) + '.lrc';
       let lyricPath = filePath.substring(0, filePath.lastIndexOf('.')) + '.lrc';
-      let jiaMilyricPath = lyricPath.substring(0, lyricPath.lastIndexOf('.')) + '.lrcc';
-      let isJiaMi = false;
       let realLyricPath = lyricPath;
       let realLyricPath = lyricPath;
 
 
-      if (FileUtil.accessSync(jiaMilyricPath)) {
-        isJiaMi = true;
-        realLyricPath = jiaMilyricPath;
-        console.info("onecold 找到加密本地歌词");
-      } else {
-        console.info("onecold 找到本地歌词");
-      }
-
       let file = fs.openSync(realLyricPath,  fs.OpenMode.READ_ONLY);
       let file = fs.openSync(realLyricPath,  fs.OpenMode.READ_ONLY);
       const stat = await fileIo.stat(file.fd);
       const stat = await fileIo.stat(file.fd);
       const arrayBuffer = new ArrayBuffer(stat.size);
       const arrayBuffer = new ArrayBuffer(stat.size);
@@ -10568,11 +10552,6 @@ export struct LocalMusic {
       let fileContent = textDecoder.decode(buf);
       let fileContent = textDecoder.decode(buf);
       console.info(`onecold The content of file1: ${fileContent}`);
       console.info(`onecold The content of file1: ${fileContent}`);
 
 
-      if (isJiaMi) {
-        fileContent = StrUtil.unit8ArrayToStr(Base64Util.decodeSync(fileContent));
-      }
-      console.info(`onecold The content of file2: ${fileContent}`);
-
       fs.closeSync(file);
       fs.closeSync(file);
       return fileContent;
       return fileContent;
 
 
@@ -10582,21 +10561,14 @@ export struct LocalMusic {
     }
     }
   }
   }
 
 
-  async saveDataToFile(content: string, filePath: string, isJiaMi?: boolean) {
+  async saveDataToFile(content: string, filePath: string) {
     if (StrUtil.isEmpty(filePath)) {
     if (StrUtil.isEmpty(filePath)) {
       return
       return
     }
     }
 
 
     try {
     try {
-      //将content进行加密
-      // let jiaMilyricPath = filePath.substring(0,filePath.lastIndexOf('.'))+'.lrcc'
       let lyricContent = ''
       let lyricContent = ''
-      if (isJiaMi) {
-        lyricContent = Base64Util.encodeToStrSync(StrUtil.strToUint8Array(content))
-      } else {
-        lyricContent = content
-      }
-
+      lyricContent = content
       const file = await fileIo.open(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
       const file = await fileIo.open(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
       // 清空文件内容
       // 清空文件内容
       await fileIo.truncate(file.fd, 0);
       await fileIo.truncate(file.fd, 0);
@@ -12882,7 +12854,8 @@ export struct LocalMusic {
                     .fontSize(24)
                     .fontSize(24)
                     .fontColor([Color.White])
                     .fontColor([Color.White])
                     .alignSelf(ItemAlign.Center)
                     .alignSelf(ItemAlign.Center)
-                    .margin({ left: 10, right: 10 })
+                    .margin({ left: 10, right: 10,bottom:5})
+                    // .padding({bottom:4})
                     .onClick(() => {
                     .onClick(() => {
                       this.doMore(more.id)
                       this.doMore(more.id)
                     })
                     })
@@ -12898,7 +12871,7 @@ export struct LocalMusic {
                 }
                 }
                 Text(more.id == 17 ? (this.isLandscape ? '竖屏模式' : '横屏模式') : more.title)
                 Text(more.id == 17 ? (this.isLandscape ? '竖屏模式' : '横屏模式') : more.title)
                   .fontSize(16)
                   .fontSize(16)
-
+                  .margin({top:more.id >= 22?3:0})
                   .fontColor(Color.White)
                   .fontColor(Color.White)
                   .onClick(() => {
                   .onClick(() => {
                     this.doMore(more.id)
                     this.doMore(more.id)
@@ -13064,6 +13037,7 @@ export struct LocalMusic {
                   .font({ size: 14, weight: FontWeight.Medium })
                   .font({ size: 14, weight: FontWeight.Medium })
                   .fontColor(Color.White)
                   .fontColor(Color.White)
                   .margin({ left: 25 })
                   .margin({ left: 25 })
+                  .margin({top:more.id >= 22?3:0})
                   .visibility(more.id === 26 ? Visibility.Visible : Visibility.None)
                   .visibility(more.id === 26 ? Visibility.Visible : Visibility.None)
                   .selected(this.spectrumModeIndex)
                   .selected(this.spectrumModeIndex)
                   .value(['柱状', '波形', '圆形', '粒子', '脉冲', '镜像柱', '影子柱', '轨道线', '点阵', '星爆'][this.spectrumModeIndex])
                   .value(['柱状', '波形', '圆形', '粒子', '脉冲', '镜像柱', '影子柱', '轨道线', '点阵', '星爆'][this.spectrumModeIndex])

+ 19 - 45
entry/src/main/ets/view/spectrum/BarSpectrumRenderer.ets

@@ -10,30 +10,8 @@ export class BarSpectrumRenderer implements SpectrumRenderer {
 
 
   render(ctx: CanvasRenderingContext2D, bands: number[], energy: number,
   render(ctx: CanvasRenderingContext2D, bands: number[], energy: number,
     width: number, height: number, brandColor: string, _options?: SpectrumRenderOptions): void {
     width: number, height: number, brandColor: string, _options?: SpectrumRenderOptions): void {
-    const originalBandCount: number = bands.length
-    if (originalBandCount === 0) return
-
-    // 根据宽度动态计算最佳柱子数量和尺寸
-    const targetBarWidth: number = 2.5 // 目标柱子宽度
-    const gap: number = 1.5 // 柱子之间的间隙
-
-    // 计算在这个宽度下能容纳多少个柱子
-    let optimalBarCount: number = Math.floor(width / (targetBarWidth + gap))
-    optimalBarCount = Math.min(optimalBarCount, originalBandCount) // 不超过原始频段数
-    optimalBarCount = Math.max(optimalBarCount, 16) // 至少16个柱子
-
-    // 根据优化后的柱子数量计算实际柱子宽度
-    const actualBarWidth: number = (width - gap * (optimalBarCount + 1)) / optimalBarCount
-
-    // 重采样 bands 数据以匹配 optimalBarCount
-    const resampledBands: number[] = []
-    const step: number = originalBandCount / optimalBarCount
-    for (let i = 0; i < optimalBarCount; i++) {
-      const sourceIndex: number = Math.min(Math.floor(i * step), originalBandCount - 1)
-      resampledBands.push(bands[sourceIndex])
-    }
-
-    const bandCount: number = optimalBarCount
+    const bandCount: number = bands.length
+    if (bandCount === 0) return
 
 
     // 初始化峰值数组
     // 初始化峰值数组
     if (this.peaks.length !== bandCount) {
     if (this.peaks.length !== bandCount) {
@@ -48,7 +26,11 @@ export class BarSpectrumRenderer implements SpectrumRenderer {
     // 轻微拖影,接近参考图中的荧光残影(使用透明背景)
     // 轻微拖影,接近参考图中的荧光残影(使用透明背景)
     ctx.clearRect(0, 0, width, height)
     ctx.clearRect(0, 0, width, height)
 
 
-    const usableHeight: number = height * 0.80
+    const gap: number = 1.0 // 减小间隙,增加柱子列数
+    const barWidth: number = 5 // 固定柱子宽度
+    const totalSpectrumWidth: number = bandCount * barWidth + (bandCount - 1) * gap
+    const startX: number = (width - totalSpectrumWidth) / 2 // 居中起始位置
+    const usableHeight: number = height * 0.60 // 减小最大高度
     const segmentHeight: number = Math.max(2, height * 0.012) // 减小单段高度
     const segmentHeight: number = Math.max(2, height * 0.012) // 减小单段高度
     const segmentGap: number = 0 // 去掉间隙,让方格紧密相连
     const segmentGap: number = 0 // 去掉间隙,让方格紧密相连
     const segmentCount: number = Math.max(10, Math.floor(usableHeight / (segmentHeight + segmentGap)))
     const segmentCount: number = Math.max(10, Math.floor(usableHeight / (segmentHeight + segmentGap)))
@@ -57,14 +39,10 @@ export class BarSpectrumRenderer implements SpectrumRenderer {
 
 
     // 地平线已移除,保持透明背景
     // 地平线已移除,保持透明背景
 
 
-    // 计算起始位置,使柱子居中
-    const totalContentWidth: number = bandCount * actualBarWidth + (bandCount - 1) * gap
-    const startX: number = (width - totalContentWidth) / 2
-
     for (let i = 0; i < bandCount; i++) {
     for (let i = 0; i < bandCount; i++) {
-      const level: number = Math.max(0, Math.min(1, resampledBands[i]))
+      const level: number = Math.max(0, Math.min(1, bands[i]))
       const activeSegments: number = Math.floor(level * segmentCount)
       const activeSegments: number = Math.floor(level * segmentCount)
-      const x: number = startX + i * (actualBarWidth + gap)
+      const x: number = startX + i * (barWidth + gap)
       const hue: number = (palette.primaryHue + (i / bandCount) * hueSpan) % 360
       const hue: number = (palette.primaryHue + (i / bandCount) * hueSpan) % 360
 
 
       for (let seg = 0; seg < segmentCount; seg++) {
       for (let seg = 0; seg < segmentCount; seg++) {
@@ -81,11 +59,11 @@ export class BarSpectrumRenderer implements SpectrumRenderer {
         const alpha: number = 0.55 + level * 0.45
         const alpha: number = 0.55 + level * 0.45
         ctx.globalAlpha = alpha
         ctx.globalAlpha = alpha
         ctx.fillStyle = hslToHex(hue, 0.86, lightness)
         ctx.fillStyle = hslToHex(hue, 0.86, lightness)
-        ctx.fillRect(x, segY, actualBarWidth, segmentHeight)
+        ctx.fillRect(x, segY, barWidth, segmentHeight)
       }
       }
       ctx.globalAlpha = 1.0
       ctx.globalAlpha = 1.0
 
 
-      // 峰值下落指示器(使用与当前柱子相同的颜色
+      // 峰值下落指示器(亮点
       if (level > this.peaks[i]) {
       if (level > this.peaks[i]) {
         this.peaks[i] = level
         this.peaks[i] = level
         this.peakVelocities[i] = 0
         this.peakVelocities[i] = 0
@@ -96,18 +74,14 @@ export class BarSpectrumRenderer implements SpectrumRenderer {
       }
       }
 
 
       const peakSeg: number = Math.floor(this.peaks[i] * segmentCount)
       const peakSeg: number = Math.floor(this.peaks[i] * segmentCount)
-      if (peakSeg >= 0) {
-        const peakY: number = height - (peakSeg + 1) * (segmentHeight + segmentGap)
-
-        // 使用当前柱子的颜色作为峰值指示器颜色
-        const currentPeakLightness: number = 0.34 + (this.peaks[i] || 0) * 0.34
-        const currentPeakColor: string = hslToHex(hue, 0.86, currentPeakLightness)
-
-        ctx.fillStyle = currentPeakColor
-        ctx.globalAlpha = 0.95
-        ctx.fillRect(x, peakY, actualBarWidth, segmentHeight + 1)
-        ctx.globalAlpha = 1.0
-      }
+      const peakY: number = height - (peakSeg + 1) * (segmentHeight + segmentGap)
+      // 使用当前柱子的颜色
+      const peakT: number = peakSeg / segmentCount
+      const peakLightness: number = 0.34 + peakT * 0.34
+      ctx.fillStyle = hslToHex(hue, 0.86, peakLightness)
+      ctx.globalAlpha = 0.95
+      ctx.fillRect(x, peakY, barWidth, segmentHeight + 1)
+      ctx.globalAlpha = 1.0
     }
     }
   }
   }
 
 

+ 3 - 2
entry/src/main/ets/view/spectrum/SpectrumView.ets

@@ -14,7 +14,7 @@ import { normalizeHexColor } from './SpectrumRenderer'
 import Logger from '../../common/util/Logger'
 import Logger from '../../common/util/Logger'
 
 
 const TAG: string = 'heanup-SpectrumView'
 const TAG: string = 'heanup-SpectrumView'
-const BAND_COUNT: number = 48 // 频段数量
+const BAND_COUNT: number = 53 // 频段数量
 const PREFERENCE_SELECTED_EFFECT: string = 'selectedSpectrumEffectId'
 const PREFERENCE_SELECTED_EFFECT: string = 'selectedSpectrumEffectId'
 
 
 @Component
 @Component
@@ -174,7 +174,8 @@ export struct SpectrumView {
 
 
     const spectrumData = this.fftAnalyzer.analyze(pcmData, 44100)
     const spectrumData = this.fftAnalyzer.analyze(pcmData, 44100)
     const scaledBandCount: number = Math.max(8, Math.floor(effect.performance.maxBandCount * this.complexityScale))
     const scaledBandCount: number = Math.max(8, Math.floor(effect.performance.maxBandCount * this.complexityScale))
-    const rawBands: number[] = spectrumData.getBands(Math.min(BAND_COUNT, scaledBandCount))
+    // const rawBands: number[] = spectrumData.getBands(Math.min(BAND_COUNT, scaledBandCount))
+    const rawBands: number[] = spectrumData.getBands(BAND_COUNT)
     const bands: number[] = this.fftAnalyzer.smooth(rawBands)
     const bands: number[] = this.fftAnalyzer.smooth(rawBands)
     const energy: number = spectrumData.getEnergy()
     const energy: number = spectrumData.getEnergy()