Prechádzať zdrojové kódy

dsf文件高采样率sampleRate>441000,统一转wav播放

onecold 10 mesiacov pred
rodič
commit
620f2eb71c

+ 1 - 1
AppScope/app.json5

@@ -2,7 +2,7 @@
   "app": {
     "bundleName": "com.xgplayer.ttmusic.hm",
     "vendor": "example",
-    "versionCode": 20250929,
+    "versionCode": 20250930,
     "versionName": "1.6.2",
     "icon": "$media:app_icon",
     "label": "$string:app_name",

+ 63 - 0
entry/src/main/ets/common/util/MusicTagUtils.ets

@@ -574,4 +574,67 @@ export async function syncLyricToDB(context:Context,filePath: string,lyric:strin
   } catch (error) {
     console.warn(`查找同名图片文件失败: ${JSON.stringify(error)}`);
   }
+}
+
+
+
+/**
+ * 纯净版DSF转WAV格式转换(不操作媒体库)
+ * @param inputPath 输入的DSF文件路径(如 `/storage/audio.dsf` )
+ * @param outputPath 输出的WAV文件路径(如 `/storage/converted.wav` )
+ * @returns Promise<boolean> 转换成功返回true,失败返回false
+ */
+@Concurrent
+export async function convertDsfToWav(
+  inputPath: string,
+  outputPath: string
+): Promise<boolean> {
+  // 1. 输入文件验证(增强型检查)
+  try {
+    const stats = await fs.stat(inputPath);
+    if (!stats.isFile  || stats.size  < 1024) { // 检查是否为文件且大于1KB
+      console.error(' 输入文件无效或过小');
+      return false;
+    }
+  } catch (error) {
+    console.error(` 文件验证失败: ${ JSON.stringify(error)}`);
+    return false;
+  }
+
+  // 2. FFmpeg命令(优化音频参数)
+  const commands = [
+    "ffmpeg",
+    "-i", inputPath,
+    "-c:a", "pcm_s24le",    // 24位高精度PCM
+    "-ar", "88200",         // 兼容性更好的采样率(96kHz的约数)
+    "-ac", "2",
+    "-fflags", "+genpts",   // 防止时间戳错误
+    "-f", "wav",
+    "-y",
+    outputPath
+  ];
+
+  // 3. 执行转换与结果验证
+  try {
+    await FFmpeg.execute(commands,  {
+      logCallback: (_, msg) => console.debug(`[FFmpeg]  ${msg}`),
+      progressCallback: (msg) => {
+        const p = FFProgressMessageParser.parse(msg);
+
+      }
+    });
+
+    // 4. 输出文件完整性检查
+    const outSize = (await fs.stat(outputPath)).size;
+    if (outSize < 1024) { // WAV文件头至少44字节,实际数据应更大
+      await fs.unlink(outputPath);
+      throw new Error('输出文件不完整');
+    }
+    return true;
+
+  } catch (error) {
+    console.error(` 转换失败: ${error instanceof Error ? error.message  : '未知错误'}`);
+    try { await fs.unlink(outputPath);  } catch {} // 静默清理
+    return false;
+  }
 }

+ 29 - 3
entry/src/main/ets/view/LocalMusic.ets

@@ -30,13 +30,14 @@ import { AnimationHelper, DialogAction, DialogHelper } from '@pura/harmony-dialo
 import { fileIo, fileUri, picker } from '@kit.CoreFileKit';
 import { MessageEvents, util, worker, ErrorEvent } from '@kit.ArkTS';
 import { Verify } from './Verify';
+import { taskpool } from '@kit.ArkTS';
 import { RotatingCover } from './RotatingCover';
 import { PlayConstants } from '../common/constants/PlayConstants';
 import { effectKit } from '@kit.ArkGraphics2D';
 import { ColorConversion } from '../common/util/ColorConversion';
 import { LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
 import { AvSessionController } from '../controller/AvSessionController';
-import { repairAudioMetadata,  getApiLyric,changeMusicCover} from '../common/util/MusicTagUtils';
+import { repairAudioMetadata, convertDsfToWav, getApiLyric,changeMusicCover} from '../common/util/MusicTagUtils';
 import { FFMpegTags,Utility } from '../common/util/Utility';
 import { TagsContentCover } from '../view/TagsContentCover';
 import { ImageFancyModifier } from '../common/util/AttributeModifierUtil'
@@ -5132,9 +5133,7 @@ export struct LocalMusic {
         this.cover = this.currentSong.pixelMapPath
         this.artist = this.currentSong.artist
         LogUtil.info('this.currentSong.duration =' + this.currentSong.duration)
-        LogUtil.info('this.currentSong.sampleRate =' + this.currentSong.sampleRate)
         LogUtil.info('this.currentSongmimeType =' + this.currentSong.mimeType)
-        console.info('onecold startPlayOrResumePlay 4547')
         this.startPlayOrResumePlay()
         break;
 
@@ -11000,6 +10999,33 @@ export struct LocalMusic {
   }
 
   private startPlayOrResumePlay() {
+    //针对dsf文件高采样率sampleRate>441000,统一转wav播放
+    const sampleRate = this.currentSong?.sampleRate||0
+    if(this.videoUrl.toLowerCase().endsWith('.dsf')&&sampleRate>=441000){
+      console.info('onecold 采样率 ='+sampleRate)
+      let newDsfPath = this.context.filesDir + '/'+FileUtil.getFileName(this.videoUrl)+'.wav'
+      console.info('onecold newDsfPath ='+newDsfPath)
+      if(!FileUtil.accessSync(newDsfPath)){
+        const task = new taskpool.Task(convertDsfToWav,this.videoUrl,newDsfPath);
+        taskpool.execute(task, taskpool.Priority.HIGH).then((data)=>{
+          this.videoUrl = newDsfPath
+          this.startPlay()
+        }).catch((e:object)=>{
+          console.info("task1 catch e: " + e);
+        })
+      }else{
+        this.videoUrl = newDsfPath
+        this.startPlay()
+      }
+
+    }else{
+      this.startPlay()
+    }
+
+
+  }
+
+  startPlay() {
     this.startPipLyric()
     this.mDestroyPage = false;
     this.animationState = AnimationStatus.Running