Skip to content

Commit 4c9ba01

Browse files
authored
Merge pull request #45 from Starrah/fix-export-offset
修复#40 导出谱面相关延迟问题,以及重构导入谱面中的padding相关逻辑
2 parents 8dfa241 + 286665d commit 4c9ba01

18 files changed

Lines changed: 279 additions & 149 deletions

File tree

MaiChartManager.CLI/Commands/MakeAcbCommand.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ public class Settings : CommandSettings
2222
[Description("音频填充(秒),正数为前置静音,负数为裁剪开头")]
2323
[DefaultValue(0f)]
2424
public float Padding { get; set; }
25+
26+
[CommandOption("--ignoreGapless")]
27+
[Description("使用MP3兼容模式(忽略音频中的Gapless信息)。如果你使用某些版本的Visual Maimai等制谱器制谱,然后发现导入的谱面有对音问题的话,可以尝试开启此项")]
28+
[DefaultValue(false)]
29+
public bool IgnoreGapless { get; set; }
2530

2631
public override ValidationResult Validate()
2732
{
@@ -87,7 +92,8 @@ await Task.Run(() =>
8792
Audio.ConvertToMai(
8893
srcPath: source,
8994
savePath: output,
90-
padding: settings.Padding
95+
padding: settings.Padding,
96+
forceUseNAudio: settings.IgnoreGapless
9197
);
9298
});
9399
});
@@ -131,7 +137,8 @@ await Task.Run(() =>
131137
Audio.ConvertToMai(
132138
srcPath: source,
133139
savePath: output,
134-
padding: settings.Padding
140+
padding: settings.Padding,
141+
forceUseNAudio: settings.IgnoreGapless
135142
);
136143
});
137144
doneCount++;

