Skip to content

Commit 95d95af

Browse files
authored
Merge pull request #10 from pnck/dev/feat_retagging
File Editing Support
2 parents 3159067 + 22d0b0a commit 95d95af

55 files changed

Lines changed: 2733 additions & 538 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.clang-format

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
AccessModifierOffset: -4
33

44
# Align parameters on the open bracket
5-
AlignAfterOpenBracket: Align
5+
AlignAfterOpenBracket: 'Align'
66

77
# Disallows contracting simple braced statements to a single line
88
AllowShortBlocksOnASingleLine: 'false'
@@ -61,4 +61,19 @@ PenaltyReturnTypeOnItsOwnLine: 10000
6161
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, GIVEN, WHEN, AND_WHEN, THEN, AND_THEN, SECTION ]
6262

6363
SortIncludes: 'false'
64-
#...
64+
65+
# https://stackoverflow.com/questions/73451931/changing-the-location-of-the-line-continuations-used-in-macros-with-clang
66+
AlignEscapedNewlines: Left
67+
68+
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#breakafterattributes
69+
BreakAfterAttributes: 'Leave'
70+
71+
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#alignarrayofstructures
72+
AlignArrayOfStructures: 'Left'
73+
74+
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#binpackarguments
75+
BinPackArguments: 'false'
76+
BinPackParameters: 'false'
77+
78+
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#alignoperands
79+
AlignOperands: 'AlignAfterOperator'

.github/workflows/build-publish.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ jobs:
8181
run: |
8282
TAG=$(git describe --tags)
8383
VERSION=${TAG:1}
84+
echo "" >> src/stdafx.h
8485
echo "#define CURRENT_VERSION $VERSION" >> src/stdafx.h
8586
echo "----------------------------"
8687
cat src/stdafx.h
@@ -109,7 +110,7 @@ jobs:
109110
env:
110111
scheme: foo_input_ncm-${{ matrix.config }}
111112
run: |
112-
xcodebuild clean build -workspace ${{ matrix.workspace }} -scheme "$scheme" -configuration ${{ matrix.config }}
113+
xcodebuild clean build -workspace ${{ matrix.workspace }} -scheme "$scheme" -configuration ${{ matrix.config }} -sdk macosx -mmacosx-version-min=13.3
113114
if: matrix.os == 'macos-14'
114115

115116
- name: (MSBuild) Setup MSBuild

.github/workflows/build.yml

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ on:
66

77
pull_request:
88
branches: ['main']
9-
9+
1010
# Allows you to run this workflow manually from the Actions tab
1111
workflow_dispatch:
1212

1313
jobs:
14-
test:
14+
test-windows:
1515
name: Test (Windows)
1616
runs-on: windows-latest
1717
strategy:
@@ -45,18 +45,53 @@ jobs:
4545
4646
- name: Run Tests (x86)
4747
working-directory: ${{ github.workspace }}/${{ matrix.config }}
48-
run: .\foo_input_ncm_tests.exe
48+
run: .\foo_input_ncm_tests.exe --gtest_repeat=10 --gtest_shuffle
4949
if: matrix.arch == 'x86'
5050

5151
- name: Run Tests (x64)
5252
working-directory: ${{ github.workspace }}/${{ matrix.arch }}/${{ matrix.config }}
53-
run: .\foo_input_ncm_tests.exe
53+
run: .\foo_input_ncm_tests.exe --gtest_repeat=10 --gtest_shuffle
5454
if: matrix.arch == 'x64'
5555

56+
test-macos:
57+
name: Test (macOS)
58+
runs-on: macos-14
59+
strategy:
60+
fail-fast: false
61+
matrix:
62+
config: [Release, Debug]
63+
steps:
64+
- name: Checkout
65+
uses: actions/checkout@v4
66+
with:
67+
submodules: recursive
68+
69+
- name: Download Foobar2000 SDK
70+
run: |
71+
curl -L https://www.foobar2000.org/files/SDK-2023-09-23.7z -o vendor/foobar2000_sdk.7z
72+
7z x vendor/foobar2000_sdk.7z -ovendor/sdk
73+
ls vendor/sdk
74+
75+
- name: Setup GoogleTest Xcode Project
76+
working-directory: ${{ github.workspace }}/vendor/googletest
77+
run: |
78+
mkdir build
79+
cd build && cmake -G Xcode cmake -DCMAKE_BUILD_TYPE=${{ matrix.config }} ..
80+
81+
- name: Build Tests
82+
working-directory: ${{ github.workspace }}
83+
run: |
84+
xcodebuild clean build -workspace foo_input_ncm.xcworkspace -scheme foo_input_ncm_tests -configuration ${{ matrix.config }} -derivedDataPath .
85+
86+
- name: Run Tests
87+
working-directory: ${{ github.workspace }}/Build/Products/${{ matrix.config }}
88+
run: |
89+
./foo_input_ncm_tests --gtest_repeat=10 --gtest_shuffle
90+
5691
matrix-build:
5792
name: Matrix Build
5893
runs-on: ${{ matrix.os }}
59-
needs: [test]
94+
needs: [test-windows, test-macos]
6095
strategy:
6196
fail-fast: false
6297
matrix:
@@ -85,8 +120,9 @@ jobs:
85120