MaiChartManager/Controllers/Charts/ImportChartController.cs

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,7 @@ namespace MaiChartManager.Controllers.Charts;
1111
public class ImportChartController(StaticSettings settings, ILogger<StaticSettings> logger,
1212
MaidataImportService importService) : ControllerBase
1313
{
14-
private static float getFirstBarFromChart(MaiChart chart)
15-
{
16-
var bpm = chart.TimingChanges[0].tempo;
17-
if (bpm == 0)
18-
{
19-
throw new DivideByZeroException(Locale.ChartBpmZero);
20-
}
21-
22-
return 60 / bpm * 4;
23-
}
24-
25-
public record ImportChartCheckResult(bool Accept, IEnumerable<ImportChartMessage> Errors, float MusicPadding, bool IsDx, string? Title, float first, float bar);
14+
public record ImportChartCheckResult(bool Accept, IEnumerable<ImportChartMessage> Errors, Dictionary<ShiftMethod, float> chartPaddings, bool IsDx, string? Title, float first);
2615

2716
[HttpPost]
2817
public ImportChartCheckResult ImportChartCheck(IFormFile file, [FromForm] bool isReplacement = false)
@@ -110,12 +99,12 @@ public ImportChartCheckResult ImportChartCheck(IFormFile file, [FromForm] bool i
11099
{
111100
errors.Add(new ImportChartMessage(Locale.MusicNoCharts, MessageLevel.Fatal));
112101
fatal = true;
113-
return new ImportChartCheckResult(!fatal, errors, 0, false, title, 0, 0);
102+
return new ImportChartCheckResult(!fatal, errors, new Dictionary<ShiftMethod, float>(), false, title, 0);
114103
}
115104

116-
var paddings = new List<float>();
117105
float.TryParse(maiData.GetValueOrDefault("first"), out var first);
118106
var isDx = false;
107+
var maiCharts = new List<MaiChart>();
119108

120109
foreach (var kvp in allChartText)
121110
{
@@ -135,7 +124,7 @@ public ImportChartCheckResult ImportChartCheck(IFormFile file, [FromForm] bool i
135124
try
136125
{
137126
var chart = importService.TryParseChartSimaiSharp(chartText, kvp.Key, errors);
138-
paddings.Add(MaidataImportService.CalcMusicPadding(chart, first));
127+
maiCharts.Add(chart);
139128

140129
var candidate = importService.TryParseChart(chartText, chart, kvp.Key, errors);
141130
if (candidate is null) throw new Exception(Locale.ChartParseGenericError);
@@ -151,19 +140,16 @@ public ImportChartCheckResult ImportChartCheck(IFormFile file, [FromForm] bool i
151140
foreachAllChartTextContinue: ;
152141
}
153142

154-
var padding = paddings.Max();
155-
156-
// 计算 bar
157-
var bar = getFirstBarFromChart(importService.TryParseChartSimaiSharp(allChartText.First().Value, allChartText.First().Key, errors));
143+
var chartPaddings = MaidataImportService.CalcChartPadding(maiCharts, out _);
158144

159-
return new ImportChartCheckResult(!fatal, errors, padding, isDx, title, first, bar);
145+
return new ImportChartCheckResult(!fatal, errors, chartPaddings, isDx, title, first);
160146
}
161147
catch (Exception e)
162148
{
163149
logger.LogError(e, "解析谱面失败(大)");
164150
errors.Add(new ImportChartMessage(Locale.ChartParseFailedGlobal, MessageLevel.Fatal));
165151
fatal = true;
166-
return new ImportChartCheckResult(!fatal, errors, 0, false, "", 0, 0);
152+
return new ImportChartCheckResult(!fatal, errors, new Dictionary<ShiftMethod, float>(), false, "", 0);
167153
}
168154
}
169155

MaiChartManager/Controllers/Music/CueConvertController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public async Task<ActionResult> GetMusicWav(int id, string assetDir)
2424

2525
[HttpPut]
2626
[DisableRequestSizeLimit]
27-
public void SetAudio(int id, [FromForm] float padding, IFormFile file, IFormFile? awb, IFormFile? preview, string assetDir)
27+
public void SetAudio(int id, [FromForm] float padding, IFormFile file, IFormFile? awb, IFormFile? preview, string assetDir, [FromForm] bool ignoreGapless = false)
2828
{
2929
id %= 10000;
3030
var targetAcbPath = Path.Combine(StaticSettings.StreamingAssets, assetDir, $@"SoundData\music{id:000000}.acb");
@@ -41,7 +41,7 @@ public void SetAudio(int id, [FromForm] float padding, IFormFile file, IFormFile
4141
}
4242
else
4343
{
44-
Audio.ConvertToMai(file.FileName, targetAcbPath, padding, file.OpenReadStream(), preview?.FileName, preview?.OpenReadStream());
44+
Audio.ConvertToMai(file.FileName, targetAcbPath, padding, file.OpenReadStream(), preview?.FileName, preview?.OpenReadStream(), forceUseNAudio: ignoreGapless);
4545
}
4646

4747
StaticSettings.AcbAwb[$"music{id:000000}.acb"] = targetAcbPath;

MaiChartManager/Controllers/Music/MusicTransferController.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
using Microsoft.AspNetCore.Mvc;
99
using Microsoft.VisualBasic.FileIO;
1010
using NAudio.Lame;
11-
using SimaiSharp;
1211
using Vanara.Windows.Forms;
13-
using Xabe.FFmpeg;
1412
using FolderBrowserDialog = System.Windows.Forms.FolderBrowserDialog;
1513

1614
namespace MaiChartManager.Controllers.Music;
@@ -555,7 +553,7 @@ public async Task ExportAsMaidata(int id, string assetDir, bool ignoreVideo = fa
555553
simaiFile.AppendLine($"&title={music.Name}");
556554
simaiFile.AppendLine($"&artist={music.Artist}");
557555
simaiFile.AppendLine($"&wholebpm={music.Bpm}");
558-
simaiFile.AppendLine("&first=0.0333");
556+
simaiFile.AppendLine("&first=0");
559557
simaiFile.AppendLine($"&shortid={music.Id}");
560558
simaiFile.AppendLine($"&genreid={music.GenreId}");
561559
var genre = StaticSettings.GenreList.FirstOrDefault(it => it.Id == music.GenreId);
@@ -622,6 +620,7 @@ public async Task ExportAsMaidata(int id, string assetDir, bool ignoreVideo = fa
622620
imageStream.Close();
623621
}
624622

623+
// 导出音频
625624
var soundEntry = zipArchive.CreateEntry("track.mp3");
626625
await using var soundStream = soundEntry.Open();
627626
var tag = new ID3TagData

MaiChartManager/Controllers/Tools/AudioConvertToolController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private IActionResult ConvertAcbAwbToMp3(string inputFile, string directory, str
8787
/// <summary>
8888
/// 将音频文件转换为 ACB/AWB
8989
/// </summary>
90-
private IActionResult ConvertToAcbAwb(string inputFile, string directory, string fileNameWithoutExt, string extension)
90+
private IActionResult ConvertToAcbAwb(string inputFile, string directory, string fileNameWithoutExt, string extension, bool ignoreGapless = false)
9191
{
9292
string tempAudioFile = null;
9393

@@ -108,7 +108,7 @@ private IActionResult ConvertToAcbAwb(string inputFile, string directory, string
108108
string awbPath = Path.Combine(directory, fileNameWithoutExt + ".awb");
109109

110110
// 执行转换
111-
Audio.ConvertToMai(actualInputFile, acbPath);
111+
Audio.ConvertToMai(actualInputFile, acbPath, forceUseNAudio: ignoreGapless);
112112

113113
return Ok(new { message = Locale.ConvertSuccess, acbPath = acbPath, awbPath = awbPath });
114114
}

MaiChartManager/Front/src/client/apiGen.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,14 +264,18 @@ export interface ISectionState {
264264
export interface ImportChartCheckResult {
265265
accept?: boolean;
266266
errors?: ImportChartMessage[] | null;
267-
/** @format float */
268-
musicPadding?: number;
267+
chartPaddings?: {
268+
/** @format float */
269+
Legacy?: number;
270+
/** @format float */
271+
Bar?: number;
272+
/** @format float */
273+
NoShift?: number;
274+
} | null;
269275
isDx?: boolean;
270276
title?: string | null;
271277
/** @format float */
272278
first?: number;
273-
/** @format float */
274-
bar?: number;
275279
}
276280

277281
export interface ImportChartMessage {
@@ -1278,6 +1282,8 @@ export class Api<
12781282
awb?: File;
12791283
/** @format binary */
12801284
preview?: File;
1285+
/** @default false */
1286+
ignoreGapless?: boolean;
12811287
},
12821288
params: RequestParams = {},
12831289
) =>

MaiChartManager/Front/src/locales/en.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ chart:
253253
present)
254254
255255
May cause first note to appear at odd timing, e.g. immediately at start
256+
ignoreGapless: MP3 compatibility mode (ignore Gapless)
257+
ignoreGaplessTip: If the chart was made with some specific chart editors, such as certain versions of Visual Maimai, and the imported chart has timing issues, you can try enabling this option.
256258
codecForceH264: H264
257259
codecForceVP9: VP9 USM
258260
assignId: Assign ID for new imported songs
@@ -264,8 +266,9 @@ chart:
264266
Will add {padding} seconds of silence to ensure first note is in second
265267
bar
266268
trimPadding: Will trim {padding} seconds of audio to ensure first note is in second bar
267-
addFirst: Will add {first} seconds of silence to match &first value
268-
trimFirst: Will trim {first} seconds of audio to match &first value
269+
addFirst: Will add {padding} seconds of silence to match &first value
270+
trimFirst: Will trim {padding} seconds of audio to match &first value
271+
addBar: Will add 1 bar of silence at the beginning of the chart to ensure the first note is after the second bar
269272
error:
270273
noMaidata: maidata.txt not found
271274
noAudio: Audio file not found

MaiChartManager/Front/src/locales/zh-TW.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,18 @@ chart:
235235
shiftNoMoveDesc: |-
236236
完全不修改譜面,從音訊中刪除 &first 的長度(如果有)
237237
可能會導致第一個音符出現的時機比較奇怪,比如說剛開始就有音符
238+
ignoreGapless: MP3 相容模式(忽略 Gapless)
239+
ignoreGaplessTip: 如果你使用某些版本的 Visual Maimai 等製譜器製譜,然後發現匯入的譜面有對音問題的話,可以嘗試開啟此項進行匯入。
238240
codecForceH264: H264
239241
codecForceVP9: VP9 USM
240242
assignId: 為新匯入的歌曲指定 ID
241243
currentProcessing: 目前正在處理的項目
242244
folderHint: 包含以下檔案的資料夾,或者,一個包含一些這樣的資料夾的資料夾
243245
addPadding: 將在音訊前面加上 {padding} 秒空白以保證第一押在第二小節
244246
trimPadding: 將裁剪 {padding} 秒音訊以保證第一押在第二小節
245-
addFirst: 將在音訊前面加上 {first} 秒空白以對應 &first 的值
246-
trimFirst: 將裁剪 {first} 秒音訊以對應 &first 的值
247+
addFirst: 將在音訊前面加上 {padding} 秒空白以對應 &first 的值
248+
trimFirst: 將裁剪 {padding} 秒音訊以對應 &first 的值
249+
addBar: 將在譜面開頭加上1小節空白,以保證第一押出現在第二小節之後
247250
error:
248251
noMaidata: 未找到 maidata.txt
249252
noAudio: 未找到音訊檔案

MaiChartManager/Front/src/locales/zh.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,18 @@ chart:
235235
shiftNoMoveDesc: |-
236236
完全不修改谱面,从音频中删除 &first 的长度(如果有)
237237
可能会导致第一个音符出现的时机比较奇怪,比如说刚开始就有音符
238+
ignoreGapless: MP3兼容模式(忽略Gapless)
239+
ignoreGaplessTip: 如果你使用某些版本的Visual Maimai等制谱器制谱,然后发现导入的谱面有对音问题的话,可以尝试开启此项进行导入。
238240
codecForceH264: H264
239241
codecForceVP9: VP9 USM
240242
assignId: 为新导入的歌曲指定 ID
241243
currentProcessing: 当前正在处理的项目
242244
folderHint: 包含以下文件的文件夹,或者,一个包含一些这样的文件夹的文件夹
243245
addPadding: 将在音频前面加上 {padding} 秒空白以保证第一押在第二小节
244246
trimPadding: 将裁剪 {padding} 秒音频以保证第一押在第二小节
245-
addFirst: 将在音频前面加上 {first} 秒空白以对应 &first 的值
246-
trimFirst: 将裁剪 {first} 秒音频以对应 &first 的值
247+
addFirst: 将在音频前面加上 {padding} 秒空白以对应 &first 的值
248+
trimFirst: 将裁剪 {padding} 秒音频以对应 &first 的值
249+
addBar: 将在谱面开头加上1小节空白,以保证第一押出现在第二小节之后
247250
error:
248251
noMaidata: 未找到 maidata.txt
249252
noAudio: 未找到音频文件

MaiChartManager/Front/src/views/Charts/ImportCreateChartButton/ImportChartButton/ErrorDisplayIdInput.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { computed, defineComponent, effect, PropType, watch } from "vue";
2-
import { Button, CheckBox, Modal, NumberInput, Section } from "@munet/ui";
2+
import { Button, CheckBox, Modal, NumberInput, Popover, Section } from "@munet/ui";
33
import { ImportChartMessage, MessageLevel, ShiftMethod } from "@/client/apiGen";
44
import { ImportChartMessageEx, ImportMeta, SavedOptions, TempOptions } from "./types";
55
import noJacket from '@/assets/noJacket.webp';
@@ -70,6 +70,15 @@ export default defineComponent({
7070
</CheckBox>
7171
<Section title={t('chart.import.option.advancedOptions')}>
7272
<ShiftModeSelector tempOptions={props.tempOptions}></ShiftModeSelector>
73+
<div class="flex items-center gap-1" style="margin-top: 0.25rem">
74+
<CheckBox v-model:value={props.tempOptions.ignoreGapless}>{t('chart.import.option.ignoreGapless')}</CheckBox>
75+
<Popover trigger="hover">
76+
{{
77+
trigger: () => <div class="i-material-symbols:info-outline-rounded op-50"/>,
78+
default: () => <div class="max-w-60">{t('chart.import.option.ignoreGaplessTip')}</div>
79+
}}
80+
</Popover>
81+
</div>
7382
</Section>
7483
</>}
7584
</div>,

0 commit comments

Comments
 (0)