86121
- name: Versioning
87122
run: |
88-
TAG=$(git describe --tags)
89-
VERSION=${TAG:1}
123+
TAG=$(git describe --long --all)
124+
VERSION=${TAG:6}
125+
echo "" >> src/stdafx.h
90126
echo "#define CURRENT_VERSION $VERSION" >> src/stdafx.h
91127
echo "----------------------------"
92128
cat src/stdafx.h
@@ -115,7 +151,7 @@ jobs:
115151
env:
116152
scheme: foo_input_ncm-${{ matrix.config }}
117153
run: |
118-
xcodebuild clean build -workspace ${{ matrix.workspace }} -scheme "$scheme" -configuration ${{ matrix.config }}
154+
xcodebuild clean build -workspace ${{ matrix.workspace }} -scheme "$scheme" -configuration ${{ matrix.config }} -sdk macosx -mmacosx-version-min=13.3
119155
if: matrix.os == 'macos-14'
120156

121157
- name: (MSBuild) Setup MSBuild

.gitmodules

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
[submodule "vendor/rapidjson"]
2-
path = vendor/rapidjson
3-
url = git@github.com:Tencent/rapidjson.git
4-
branch = master
1+
[submodule "vendor/spdlog"]
2+
path = vendor/spdlog
3+
url = git@github.com:gabime/spdlog.git
4+
[submodule "vendor/googletest"]
5+
path = vendor/googletest
6+
url = git@github.com:google/googletest.git
7+
[submodule "vendor/json"]
8+
path = vendor/json
9+
url = git@github.com:nlohmann/json.git

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@
22
![GitHub Release](https://img.shields.io/github/v/release/pnck/foo_input_ncm)
33
![GitHub Release Date](https://img.shields.io/github/release-date/pnck/foo_input_ncm)
44

5-
## Play NCM files directly with our favourite
5+
## Play NCM files directly with our favorite
66

77
- On Windows
8-
![screenshot-win](/doc/screenshot-win.png)
8+
![screenshot-win](/docs/screenshot-win.png)
99

1010
- Or on MacOS
11-
![screenshot-mac](/doc/screenshot-mac.png)
11+
![screenshot-mac](/docs/screenshot-mac.png)
1212

1313

1414
### Features
1515
- [x] Seamlessly load and play `.ncm` format files in _foobar2000_.
1616
- [x] Fully integrated with the powerful built-in _`metadb`_ system, enabling efficient searching and strong management capabilities.
1717
- [x] Unwrap/Convert `.ncm` to normal `.mp3` or `.flac` format with blazing speed.
18-
- [ ] ReplayGain and retagging support.
18+
- [x] Edit `replay gain`/`metadata`/`album art` in place.
19+
20+
Constraints and compromises are noted in [reminders.md](/docs/reminders.md).
21+
1922

2023
### Compatibility
2124

@@ -35,6 +38,6 @@
3538
- [foo_input_msu](https://github.com/qwertymodo/foo_input_msu) by [qwertymodo](https://github.com/qwertymodo)
3639
- [foo_input_nemuc](https://github.com/hozuki/foo_input_nemuc) by [hozuki](https://github.com/hozuki)
3740
- [foo_input_spotify](https://github.com/FauxFaux/foo_input_spotify) by [FauxFaux](https://github.com/FauxFaux)
38-
- The very detailed [develop tutorio](http://yirkha.fud.cz/tmp/496351ef.tutorial-draft.html) for starters
41+
- The very detailed [development tutorial](http://yirkha.fud.cz/tmp/496351ef.tutorial-draft.html) for starters
3942
- Official component samples included in [foobar2000 sdk](https://www.foobar2000.org/SDK)
40-
- And the [ncmdump](https://github.com/anonymous5l/ncmdump) which encourages me doing a lot of research on the file structure
43+
- And the [ncmdump](https://github.com/anonymous5l/ncmdump) which encourages me to do a lot of research on the file structure

docs/reminders.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
## Playback and Meta Information Retrieval
2+
3+
- By currently observed, a `.ncm` file may contain duplicated meta tags, coming from both _the NCM file heder_ and the wrapped _audio content_. This component would take all of them and merge together to show distinct values.
4+
5+
- Other meta informations such as `codec`, `duration`, `bitrate`... are retrieved by the underlying decoder, typically `FLAC` or `MPEG` decoder. **And the decoder is also reading meta tags of the audio at the same time**. Thats the reason why both source of meta have to be dealed with.
6+
7+
- The audio content is **NOT fully decrypted in the memory at once**, but decrypt while reading on demand. This significantly reduces the memory usage, but slightly affects the speed of processing. It may be hard for compilers to optimize the `transform_view`, used in the implementation.
8+
9+
## Retagging
10+
11+
By design, the music meta information such as title/artist/album etc is embedded and re-encrypted into the `ncm` header. This decision was reached considering the following factors:
12+
13+
- The audio content isn't _"clean"_. Meaning if we take the embedded tag of the audio as overwrites, some music might be "polluted" by the tags at the original state.
14+
15+
- Audio content is not verified when played by _NCM player_. So there is no way to tell whether it's modified, nor to completely restore the file state.
16+
17+
- Replay gain is written to the audio content because it's recoverable and **simply tested** that the file hash will turn back after clearing the custom gain information.
18+
19+
- The meta information stored in the `ncm` header is essentially a standard stringified `JSON` object, allowing easy extension while preserving all original fields. Although modifying it would change its identification, causing the _NCM player_ to refuse to playback, it's totally recoverable by deleting the overwrites.
20+
- An `overwrite` key is **appended** to the `JSON` if user customized the meta tags. All changes would happen in the `overwrite` object.
21+
- **Clear all tags result in removing the `overwrite` key, which is actually equivalent to restore the file idntification.**
22+
23+
Currently, the downloaded `.ncm` files can be recognized and played by the _NCM player_ **IF**:
24+
25+
- It's put in a monitored directory.
26+
- The album art or the replay gain is edited, while other meta information isn't.
27+
- Meta tags are once edited, then cleared by `Remove tags` utility.
28+
29+
## Extractor
30+
31+
- The extraction menu is unavailable in old fb2k versions (v1.6), due to **API incompatity**.
32+
33+
- The extraction process is multi-threaded. Spawn order: `UI => extractor(x1) => worker(xN)`.
34+
35+
- The output file would be overwritten if existed, and no warning would be given in this case. See the comments at
36+
`ncm_file::save_raw_audio()`.
37+
38+
## The Behaviors of the _NCM Player_
39+
40+
- The meta info JSON is built and writen **AFTER** completing the download of the music file.
41+
42+
- The cover image is always retrived from the internet. The player will just ignore the embedded image.
43+
44+
- There may be some extra verifications to the identification (`163 key(Don't modify):xxxx`). The player refuses to play even when off-line.
45+
46+
- The player actually recognize `.ncm` files by the identification, not by the decrypted meta info fields.

foo_input_ncm.vcxproj

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
<ConformanceMode>true</ConformanceMode>
9191
<LanguageStandard>stdcpp20</LanguageStandard>
9292
<LanguageStandard_C>stdc11</LanguageStandard_C>
93-
<AdditionalIncludeDirectories>src; vendor; vendor/sdk</AdditionalIncludeDirectories>
93+
<AdditionalIncludeDirectories>src;vendor/spdlog/include;vendor/json/include;vendor/sdk;vendor/sdk/foobar2000</AdditionalIncludeDirectories>
9494
<AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>
9595
<PrecompiledHeader>Use</PrecompiledHeader>
9696
</ClCompile>
@@ -116,7 +116,7 @@
116116
<ConformanceMode>true</ConformanceMode>
117117
<LanguageStandard>stdcpp20</LanguageStandard>
118118
<LanguageStandard_C>stdc11</LanguageStandard_C>
119-
<AdditionalIncludeDirectories>src; vendor; vendor/sdk</AdditionalIncludeDirectories>
119+
<AdditionalIncludeDirectories>src;vendor/spdlog/include;vendor/json/include;vendor/sdk;vendor/sdk/foobar2000</AdditionalIncludeDirectories>
120120
<AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>
121121
<PrecompiledHeader>Use</PrecompiledHeader>
122122
</ClCompile>
@@ -140,7 +140,7 @@
140140
<ConformanceMode>true</ConformanceMode>
141141
<LanguageStandard>stdcpp20</LanguageStandard>
142142
<LanguageStandard_C>stdc11</LanguageStandard_C>
143-
<AdditionalIncludeDirectories>src; vendor; vendor/sdk</AdditionalIncludeDirectories>
143+
<AdditionalIncludeDirectories>src;vendor/spdlog/include;vendor/json/include;vendor/sdk;vendor/sdk/foobar2000</AdditionalIncludeDirectories>
144144
<AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>
145145
<PrecompiledHeader>Use</PrecompiledHeader>
146146
</ClCompile>
@@ -168,7 +168,7 @@ copy $(TargetPath) $(ProjectDir)dist\$(Platform)\</Command>
168168
<ConformanceMode>true</ConformanceMode>
169169
<LanguageStandard>stdcpp20</LanguageStandard>
170170
<LanguageStandard_C>stdc11</LanguageStandard_C>
171-
<AdditionalIncludeDirectories>src; vendor; vendor/sdk</AdditionalIncludeDirectories>
171+
<AdditionalIncludeDirectories>src;vendor/spdlog/include;vendor/json/include;vendor/sdk;vendor/sdk/foobar2000</AdditionalIncludeDirectories>
172172
<AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>
173173
<PrecompiledHeader>Use</PrecompiledHeader>
174174
</ClCompile>
@@ -187,7 +187,7 @@ copy $(TargetPath) $(ProjectDir)dist\$(Platform)\</Command>
187187
</PostBuildEvent>
188188
</ItemDefinitionGroup>
189189
<ItemGroup>
190-
<ClInclude Include="src\album_art_extractor.hpp" />
190+
<ClInclude Include="src\album_art.hpp" />
191191
<ClInclude Include="src\cipher\abnormal_RC4.hpp" />
192192
<ClInclude Include="src\cipher\aes.hpp" />
193193
<ClInclude Include="src\cipher\aes_common.hpp" />
@@ -197,17 +197,20 @@ copy $(TargetPath) $(ProjectDir)dist\$(Platform)\</Command>
197197
<ClInclude Include="src\cipher\utils.hpp" />
198198
<ClInclude Include="src\common\consts.hpp" />
199199
<ClInclude Include="src\common\helpers.hpp" />
200+
<ClInclude Include="src\common\log.hpp" />
200201
<ClInclude Include="src\common\platform.hpp" />
202+
<ClInclude Include="src\meta_process.hpp" />
201203
<ClInclude Include="src\ui\context_menu.hpp" />
202204
<ClInclude Include="src\input_ncm.hpp" />
203205
<ClInclude Include="src\ncm_file.hpp" />
204206
<ClInclude Include="src\stdafx.h" />
205207
</ItemGroup>
206208
<ItemGroup>
207-
<ClCompile Include="src\album_art_extractor.cpp" />
209+
<ClCompile Include="src\album_art.cpp" />
208210
<ClCompile Include="src\cipher\abnormal_RC4.cpp" />
209211
<ClCompile Include="src\cipher\aes_common.cpp" />
210212
<ClCompile Include="src\cipher\aes_win32.cpp" />
213+
<ClCompile Include="src\meta_process.cpp" />
211214
<ClCompile Include="src\ui\context_menu.cpp" />
212215
<ClCompile Include="src\input_ncm.cpp" />
213216
<ClCompile Include="src\main.cpp" />

foo_input_ncm.vcxproj.filters

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
</Filter>
2525
</ItemGroup>
2626
<ItemGroup>
27-
<ClInclude Include="src\album_art_extractor.hpp">
27+
<ClInclude Include="src\album_art.hpp">
2828
<Filter>Header Files</Filter>
2929
</ClInclude>
3030
<ClInclude Include="src\input_ncm.hpp">
@@ -69,9 +69,15 @@
6969
<ClInclude Include="src\ui\context_menu.hpp">
7070
<Filter>Header Files\ui</Filter>
7171
</ClInclude>
72+
<ClInclude Include="src\common\log.hpp">
73+
<Filter>Header Files\common</Filter>
74+
</ClInclude>
75+
<ClInclude Include="src\meta_process.hpp">
76+
<Filter>Header Files</Filter>
77+
</ClInclude>
7278
</ItemGroup>
7379
<ItemGroup>
74-
<ClCompile Include="src\album_art_extractor.cpp">
80+
<ClCompile Include="src\album_art.cpp">
7581
<Filter>Source Files</Filter>
7682
</ClCompile>
7783
<ClCompile Include="src\input_ncm.cpp">
@@ -98,5 +104,8 @@
98104
<ClCompile Include="src\ui\context_menu.cpp">
99105
<Filter>Source Files\ui</Filter>
100106
</ClCompile>
107+
<ClCompile Include="src\meta_process.cpp">
108+
<Filter>Source Files</Filter>
109+
</ClCompile>
101110
</ItemGroup>
102111
</Project>

0 commit comments

Comments
 (0)