diff --git a/.gitignore b/.gitignore
index fee8d3b..924619a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,5 @@ settings.txt
ignoreme/
product_info
libMali*
+ss/*
+*.tbl
diff --git a/.gitmodules b/.gitmodules
index df95fa7..7f52d49 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -38,3 +38,6 @@
[submodule "ipod_theme"]
path = digital_clock/ipod_theme
url = https://github.com/nfzerox/ipod_theme
+[submodule "libs/llusbdac"]
+ path = libs/llusbdac
+ url = https://github.com/zhangboyang/llusbdac.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 12d3046..a56580f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -111,12 +111,14 @@ add_executable(${PROJECT_NAME}
libs/zlib/contrib/minizip/ioapi.c
libs/ImageMagick/Magick++/lib/Color.cpp
${PROTOBUF_SOURCE}
+ src/dac/cxd3778gf_table.cpp
src/util/util.cpp
src/wstring.cpp
src/w1/w1.cpp
src/cassette/tape.cpp
src/digital_clock/digital_clock.cpp
${CONNECTOR_SOURCE}
+ src/imgui_curve.cpp
src/imgui_widgets.cpp
src/connector/song.cpp
src/skinVariant.cpp
@@ -125,7 +127,14 @@ add_executable(${PROJECT_NAME}
src/cassette/cassette.cpp
src/config.cpp
# libs/imgui/imgui_demo.cpp
- src/shader.cpp)
+ src/shader.cpp
+ src/dac/dac.cpp)
+
+#add_executable(dac
+# src/dac/cxd3778gf_table.cpp
+# src/dac/dac.cpp
+# src/dac/main.cpp
+# )
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/${arch})
diff --git a/Dockerfile b/Dockerfile
index 09af555..9b79d0b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,3 +10,8 @@ RUN apt-get build-dep qtbase5-dev -y && \
tar -xf cmake-${CMAKE_RELEASE_VERSION}-linux-x86_64.tar.gz && \
rm cmake-${CMAKE_RELEASE_VERSION}-linux-x86_64.tar.gz && \
ln -s /opt/cmake-${CMAKE_RELEASE_VERSION}-linux-x86_64/bin/cmake /usr/bin/cmake
+
+RUN ln -s /x-tools/armv5-unknown-linux-gnueabihf/bin/armv5-unknown-linux-gnueabihf-ld /usr/bin/arm-linux-gnueabihf-ld && \
+ ln -s /x-tools/armv5-unknown-linux-gnueabihf/bin/armv5-unknown-linux-gnueabihf-ld.bfd /usr/bin/arm-linux-gnueabihf-ld.bfd && \
+ ln -s /x-tools/armv5-unknown-linux-gnueabihf/bin/armv5-unknown-linux-gnueabihf-gcc /usr/bin/arm-linux-gnueabihf-gcc && \
+ ln -s /x-tools/armv5-unknown-linux-gnueabihf/bin/armv5-unknown-linux-gnueabihf-strip /usr/bin/arm-linux-gnueabihf-strip
\ No newline at end of file
diff --git a/MAKING_OF.md b/MAKING_OF.md
index fb9a301..f2b05de 100644
--- a/MAKING_OF.md
+++ b/MAKING_OF.md
@@ -37,7 +37,7 @@
* [Client misc](#client-misc)
* [Settings](#settings)
* [Config file](#config-file)
- * [NW-WM1Z / Walkman One compatibility](#nw-wm1z--walkman-one-compatibility)
+ * [Walkman One compatibility](#walkman-one-compatibility)
* [Build](#build)
* [Cross-compilation](#cross-compilation)
* [Qt](#qt)
@@ -627,9 +627,9 @@ about that, but no definitive solution. I've spent embarrassing amount of time f
-Just as usual, task looks easy. After some googling you may find out that there is magic `QT_QPA_EGLFS_FB` env variable,
-which points to framebuffer to draw to. Can we tell `HgrmMediaPlayerApp` to temporarily change framebuffer? No, we
-can't, this variable is set only on application start.
+Just as usual, task looks easy. After some googling you may find out that there is magical `QT_QPA_EGLFS_FB` env
+variable, which points to framebuffer to draw to. Can we tell `HgrmMediaPlayerApp` to temporarily change framebuffer?
+No, we can't, this variable is set only on application start.
#### Kernel module?
@@ -871,6 +871,8 @@ all dropdowns show their content downwards. That breaks tape/reel selection at t
Dropdown dropping in a wrong direction
+That issue was fixed later, but the memory remains.
+
There is also an issue with elements' size. Make them bigger = introduce screen scrolling. This is undesirable with
broken culling. Personally, I am fine with current layout and scaling, but my fingers are not that big.
@@ -895,16 +897,12 @@ with software mod not starting aside from making an issue on GitHub? So here com
protection. Everything from settings is reflected in config file, crash logs are collected into user directory for
easier sharing. Hopefully that'll make post-release maintenance much easier.
-### NW-WM1Z / Walkman One compatibility
+### Walkman One compatibility
Walkman One firmware is quite popular. Under the hood it changes some system properties so software is tricked into
changing interface and enabling features to match NW-WM1Z. Since software is mostly compatible with Wampy, an effort was
made to add Walkman One build (on that later).
-There is no firmware made specifically for NW-WM1Z, because I don't have hardware to test on. Even though software
-works, upgrade process may fail in unexpected way and brick the device. If you really want Wampy on your device, please
-provide backup file (see [BACKUP.md](./BACKUP.md)).
-
### Build
diff --git a/MAKING_OF_SOUND_SETTINGS.md b/MAKING_OF_SOUND_SETTINGS.md
new file mode 100644
index 0000000..7375455
--- /dev/null
+++ b/MAKING_OF_SOUND_SETTINGS.md
@@ -0,0 +1,680 @@
+# Making of sound settings
+
+Perhaps it started after reading countless posts discussing how region affects sound signature. Region changes were made
+using [SonyNWDestTool][1] in the past and Walkman One in the present. There are almost no fancy volume graphs showing
+differences, only vague descriptions like "warmer" and "richer" ([exception][9] - take a good look at last picture
+comparing 4 regions). Subjective perception is… subjective; changing region takes time during which your ears can
+readjust; device enters energy saving mode after reboot and lowers sound quality; something else? These posts don't look
+trustworthy and probably were made by snake oil consumers - that's what I thought.
+
+So let's check out what really happens on NW-A50, stock firmware.
+
+## Boot
+
+### Little kernel
+
+First, device boots little kernel ([details][2]). There is no source available, no logs as well. You can get those by
+soldering some wires to board ([like this][3]), but I am not doing that. It makes no sense to modify sound settings in
+little kernel, it's purpose to prepare board and boot real kernel. Little kernel is NOT provided in stock firmware, but
+you can dump it:
+
+```shell
+$ cat /proc/dumchar_info | grep uboot
+uboot 0x0000000000060000 0x0000000002120000 2 /dev/block/mmcblk0p7
+
+$ dd if=/dev/block/mmcblk0p7 of=/contents/p7
+768+0 records in
+768+0 records out
+393216 bytes transferred in 0.018 secs (21845333 bytes/sec)
+
+$ xxd p7 | head -n 1
+00000000: 8816 8858 541c 0400 4c4b 0000 0000 0000 ...XT...LK......
+ ^ there it is, little kernel
+```
+
+Little kernel provides various variables for real kernel. You can check them in cmdline:
+
+```shell
+$ cat /proc/cmdline
+console=/dev/null root=/dev/ram vmalloc=496M slub_max_order=0 slub_debug=O lcm=1-hx8379c_vdo_cmd fps=6077 vram=6291456 \
+androidboot.selinux=disabled bootprof.pl_t=1087 bootprof.lk_t=1217 printk.disable_uart=0 boot_reason=4 \
+icx_pm_helper.icx_sysinfo=0x0000001b icx_pm_helper.icx_modelid=0x25000004 icx_pm_helper.icx_ship=0x00000002 \
+mmc_core.emmc_boot_serial=0xb6a4e23e icx_pm_helper.icx_boot_option=0 leds_drv.brightness=255 \
+androidboot.serialno=10458B75388765 androidboot.bootreason=wdt_by_pass_pwk icx_pm_helper.icx_boot_reason=0x0 \
+androidboot.hardware=icx androidboot.console=ttyMT1 androidboot.printk=0 icx_pm_helper.icx_bid2=0 icx_pm_helper.icx_bid3=0
+```
+
+Nothing of interest, let's move on.
+
+### Real kernel
+
+Real kernel is a place of interest. There are [sources available][4], so we can take a look. The part we are looking for
+is audio driver. We are using `cxd3778gf` card:
+
+```shell
+$ aplay -l
+**** List of PLAYBACK Hardware Devices ****
+card 0: sonysoccard [sony-soc-card], device 0: cxd3778gf-hires-out DAI_CXD3778GF_DAC-0 []
+... ^ there
+```
+
+There it is: `./linux/sound/soc/codecs/cxd3778gf`. In `cxd3778gf_table.h/cpp` you can find volume table structures
+(which were used later by Wampy) and other information about different audio devices (such as low power output and DSD
+output).
+
+There is nothing in driver that indicates sound change during kernel boot.
+
+What happens next? Ramdisc and init.
+
+### Init
+
+NW-A50, just like all other DAPs in these series uses stripped down Android (or could it be "not bloated?"). Basically a
+regular Linux with some systems written by Google, like [init][5].
+
+Little kernel boots real kernel, which unpacks ramdisk (initrd.gz) into memory with small set of binaries and init
+scripts. At this stage drivers are loaded and configured. Let's take a look into main script, `init.rc`:
+
+```text
+74 on fs
+75 mount ext4 /emmc@android /system noatime ro
+76
+77 start bootanimation
+78 exec /system/bin/load_sony_driver_early <--- nvp nodes
+
+178 on boot
+179 ...
+212 start load_sony_driver
+
+390 service load_sony_driver /system/bin/load_sony_driver <--- there you are
+391 oneshot
+```
+
+bin/load_sony_driver:
+
+```shell
+...
+mid=`nvpflag -x mid` # 0x25000004
+midupper=`echo $mid | cut -c1-8` # 0x250000
+nvpflag mid $midupper$nvpcap # 0x25000004, 04 comes from storage size check above, irrelevant
+
+#volume table
+PRODDEV=`getprop ro.product.device` # BBDMP5_linux
+shp=`nvpflag -x shp` # 0x00000002 0x00000000 (region)
+shpfirst=`echo $shp | cut -c1-10` # 0x00000002
+/system/bin/dacdat auto $PRODDEV $midupper $shpfirst
+```
+
+What does it do?
+
+```shell
+$ strace -e open /bin/dacdat auto BBDMP5_linux 0x250000 0x00000002
+...
+open("/system/usr/share/audio_dac/ov_1291.tbl", O_RDONLY) = 3
+open("/proc/icx_audio_cxd3778gf_data/ovt", O_RDWR) = 4
+open("/system/usr/share/audio_dac/ov_dsd_1291.tbl", O_RDONLY) = 3
+open("/proc/icx_audio_cxd3778gf_data/ovt_dsd", O_RDWR) = 4
+open("/system/usr/share/audio_dac/tc_1291.tbl", O_RDONLY) = 3
+open("/proc/icx_audio_cxd3778gf_data/tct", O_RDWR) = 4
+```
+
+Volume tables for NW-A50, region 2, applied.
+
+After that script applies noise cancel tables, limiter tables, gain, ambgain and idata(?). All of those are related to
+MDR-NW750N, IER-NW500N and `something 31`. We don't use those, so let's ignore them.
+
+Volume table application changes sound significantly, making it at least louder, you can literally hear it while
+switching from `_cew` table to regular one. Could it be the secret empowering people to make those posts about "juice"
+and "dryness" in their ears?
+
+It is not, because there is not enough files for all regions:
+
+```shell
+$ find /system/usr/share/audio_dac/ -name "*.tbl" | grep -vE "(ncgain|amb)"
+/system/usr/share/audio_dac/ov_1291.tbl
+/system/usr/share/audio_dac/ov_1291_cew.tbl
+/system/usr/share/audio_dac/ov_dsd_1291.tbl
+/system/usr/share/audio_dac/ov_dsd_1291_cew.tbl
+/system/usr/share/audio_dac/tc_1291.tbl
+```
+
+That's it, `ov_1291` table with it's limited variation (`_cew`) plus tone control.
+
+Is there anything else that reads region flag during init? Nope:
+
+```shell
+/initrd_unpacked$ grep -r "nvpflag shp"
+install_update_script/init_nvp.sh: nvpflag shp $_SHP_123_ $_SHP_456_
+
+/bin$ grep -r "nvpflag -x shp"
+load_sony_driver:shp=`nvpflag -x shp`
+load_sony_driver:shp=`nvpflag -x shp`
+```
+
+There is nothing else that changes sound at this stage, there is only one region-dependant point of interest and that's
+`dacdat` binary. Decompilation showed that there are 5 table variations total and region does not matter. Check out
+handy table connecting regions, products and model ids at [./SOUND_SETTINGS.md](./SOUND_SETTINGS.md).
+
+At this point I started adding volume table functionality to Wampy because I had all info to do so, but it there was no
+answer to claims by those snake oil consumers. There is also another motive to investigate further: people like to
+change and compare regions, so what if we can change regions instantly and hear changes right now instead of rebooting?
+That would be the best way to prove that region matters (or not).
+
+Where do we go next at this point? Software is loaded, but what happens next is hard to analyze. There is an easy way
+though: Walkman One external tunings.
+
+### Walkman One external tunings
+
+According to Mr.Walkman and Walkman One users, external tunings **do** change signature. Perhaps there is the answer?
+
+Mr.Walkman uses vague explanations on Head-Fi forums and uses same language as others ("warm", "deep"). He also avoids
+discussing technical details (for what reason? Afraid to get into nerd discussions? Poor code quality? NPAudio collab
+with percentage per every DAP with Walkman One installed?). No matter the reason, he openly suggested someone on forums
+to take a look yourself into firmware and that's what we are going to do. Let's compare stock NW1A firmware and Walkman
+One NW-A50. That way we could find Mr.Walkman's changes and probably get an explanation.
+
+Comparing root filesystem, ignoring translations and audioanalyzer params (both don't matter):
+
+
+Click
+
+```shell
+$ diff -rq StockRevert/NW_WM_FW/6 NW-A50/walkmanOne/fw/7 2>/dev/null | grep -Ev "(translations|audioanalyzer_params|no such file)"
+Files StockRevert/NW_WM_FW/6/bin/load_sony_driver and NW-A50/walkmanOne/fw/7/bin/load_sony_driver differ
+Only in NW-A50/walkmanOne/fw/7/bin: p
+Files StockRevert/NW_WM_FW/6/etc/libnfc-nci.conf and NW-A50/walkmanOne/fw/7/etc/libnfc-nci.conf differ
+Only in NW-A50/walkmanOne/fw/7/etc: .mod
+Files StockRevert/NW_WM_FW/6/lib/libnfc-nci.so and NW-A50/walkmanOne/fw/7/lib/libnfc-nci.so differ
+Only in NW-A50/walkmanOne/fw/7/lib: libnfcsequencer.so
+Files StockRevert/NW_WM_FW/6/media/bootanimation.zip and NW-A50/walkmanOne/fw/7/media/bootanimation.zip differ
+Files StockRevert/NW_WM_FW/6/vendor/sony/bin/HgrmMediaPlayerApp and NW-A50/walkmanOne/fw/7/vendor/sony/bin/HgrmMediaPlayerApp differ
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NC31_176400.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NC31_192000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NC31_44100.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NC31_48000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NC31_88200.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NC31_96000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW500N_176400.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW500N_192000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW500N_44100.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW500N_48000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW500N_88200.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW500N_96000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW750N_176400.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW750N_192000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW750N_44100.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW750N_48000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW750N_88200.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: ClearPhase_HP_NW750N_96000.lps
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: DseeAi.bin
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: DseeAi.dcfg
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: DseeAi_HP_ICX1291.bin
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: DseeAi_HP_ICX1291.dcfg
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: DseeAi_HP_ICX1300.bin
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: DseeAi_HP_ICX1300.dcfg
+Files StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params/DseeHxCustom_HP_ICX1278.dcfg and NW-A50/walkmanOne/fw/7/vendor/sony/etc/effector_params/DseeHxCustom_HP_ICX1278.dcfg differ
+Only in StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params: DseeHxCustom_HP_ICX1280.dcfg
+Files StockRevert/NW_WM_FW/6/vendor/sony/etc/effector_params/DseeHxCustom_Mode_0.bin and NW-A50/walkmanOne/fw/7/vendor/sony/etc/effector_params/DseeHxCustom_Mode_0.bin differ
+```
+
+
+
+ClearPhase can be ignored (no supported headphones), Dsee is ignored too (signature changes even without it). Libnfc is
+NFC, doesn't matter. `load_sony_driver` script is stripped of storage size detection and some comments, everything is
+the same about `dacdat` and volume tables. `NW-A50/walkmanOne/fw/7/bin/p` is a script that compresses binaries for UPG?
+Definitely not audio. `bootanimation.zip` is boot animation, next file. `vendor/sony/bin/HgrmMediaPlayerApp` is the
+audio player application, what's changed there? Could it be IT?
+
+Nope, this is not it. Here are the changes:
+
+```shell
+hgrmmediaplayerapp diff:
+FUN_0036ed60 - ghidra thinks that function is one byte longer
+005ebc17 - power off logo
+005f7bf2 - qml height, interface adjustments
+005f90f5 - qml height
+005fc002 - qml string
+005fc44c - qml string
+005ff559 - qml height
+00604888 - qml string
+00607568 - qml string
+0060e846 - qml string
+006106dc - qml string
+0061f08e - qml string
+```
+
+You can do it using Ghidra binary diff mode or `vbindiff`, whichever you like. The gist is that this file has only
+cosmetic adjustments and nothing about sound signatures.
+
+Okay, system files on root partition are not changed. What about `etc/.mod` directory?
+
+```shell
+etc/.mod$ find . -type f | grep -Ev "(txt|anls|lang|dll|exe|UPG|xml)"
+./adler/normal/libaudiohal-adleralsa.so
+./adler/normal_nt/libaudiohal-adleralsa.so
+./adler/pv1/libaudiohal-adleralsa.so
+./adler/pv2/libaudiohal-adleralsa.so
+./gain/gain_l/ov_127x.tbl
+./gain/gain_l/ov_127x_cew.tbl
+./gain/gain_l/ov_dsd_127x.tbl
+./gain/gain_l/ov_dsd_127x_cew.tbl
+./gain/gain_n/ov_127x.tbl
+./gain/gain_n/ov_127x_cew.tbl
+./gain/gain_n/ov_dsd_127x.tbl
+./gain/gain_n/ov_dsd_127x_cew.tbl
+./conf_a
+./conf_b
+./conf_c
+```
+
+There we are excluding audio analyzer tweaks (which are not applied by default, stock revert files and translation
+files). About translation files: these are needed to display "Bright tuning applied" in unit info. That string indicates
+that installation script copied translation files, not that tuning was actually applied - there is a difference.
+
+Adler files are plus modes, v1 and v2. They do change sound What's the difference between these and stock?
+
+Walkman One firmware page:
+
+> "Plus v1" - a different flavor of the normal chosen sound signature;
+>
+> "Plus v2" - usually better than the Plus v1 mode, and it also consumes less battery;
+
+Even though this is optional functionality, let's take a detour.
+
+---
+
+
+
+
+That's it. What do those bytes mean?
+
+Plus v1 changes output hw0:4 (cxd3778gf-icx-lowpower) to hw0:0 (cxd3778gf-hires-out), forcing you through hires
+output. It also increases value in `/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq`, which increases cpu
+frequency on powersave governor. You can read more about it [here][6]. Perhaps it helps to prevent slowing down on low
+battery?
+
+Plus v2 changes output hw0:0 (cxd3778gf-hires-out) to hw0:4 (cxd3778gf-icx-lowpower) forcing you to play hires
+audio on lowpower interface, thus probably reducing battery consumption. Cpu frequency increase is also applied.
+
+Difference between outputs? Consult kernel driver.
+
+mediatek/mt8590/icx-machine-links.c, line 319 (CONFIG_SND_SOC_ICX_AUDIO_MOBILE_NEXT=y in /proc/config.gz):
+
+
+hires
+
+```c
+ {
+ .name = "CXD3778GF_ICX",
+ .stream_name = "cxd3778gf-hires-out",
+ .cpu_dai_name = "mt8590-i2s1",
+ .platform_name = "mt8590-audio",
+ .codec_dai_name = CXD3778GF_DAC_DAI_NAME,
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_CBM_CFM
+ | SND_SOC_DAIFMT_NB_NF,
+ .ops = &stream_icx_slave_ops,
+ },
+
+/*
+ * For cxd3778gf special seqpuecne
+ * slave mode by alsa rate
+ */
+static int pcm_slave_icx_rate_hw_params(
+ struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct mt_stream *s = substream->runtime->private_data;
+ /* codec slave, mt8590 master */
+ unsigned int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CONT;
+ unsigned int mclk_rate;
+ unsigned int rate = params_rate(params); /* data rate */
+ unsigned int div_mclk_to_bck = rate > 192000 ? 2 : 4;
+ unsigned int div_bck_to_lrck = 64;
+ pr_debug("%s() rate = %d\n", __func__, rate);
+
+ mclk_rate = rate * div_bck_to_lrck * div_mclk_to_bck;
+ /* codec mclk */
+ snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+ /* mt8590 mclk */
+ snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+ /* mt8590 bck */
+ snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_MCLK_TO_BCK, div_mclk_to_bck);
+ /* mt8590 lrck */
+ snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_BCK_TO_LRCK, div_bck_to_lrck);
+ /* mt8590 master */
+
+ fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ /* codec master */
+ snd_soc_dai_set_fmt(codec_dai, fmt);
+ fmt |= SLAVE_USE_ASRC_NO;
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ s->use_i2s_slave_clock = 1;
+
+ return 0;
+}
+```
+
+
+
+
+lowpower
+
+```c
+ {
+ .name = "CXD3778GF_ICX_lowpower",
+ .stream_name = "cxd3778gf-icx-lowpower",
+ .cpu_dai_name = "mt8590-i2s1",
+ .platform_name = "mt8590-lp-audio",
+ .codec_dai_name = CXD3778GF_ICX_DAI_NAME,
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_CBM_CFM
+ | SND_SOC_DAIFMT_NB_NF,
+ .ops = &stream_icx_lp_slave_ops,
+ },
+
+/*
+ * For cxd3778gf special seqpuecne
+ * lp slave mode by alsa rate
+ */
+static int lp_slave_icx_rate_hw_params(
+ struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct mt_lp_private *priv = snd_soc_platform_get_drvdata(rtd->platform);
+ /* codec slave, mt8590 master */
+ unsigned int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CONT;
+ unsigned int mclk_rate;
+ unsigned int rate = params_rate(params); /* data rate */
+ unsigned int div_mclk_to_bck = rate > 192000 ? 2 : 4;
+ unsigned int div_bck_to_lrck = 64;
+ pr_debug("%s() rate = %d\n", __func__, rate);
+
+ mclk_rate = rate * div_bck_to_lrck * div_mclk_to_bck;
+ /* codec mclk */
+ snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+ /* mt8590 mclk */
+ snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+ /* mt8590 bck */
+ snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_MCLK_TO_BCK, div_mclk_to_bck);
+ /* mt8590 lrck */
+ snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_BCK_TO_LRCK, div_bck_to_lrck);
+ /* mt8590 master */
+
+ fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ /* codec master */
+ snd_soc_dai_set_fmt(codec_dai, fmt);
+ fmt |= SLAVE_USE_ASRC_NO;
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ priv->use_i2s_slave_clock = 1;
+#ifdef CONFIG_MTK_CM4_USE_HIGH_FREQ_ASRC
+ priv->univpll_keep = 0;
+#endif
+ return 0;
+}
+```
+
+
+
+
+diff
+
+```diff
+2,3c2,3
+< .name = "CXD3778GF_ICX",
+< .stream_name = "cxd3778gf-hires-out",
+---
+> .name = "CXD3778GF_ICX_lowpower",
+> .stream_name = "cxd3778gf-icx-lowpower",
+5,6c5,6
+< .platform_name = "mt8590-audio",
+< .codec_dai_name = CXD3778GF_DAC_DAI_NAME,
+---
+> .platform_name = "mt8590-lp-audio",
+> .codec_dai_name = CXD3778GF_ICX_DAI_NAME,
+10c10
+< .ops = &stream_icx_slave_ops,
+---
+> .ops = &stream_icx_lp_slave_ops,
+15c15
+< * slave mode by alsa rate
+---
+> * lp slave mode by alsa rate
+17c17
+< static int pcm_slave_icx_rate_hw_params(
+---
+> static int lp_slave_icx_rate_hw_params(
+23c23
+< struct mt_stream *s = substream->runtime->private_data;
+---
+> struct mt_lp_private *priv = snd_soc_platform_get_drvdata(rtd->platform);
+48,49c48,51
+< s->use_i2s_slave_clock = 1;
+<
+---
+> priv->use_i2s_slave_clock = 1;
+> #ifdef CONFIG_MTK_CM4_USE_HIGH_FREQ_ASRC
+> priv->univpll_keep = 0;
+> #endif
+```
+
+
+
+Hires is cooler I guess. Needs more technical explanation, but ultimately it doesn't matter because plus mode is region
+independent; not what we are looking for. These values (cpu, output device) are now shown on
+Wampy's `Sound Settings -> Status` page.
+
+---
+
+Back to other files. Gain is master volume tables taken from other firmwares. Consult [uniq.txt][7].
+
+Only `conf_*` files left, what are those? According to the `sbin/boot_complete.sh` script (the heart of Walkman One),
+those go into p22 partition, which is called nvp (non-volatile partition):
+
+```shell
+$ grep p22 /proc/dumchar_info
+nvp 0x0000000000f00000 0x0000000039e80000 2 /dev/block/mmcblk0p22
+```
+
+These files are 15 megabytes each and used for external tuning change. They are quite different inside, different values
+at different locations. Perhaps these files contain some magical code, region definitions or...?
+
+Let's take a look at tuning installation steps:
+
+- write conf_a/b/c into nvp partition
+- write backed up nvram into nvram partition
+- ask user to install tuning from .exe (why not everything on device?)
+- installer writes some info into nvram partition
+- installer updates uboot partition with new little kernel
+
+It's hard to diff those changes with regular tools, so I tried to do it separately starting with nvp.
+First idea was to talk to nvp driver to clarify things. There is `./lib/modules/icx_nvp_emmc.ko` kernel module that
+looks just like what we need. Perhaps it has some answers? Usually you can communicate with drivers using ioctl and this
+driver is no exception. `icx_nvp_ioctl` has ioctl ids we need (0x40046e00).
+
+
+Code
+
+```c
+#include
+#include
+#include
+#include
+#include
+
+int main(int argc, char *argv[]) {
+ uint32_t ioctl_nr;
+ uint32_t ioctl_p1;
+
+ auto fd = open(argv[1], O_RDWR | O_SYNC);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s\n", argv[1]);
+ return 1;
+ }
+
+ ioctl_nr = strtol(argv[2], nullptr, 0);
+ ioctl_p1 = strtol(argv[3], nullptr, 0);
+ printf("%u\n", ioctl_nr);
+ printf("%s\n",argv[2]);
+
+ ioctl(fd, ioctl_nr, ioctl_p1);
+}
+```
+
+
+
+Ioctl must be sent to a block device file. Where is it?
+
+```shell
+$ cat /proc/icx_nvp
+232,93
+
+$ busybox stat /dev/icx_nvp/000
+ File: /dev/icx_nvp/000
+ Size: 0 Blocks: 0 IO Block: 4096 character special file
+Device: dh/13d Inode: 2486 Links: 1 Device type: e8,0 <----- major number 0xe8 == 232
+Access: (0660/crw-rw----) Uid: ( 100/ system) Gid: ( 100/ UNKNOWN)
+Access: 2025-01-16 07:32:54.000000000
+Modify: 2025-01-16 07:32:54.000000000
+Change: 2025-01-16 07:32:54.000000000
+```
+
+You can consult [The Linux Kernel Module Programming Guide, Chapter 4. Character Device Files][8] for more info.
+
+Using:
+
+```shell
+$ /tmp/ioctl /dev/icx_nvp/000 0x40046e00 1
+$ dmesg -c
+<4>[22021.017475] (0)[1489:ioctl]ZONE INFO
+<4>[22021.017502] (0)[1489:ioctl] Area 0: type=small u-boot parameter
+<4>[22021.017515] (0)[1489:ioctl] Zone 0: node=1 start=0x0 count=0x1 size=4 system information
+<4>[22021.017528] (0)[1489:ioctl] 00: 00000064
+<4>[22021.017539] (0)[1489:ioctl] Zone 1: node=2 start=0x1 count=0x1 size=32 u-boot password
+<4>[22021.017549] (0)[1489:ioctl] 00: 00000060
+<4>[22021.017560] (0)[1489:ioctl] Zone 2: node=3 start=0x2 count=0x1 size=4 firmware update flag
+```
+
+Cool, right? Now we know what `/dev/icx_nvp/` nodes are for.
+
+Here is the non-cool way of doing it using `/bin/nvp` which was waiting on device all that time:
+
+```shell
+$ /bin/nvp stat node
+ok
+$ dmesg -c
+<4>[22549.164655] (0)[1500:nvp]NODE INFO
+<4>[22549.164679] (0)[1500:nvp] Node: Area Zone Size Name
+<4>[22549.164691] (0)[1500:nvp] 1: 0 0 4 system information
+<4>[22549.164702] (0)[1500:nvp] 2: 0 1 32 u-boot password
+<4>[22549.164713] (0)[1500:nvp] 3: 0 2 4 firmware update flag
+<4>[22549.164723] (0)[1500:nvp] 4: 0 3 4 printk flag
+```
+
+Really...? Anyway, all that cool info means nothing without better data identification. Node 36 is named `EMPR 0` and
+has 1024 bytes in it, what am I supposed to do with these bytes?
+
+Then I attempted to do it programmatically using hagoromo framework. I've successfully dumped application/device
+configuration keys and values from nvp only to discover that they do not change with 2 exceptions.
+These exceptions are _configuration_ nodes (not nvp ones), containing model name (group 0, key 4) and serial number (
+group 0, key 1).
+
+What about nvram? From what I've gathered, it has some "custom files" inside used to configure device in some cases.
+These files are located at `/data/nvram`:
+
+```shell
+$ find /data/nvram/ -type f
+/data/nvram/AllFile
+/data/nvram/AllMap
+/data/nvram/APCFG/APRDEB/WIFI
+/data/nvram/APCFG/APRDEB/BT_Addr
+/data/nvram/APCFG/APRDEB/WIFI_CUSTOM
+/data/nvram/APCFG/APRDCL/FILE_VER
+/data/nvram/APCFG/APRDCL/AUXADC
+/data/nvram/RestoreFlag
+```
+
+There is a file map with promising hardcoded names such as `Audio_Effect`, but there is no file data for that name -
+failure again? And even if there were some audio-changing bytes, why would you need to use exe later? Conf_a/b/c are
+actually nvram files from different devices and involved in transforming your DAP into another model. Perhaps they
+change device identification so exe installer could recognize the device? Exe installer then flashes new little kernel
+and patches nvp a little (changing those configuration nodes?).
+
+So now you have nvram from DMP-Z1 (for example), little kernel from DMP-Z1 and your configuration nodes say DMP-Z1.
+After these manipulations your device is bootable and software believes that your model is actually DMP-Z1. Software
+then sets up audio processing just like it would on real hardware, unlocking DseeAi or whatever else.
+
+And there is no _custom_ audio data changes, only swapping files from one device to another.
+
+There are some questions left: how does DMP-1Z even work on lower-end hardware? From what I've gathered, it has its own
+Aulos card(?) instead of cxd3778gf used on NW-A50; there are no volume tables and dac tools in its stock firmware. And
+where do these little kernels and nvrams used in tunings came from, dumped from devices?
+
+I am 100% sure that I've mixed up some info about nvp and nvram (it was a badly documented process), but overall the
+conclusion is this: external tuning is just a device mutating into another, not a handcrafted sound settings bundle.
+
+---
+
+Still, all this info is not related to regions.
+
+Let's sum it up: volume tables depend on regions, but there are only two variations - `_cew` and regular one. During
+boot nothing else changed sound signature. Walkman One provides device model changer (external tunings) and juggles
+sound outputs (plus mode, adler libs), but only when asked for. Where is that region value handler that makes device
+sound different?
+
+It's buried deep in software and split
+between `libConfigurationService`, `HgrmMediaPlayerApp`, `libDmpFeature`, `libSoundServiceFW` and maybe other libs.
+
+Here is an example of such check in `libDmpFeature`:
+
+
+
+It checks currently set region against 0x8 (MX3), 0x3 (CEW) and 0x7 (TW); if region matches then that strategy is
+*probably* applied. Why probably? Software doesn't blindly trust `libDmpFeature` and does additional checks on its own.
+For example, `pst::dmpfeature::DmpFeature::IsFeaturedClearAudioPlus` always returns `true` no matter the region and
+model; despite that there is no ClearAudio on WM1Z. And even if it's used, *how* is it used? Maybe it spawns a gui popup
+with some volume limiting info, maybe not.
+
+These checks are scattered all over the place. Example above decompiled very nicely, but similar
+checks in `HgrmMediaPlayerApp` are hard to find and follow through. So... there is no definitive answer for "what
+exactly happens to the sound when region changes". There is no huge table with rules saying "region KR3 has 50% more
+bass and 36% less treble", only binary code all over the place.
+
+What about Walkman One J123 firmware with added J1/2/3 regions? These regions were actually added, but not to
+Walkman software stack - they were added to Walkman One *config script*, ids `0x00000100,0x00000200,0x00000300`. Not a
+single significant file was changed, only this script and NPAudio-related stuff. Most likely Mr.Walkman was looking
+around some binaries and found these just like I did on that screenshot above. Or perhaps he was bruteforcing
+regions?
+
+All that info above may look like an attempt to belittle Mr.Walkman and his contribution to custom firmware scene, but
+it is not. In my mind his firmware turned from some black box with unknown inputs into a logically structured piece of
+software. It does suffer from lack of GUI, but ultimately it doesn't matter because it still delivers better sound. Yes,
+there are no sound tunings made exclusively by him, but putting all this together and spreading over various devices is
+a hard job; I respect that. It just happened that he made a sound-changing firmware, and I was curious about inner
+workings of it.
+
+At the end the mystery of regions stays unsolved; most people will continue to speak in vague terms without actual data
+to back up their opinions. At least this hunt for info made me research volume tables and make an editor for them. By
+the way NW-A100 also uses `.tbl` files, different size and structure, but the process should apply to it just like it
+does on NW-A50.
+
+[1]: https://www.rockbox.org/wiki/SonyNWDestTool.html
+
+[2]: http://www.lieberbiber.de/2015/07/05/mediatek-details-little-kernel/
+
+[3]: https://4pda.to/forum/index.php?showtopic=535287&st=5260#entry46980943
+
+[4]: https://oss.sony.net/Products/Linux/Audio/NW-A57.html
+
+[5]: https://android.googlesource.com/platform/system/core/+/master/init
+
+[6]: https://wiki.archlinux.org/title/CPU_frequency_scaling#Scaling_governors
+
+[7]: https://github.com/unknown321/wampy/blob/master/tunings/uniq.txt
+
+[8]: https://tldp.org/LDP/lkmpg/2.4/html/x579.html
+
+[9]: https://www.head-fi.org/threads/sony-walkman-custom-firmware-non-android.943661/post-18092878
\ No newline at end of file
diff --git a/Makefile b/Makefile
index ec6610e..c5a9dc5 100644
--- a/Makefile
+++ b/Makefile
@@ -54,7 +54,7 @@ push:
$(ADB) push base-2.91.wsz $(VENDOR)/usr/share/skins/winamp/base-2.91.wsz
$(MAKE) -C server push
-nw-installer/installer/userdata.tar.gz: LICENSE_3rdparty qr.bmp
+nw-installer/installer/userdata.tar.gz: LICENSE_3rdparty qr.bmp qrDonate.bmp
$(MAKE) -C nw-installer prepare
cp $(INSTALL)/bin/$(PRODUCT) installer/
bash -c "cp $(INSTALL)/lib/libMagick{++,Core,Wand}-7.Q8HDRI.so installer/"
@@ -70,9 +70,13 @@ nw-installer/installer/userdata.tar.gz: LICENSE_3rdparty qr.bmp
cp cassette/cassette.tar.gz installer/
$(MAKE) -C digital_clock
cp digital_clock/digital_clock.tar.gz installer/
+ $(MAKE) -C tunings
+ cp tunings/tunings.tar.gz installer/
+ cp libs/llusbdac/llusbdac/llusbdac.ko installer/
cp LICENSE installer/
cp LICENSE_3rdparty installer/
cp qr.bmp installer/
+ cp qrDonate.bmp installer/
echo -n "$(PRODUCT), version " > installer/product_info
grep VERSION src/Version.h | cut -f 3,4,5 -d " " | sed 's/"//g' >> installer/product_info
tar -C installer -cf nw-installer/installer/userdata.tar.gz \
@@ -87,10 +91,13 @@ nw-installer/installer/userdata.tar.gz: LICENSE_3rdparty qr.bmp
base-2.91.wsz \
cassette.tar.gz \
digital_clock.tar.gz \
+ tunings.tar.gz \
+ llusbdac.ko \
upgtool-linux-arm5 \
LICENSE \
LICENSE_3rdparty \
qr.bmp \
+ qrDonate.bmp \
product_info \
wampy || rm -f nw-installer/installer/userdata.tar.gz
cat LICENSE LICENSE_3rdparty > nw-installer/installer/windows/LICENSE.txt.user
@@ -170,10 +177,22 @@ LICENSE_3rdparty:
@$(ECHO) -e "\n***\nDear ImGui:\n" >> $@
@cat libs/imgui/LICENSE.txt >> $@
+ @$(ECHO) -e "\n***\nLumixEngine:\n" >> $@
+ @cat libs/LumixEngine_LICENSE.txt >> $@
+
+ @$(ECHO) -e "\n***\nLLUSBDAC:\n" >> $@
+ @cat libs/llusbdac/LICENSE >> $@
+
# https://github.com/fukuchi/libqrencode
qr.bmp:
@qrencode -o qr.png -m 1 -s 7 https://github.com/unknown321/$(PRODUCT)
@convert qr.png -type palette qr.bmp
@rm qr.png
+qrDonate.bmp:
+ @qrencode -o qrDonate.png -m 1 -s 7 https://boosty.to/unknown321/donate
+ @convert qrDonate.png -type palette qrDonate.bmp
+ @rm qrDonate.png
+
+
.PHONY: build build-arm docker docker_digital_clock push profile profile-arm valgrind deps release release-clean LICENSE_3rdparty server userdata
diff --git a/README.md b/README.md
index 826447e..32e5e98 100644
--- a/README.md
+++ b/README.md
@@ -6,30 +6,40 @@ Interface addon for NW-A50 series Walkman player.
Also works on [Walkman One (NW-A50)](https://www.mrwalkman.com/p/sony-nw-a50series-custom-firmware.html).
+
## Features:
- Winamp 2 skin support
- Custom cassette skins
- On-the-fly skin change
+- Volume table editor
- Default player enhancements (add clock and increase cover art size)
+- Digital clock skin (pretty!)
+- [Low latency USB DAC module](https://github.com/zhangboyang/llusbdac)
See [USAGE.md](./USAGE.md).
## Device support
-| Device | Stock | Walkman One | Notes |
-|-----------------|-------|-------------|-----------------------------------------------------------------------------------|
-| NW-A50 | ✅ | ✅ | |
-| NW-A40 | ? | ✅ | help wanted for stock firmware [#1](https://github.com/unknown321/wampy/issues/1) |
-| └──[A50 mod][1] | ? | - | needs testing, use NW-A50 during installation |
-| NW-A30 | ? | ? | help wanted for stock firmware [#1](https://github.com/unknown321/wampy/issues/1) |
-| NW-ZX300 | ? | ? | help wanted [#12](https://github.com/unknown321/wampy/issues/12) |
-| NW-WM1A/Z | ? | ? | help wanted [#13](https://github.com/unknown321/wampy/issues/13) |
-| DMP-Z1 | ? | ? | help wanted |
+| Device | Stock | Walkman One | Notes |
+|-----------------|-------|-------------|----------------------------------------|
+| NW-A50 | ✅ | ✅ | |
+| NW-A40 | ? | ✅ (v1.3.2) | help wanted for stock firmware [#1][2] |
+| └──[A50 mod][1] | ? | - | looking for tester |
+| NW-A30 | ❌ | ? | looking for Walkman One tester [#1][2] |
+| NW-ZX300 | ? | ? | looking for tester [#12][3] |
+| NW-WM1A/Z | ? | ? | looking for tester [#13][4] |
+| DMP-Z1 | ? | ? | looking for tester |
[1]: https://www.mrwalkman.com/p/nw-a40-stock-update.html
+[2]: https://github.com/unknown321/wampy/issues/1
+
+[3]: https://github.com/unknown321/wampy/issues/12
+
+[4]: https://github.com/unknown321/wampy/issues/13
+
## Install
### Pre-install
@@ -55,4 +65,10 @@ See [BUILD.md](./BUILD.md)
[Making of](./MAKING_OF.md)
+[Making of sound settings](./MAKING_OF_SOUND_SETTINGS.md)
+
[scrobbler](https://github.com/unknown321/scrobbler)
+
+## Support me
+
+https://boosty.to/unknown321/donate
\ No newline at end of file
diff --git a/SOUND_SETTINGS.md b/SOUND_SETTINGS.md
new file mode 100644
index 0000000..82ff449
--- /dev/null
+++ b/SOUND_SETTINGS.md
@@ -0,0 +1,200 @@
+# Sound settings
+
+
+
+* [Sound settings](#sound-settings)
+ * [Table types](#table-types)
+ * [Master volume](#master-volume)
+ * [Value editor](#value-editor)
+ * [Master volume, DSD](#master-volume-dsd)
+ * [Tone control](#tone-control)
+ * [Status](#status)
+ * [Low latency USB DAC (llusbdac)](#low-latency-usb-dac-llusbdac)
+ * [Usage](#usage)
+ * [Issues](#issues)
+ * [Other issues](#other-issues)
+
+
+
+NW-* series support adjusting volume tables on the fly. These tables impact sound signature and are loaded on boot. On
+Walkman One these are known as "Gain mode".
+
+Wampy provides interface for manipulating those tables.
+
+## Table types
+
+There are three table types supported by wampy.
+
+- master volume
+- master volume (dsd)
+- tone control
+
+Wampy comes with 6 master, 4 master dsd and 2 tone control tables. All of these are taken from stock firmwares of:
+NW-A40, NW-A50, WM1A, WM1Z, ZX-300. DMP-Z1 does not have these at all (different SOC). NW-A30 uses incompatible tables.
+These devices share tables, and sometimes they have different names, but same content.
+
+Here is the table with default tables for each device:
+
+
+
+
Device product
+
BBDMP5_linux
+
BBDMP3_linux
+
BBDMP2_linux
+
+
+
Model ID
+
0x250000 (A50)
+
0x230000 (ZX300?)
+
0x240000 (A40)
+
0x200000, 0x210000 (WM1A/Z?)
+
0x220000 (A30)
+
+
+
Region
+
CEW2, KR3
+
Others
+
CEW2
+
Others
+
CEW2, KR3
+
Others
+
CEW2
+
Others
+
CEW2
+
Others
+
+
+
Volume table
+
ov_1291_cew ov_dsd_1291_cew
+
ov_1291 ov_dsd_1291
+
ov_1288_cew ov_dsd_1288_cew
+
ov_1288 ov_dsd_1288
+
ov_1290_cew ov_dsd_1290_cew
+
ov_1290 ov_dsd_1290
+
ov_127x_cew ov_dsd_127x_cew
+
ov_127x ov_dsd_127x
+
ov_1280_cew ov_dsd_1280_cew
+
ov_1280 ov_dsd_1280
+
+
+
Tone control
+
tc_1291
+
tc_1288
+
tc_1290
+
tc_127x
+
tc_1280
+
+
+
+Your sound signature without any software effects is affected by those files on boot and depends on: model id, device
+product and region. Only two regions matter at this stage of processing - CEW2 and KR3 (region names taken from Walkman
+One). Other regions affect sound on software level. `_cew` tables have much lower volume.
+
+After deduplication there were a handful of files left. You can check which files were deduplicated
+in [this file](https://github.com/unknown321/wampy/blob/master/tunings/uniq.txt). Yes, Walkman One uses tables from
+other firmwares without any modifications; same with Eclipse. Model suffix indicates firmware it was taken from with
+stock priority over mods.
+
+### Master volume
+
+
+
+System files are marked by Ⓢ symbol; you cannot save these, but you can make a copy.
+
+Master volume table structure:
+
+
+
+
+
Sound effect OFF
+
Sound effect ON
+
+
+
Table type
+
Headphone
Bluetooth
Others
+
Headphone
Bluetooth
Others
+
+
+
+
Value type
+
LineOut
Headphones
Others
LineOut
Headphones
Others
LineOut
Headphones
Others
+
LineOut
Headphones
Others
LineOut
Headphones
Others
LineOut
Headphones
Others
+
+
+
+Every value type has 120 values, one value per volume point. Value range: 0-255.
+
+On image above you can see that at current volume (~40, X axis, indicated by red line) output for headphones on high
+gain using HpOut output will be modified by a value ~100 (Y axis).
+
+What does that value means? Depends on value type. For example, `Play` type works in opposite direction, the less value
+is, the louder is output.
+
+Use buttons `Copy val` and `Paste val` to copy and paste values from/to currently selected value type (everything on
+graph). Press `Edit` to edit values. `Apply` immediately applies changes. Press `Save` to save file
+to `wampy/sound_settings/master_volume/` directory.
+
+There are 26 table types and 13 value types specified by codec; you usually want to change `Play` and `Headphones` types
+for `Headphones, high gain` table. Feel free to experiment.
+
+Volume tables are NOT applied after reboot, you have to load them manually. There might be an option for automatically
+loading them later.
+
+### Value editor
+
+
+
+Drag point wherever you want it to be. `Reset` button resets everything that happened on editor screen (and only
+there).
+
+There is no multitouch, use zoom buttons. Panning sucks, zoom malfunctions in some cases, but hey, it works. There will
+be improvements later.
+
+### Master volume, DSD
+
+
+
+DSD tables are simpler. There is no sound effect toggle and value type, just table type. You are interested in `DSD*`
+tables. X-axis: 120 values. Value range: 0-32768.
+
+### Tone control
+
+
+
+This table is **NOT** related to tone control settings in Walkman application. You are interested in `SAMP_GENERAL_HP`
+table (maybe `NAMP_GENERAL_HP`?), others are for NW500/700 headphones. X-axis: 320 values. Value range: 0-256.
+
+### Status
+
+
+
+Press "Refresh" to get actual values. Press on "Refreshed" label to remove it.
+
+At this page you can see your model, region and device product, which affect volume table loaded by default.
+
+Tables shown are tables currently in use.
+
+Audio card, audio device and CPU frequency change depending on type of currently played file (DSD/HiRes) and on Walkman
+One Plus mode status.
+
+## Low latency USB DAC (llusbdac)
+
+### Usage
+
+Stop currently playing track, press huge button to start, see overlay, navigate to USB DAC screen.
+
+Source code is available on https://github.com/zhangboyang/llusbdac.
+
+### Issues
+
+Random crashes during module startup, especially when music is playing.
+
+Module cannot be unloaded after USB DAC function was enabled (overlay stays on); reboot required;
+
+## Other issues
+
+Sound setting tabs require double taps for reasons I wasn't able to understand. This is a bug, not a feature.
+
+## See also
+
+[Making of sound settings](./MAKING_OF_SOUND_SETTINGS.md)
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
index 55de482..42ec3ef 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,5 +1,9 @@
-# general
+#general
+- [ ] make llusbdac build everywhere, not just on my machine
+- [ ] apply tunings on boot
+- [x] export bookmarks
+- [x] current volume on volume table graphs
- [x] read skins from sd card a lot of code for like what, 10 mb of skins on sd card? just use internal storage
- [x] cassette default tape text pos bug on first start
- [x] digital clock compressed quality drops no matter what
@@ -67,6 +71,7 @@
- [ ] visualization from NW-WM1Z spectrum analyzer
- [ ] eq button custom action - too small?
- [ ] single connection over socket?
+- [ ] enqueue?
- [x] walkman One settings in wampy settings
- [x] simple clock skin
- [x] region.txt (winamp)
diff --git a/USAGE.md b/USAGE.md
index 2ec6713..0f8e200 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -28,6 +28,7 @@
* [Issues](#issues)
* [Walkman One](#walkman-one)
* [Stock firmware](#stock-firmware)
+* [Sound settings, Low-latency USB DAC (♪♫ button)](#sound-settings-low-latency-usb-dac--button)
* [Providing debug information](#providing-debug-information)
@@ -161,7 +162,6 @@ You can get skins from [Winamp Skin Museum](https://skins.webamp.org/).
Issues:
- Tapping position bar doesn't work sometimes
-- Playlist flickers sometimes
- Track time and title marquee tick in uneven intervals
Quirks:
@@ -210,17 +210,9 @@ Tap middle of the screen while in `Randomize?` mode to re-roll current tape/reel
#### Issues and quirks
-Issues:
-
-- Default reels have a lot of textures, which may negatively impact performance. Sometimes reboot is required after
- changing settings.
-- Changing active skin to cassette may crash device if there is not enough memory at the moment.
-
-Quirks:
+Issues: none?
-- Default reels and tapes load for quite some time (at least 70 seconds). Wampy is not available during loading and
- there is no indication of loading. Default player loads faster than that, so there is a period of time when user
- expects everything to work, but it is not possible yet.
+Quirks: none?
### Digital clock
@@ -317,20 +309,33 @@ Alternatively, see advanced section below.
#### Cassette skins, advanced
-JPEG-based skins are very slow to load and take a lot of memory. You should use compressed textures (ETC1) and atlases.
+JPEG-based skins are very slow to load and take a lot of memory. You should use compressed textures (ETC1, `.pkm`
+extension) and atlases.
File naming:
-- tapes/myCoolTape/tape.pkm
-- tapes/myCoolTape/config.txt
-- reels/awesomeReel/atlas.pkm
-- reels/awesomeReel/atlas.txt
-- reels/awesomeReel/config.txt
+```text
+wampy/
+├── config.ini
+└── skins
+ ├── reels
+ │ └── awesomeReel
+ │ ├── atlas.pkm
+ │ ├── atlas.txt
+ │ └── config.txt
+ └── tapes
+ └── myCoolTape
+ ├── config.txt
+ └── tape.pkm
+```
+
+ETC1 textures are produced from PNG by etc1tool - [Windows][1], [Mac][2], [Linux][3]
+
+[1]: https://dl.google.com/android/repository/platform-tools-latest-windows.zip
+
+[2]: https://dl.google.com/android/repository/platform-tools-latest-darwin.zip
-ETC1 textures are produced from PNG by
-etc1tool - [Windows](https://dl.google.com/android/repository/platform-tools-latest-windows.zip),
-[Mac](https://dl.google.com/android/repository/platform-tools-latest-darwin.zip),
-[Linux](https://dl.google.com/android/repository/platform-tools-latest-linux.zip)
+[3]: https://dl.google.com/android/repository/platform-tools-latest-linux.zip
Atlas contains all the reel images. Maximum resolution: 4096x4096.
@@ -377,23 +382,27 @@ delayMS: 100
-`Swap prev/next buttons` swaps prev/next buttons when Wampy is on to match Winamp track buttons.
+`Swap prev/next buttons` swaps prev/next buttons when Wampy is on to match Winamp track button directions.
-`Huge cover art` changes cover art resolution in default player to 480x480. It also changes title field
+`Huge cover art` changes cover art resolution in default player to 480x480 (max width). It also changes title field
to ` - `.
`Show time` adds current time to volume indicator. Time updates every 10 seconds, 24 hours format. Disabled on
Walkman One due to interface clutter.
-`Limit max volume` limits max volume to 63 in Wampy, so you can use whole volume slider without going deaf.
+`Limit max volume` limits max volume to 63 in Wampy, so you can use whole Winamp volume slider without going deaf.
`Disable touchscreen` disables touchscreen on next Wampy toggle. To temporarily enable touchscreen in Wampy while in
this mode, set volume to 120 in default player app and toggle Wampy on.
+`Export bookmarks` button saves bookmark files to `wampy/bookmarks/` directory.
+
+`Remove wampy logs` button removes logs from `wampy/log/` directory.
+
`Debug` checkbox enables some logging, which you (the user) don't need. It also shows codec/bitrate when active skin is
cassette.
-`Limit fps`, target fps = 24 . Does it really help to save battery/improve performance? Don't know, off by default.
+`Limit fps`, target fps = 24. Does it really help to save battery/improve performance? Don't know, off by default.
Applied after restart.
@@ -414,7 +423,7 @@ Applied after restart.
Walkman One settings
-User interface Walkman One firmware. Follow instructions in `CFW/settings.txt` file.
+User interface for Walkman One firmware. Follow instructions in `CFW/settings.txt` file.
Sound signature is applied on device without PC connection. `CFW/External_Tunings` directory **must** be present.
@@ -427,6 +436,10 @@ Sound signature is applied on device without PC connection. `CFW/External_Tuning
On stock firmware there is only one option: UI color change.
+## Sound settings, Low-latency USB DAC (♪♫ button)
+
+See [SOUND_SETTINGS.md](./SOUND_SETTINGS.md)
+
## Providing debug information
Wampy automatically collects crash dumps and logs on start. These are located in `wampy/log/` directory. Feel free to
diff --git a/images/adler1.png b/images/adler1.png
new file mode 100644
index 0000000..e8b61c9
Binary files /dev/null and b/images/adler1.png differ
diff --git a/images/adler2.png b/images/adler2.png
new file mode 100644
index 0000000..347d77d
Binary files /dev/null and b/images/adler2.png differ
diff --git a/images/is_ship.png b/images/is_ship.png
new file mode 100644
index 0000000..126dc90
Binary files /dev/null and b/images/is_ship.png differ
diff --git a/images/promo-cassette-skin.png b/images/promo-cassette-skin.png
new file mode 100644
index 0000000..bdacbb5
Binary files /dev/null and b/images/promo-cassette-skin.png differ
diff --git a/images/promo-digiclock.png b/images/promo-digiclock.png
new file mode 100644
index 0000000..c714672
Binary files /dev/null and b/images/promo-digiclock.png differ
diff --git a/images/promo-mvt.png b/images/promo-mvt.png
new file mode 100644
index 0000000..5822c49
Binary files /dev/null and b/images/promo-mvt.png differ
diff --git a/images/promo.xcf b/images/promo.xcf
index 072588e..8e6e5c3 100644
Binary files a/images/promo.xcf and b/images/promo.xcf differ
diff --git a/images/settings-dsd.png b/images/settings-dsd.png
new file mode 100644
index 0000000..911d292
Binary files /dev/null and b/images/settings-dsd.png differ
diff --git a/images/settings-llusbdac.png b/images/settings-llusbdac.png
new file mode 100644
index 0000000..bd1c555
Binary files /dev/null and b/images/settings-llusbdac.png differ
diff --git a/images/settings-master-volume-table.png b/images/settings-master-volume-table.png
new file mode 100644
index 0000000..ead84a5
Binary files /dev/null and b/images/settings-master-volume-table.png differ
diff --git a/images/settings-misc.png b/images/settings-misc.png
index 806abed..f2a17e9 100644
Binary files a/images/settings-misc.png and b/images/settings-misc.png differ
diff --git a/images/settings-mvt-editor.png b/images/settings-mvt-editor.png
new file mode 100644
index 0000000..de2cae8
Binary files /dev/null and b/images/settings-mvt-editor.png differ
diff --git a/images/settings-status.png b/images/settings-status.png
new file mode 100644
index 0000000..971db1a
Binary files /dev/null and b/images/settings-status.png differ
diff --git a/images/settings-tct.png b/images/settings-tct.png
new file mode 100644
index 0000000..f27ec17
Binary files /dev/null and b/images/settings-tct.png differ
diff --git a/installer/.gitignore b/installer/.gitignore
index 2bace47..bbce69a 100644
--- a/installer/.gitignore
+++ b/installer/.gitignore
@@ -8,3 +8,4 @@ lib*
*.bmp
LICENSE*
upgtool*
+*.ko
diff --git a/installer/run.sh b/installer/run.sh
index 2936435..24395d2 100644
--- a/installer/run.sh
+++ b/installer/run.sh
@@ -100,6 +100,15 @@ install() {
log "installing qr code"
cp qr.bmp ${VENDOR}/usr/share/${BINARY}/
+ cp qrDonate.bmp ${VENDOR}/usr/share/${BINARY}/
+
+ log "installing tunings"
+ mkdir -p ${VENDOR}/usr/share/${BINARY}/sound_settings/
+ busybox tar -C ${VENDOR}/usr/share/${BINARY}/sound_settings/ -xf tunings.tar.gz
+
+ log "installing llusbdac"
+ mkdir -p ${VENDOR}/modules/
+ cp llusbdac.ko ${VENDOR}/modules/
}
log "installing $(cat product_info)"
diff --git a/libs/LumixEngine_LICENSE.txt b/libs/LumixEngine_LICENSE.txt
new file mode 100644
index 0000000..9ba434f
--- /dev/null
+++ b/libs/LumixEngine_LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-9001 Mikulas Florek
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/libs/Makefile b/libs/Makefile
index 57784df..524fa04 100644
--- a/libs/Makefile
+++ b/libs/Makefile
@@ -98,7 +98,10 @@ protobuf:
make -f Makefile.protobuf \
"
-all: patch libEGL.so libGLESv2.so libjpeg magick protobuf
+llusbdac:
+ $(MAKE) -f Makefile.llusbdac
+
+all: patch libEGL.so libGLESv2.so libjpeg magick protobuf llusbdac
clean:
rm -rf libjpeg-turbo/build-*
@@ -110,4 +113,4 @@ clean:
.DEFAULT_GOAL := all
-.PHONY: libjpeg-turbo/build-arm libjpeg-turbo/build-x86 protobuf patch
+.PHONY: libjpeg-turbo/build-arm libjpeg-turbo/build-x86 protobuf patch llusbdac
diff --git a/libs/Makefile.llusbdac b/libs/Makefile.llusbdac
new file mode 100644
index 0000000..b4e702f
--- /dev/null
+++ b/libs/Makefile.llusbdac
@@ -0,0 +1,18 @@
+# works on my machine
+
+KERNEL_PATH ?= $(shell pwd)/../../../../walkman/NW-A50/kernel/linux
+IMAGE=wampy-builder
+DOCKER=docker run -it --rm -v $(KERNEL_PATH):$(KERNEL_PATH) -v `pwd`:`pwd` -w `pwd` $(IMAGE)
+
+llusbdac/kernel:
+ ln -s $(KERNEL_PATH) $(shell pwd)/llusbdac/kernel
+
+llusbdac/llusbdac/llusbdac.ko:
+ $(DOCKER) bash -c "cd llusbdac/llusbdac/ && make"
+
+all: llusbdac/llusbdac/llusbdac.ko
+
+clean:
+ $(DOCKER) bash -c "cd llusbdac/llusbdac/ && make clean"
+
+.DEFAULT_GOAL := all
\ No newline at end of file
diff --git a/libs/llusbdac b/libs/llusbdac
new file mode 160000
index 0000000..710a174
--- /dev/null
+++ b/libs/llusbdac
@@ -0,0 +1 @@
+Subproject commit 710a17415e785afbc773906798445bdf03626722
diff --git a/nw-installer b/nw-installer
index 699ba0c..58a6e38 160000
--- a/nw-installer
+++ b/nw-installer
@@ -1 +1 @@
-Subproject commit 699ba0c874092c09726af00d6686c6731ad6025d
+Subproject commit 58a6e38837f21862b6295bafc88122aff3968e59
diff --git a/qrDonate.bmp b/qrDonate.bmp
new file mode 100644
index 0000000..1571917
Binary files /dev/null and b/qrDonate.bmp differ
diff --git a/server/armv5-unknown-linux-gnueabihf/Controller.cpp b/server/armv5-unknown-linux-gnueabihf/Controller.cpp
index ef18cc3..d5a468f 100644
--- a/server/armv5-unknown-linux-gnueabihf/Controller.cpp
+++ b/server/armv5-unknown-linux-gnueabihf/Controller.cpp
@@ -322,6 +322,7 @@ void Controller::GetStatus(Command::Command *c) {
s->set_elapsed(provider.curTime);
s->set_hires(provider.hires);
s->set_volume(provider.volume * 100 / maxVolume);
+ s->set_volumeraw(provider.volume);
c->set_code(Command::OK);
}
diff --git a/server/armv5-unknown-linux-gnueabihf/command.pb.cc b/server/armv5-unknown-linux-gnueabihf/command.pb.cc
index 31fc002..2f0882c 100644
--- a/server/armv5-unknown-linux-gnueabihf/command.pb.cc
+++ b/server/armv5-unknown-linux-gnueabihf/command.pb.cc
@@ -167,7 +167,8 @@ PROTOBUF_CONSTEXPR Status::Status(
, /*decltype(_impl_.volume_)*/0
, /*decltype(_impl_.bitrate_)*/0
, /*decltype(_impl_.samplerate_)*/0
- , /*decltype(_impl_.bitdepth_)*/0} {}
+ , /*decltype(_impl_.bitdepth_)*/0
+ , /*decltype(_impl_.volumeraw_)*/0} {}
struct StatusDefaultTypeInternal {
PROTOBUF_CONSTEXPR StatusDefaultTypeInternal()
: _instance(::_pbi::ConstantInitialized{}) {}
@@ -296,6 +297,7 @@ const uint32_t TableStruct_command_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(
PROTOBUF_FIELD_OFFSET(::Command::Status, _impl_.samplerate_),
PROTOBUF_FIELD_OFFSET(::Command::Status, _impl_.bitdepth_),
PROTOBUF_FIELD_OFFSET(::Command::Status, _impl_.playlist_),
+ PROTOBUF_FIELD_OFFSET(::Command::Status, _impl_.volumeraw_),
0,
2,
3,
@@ -307,6 +309,7 @@ const uint32_t TableStruct_command_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(
9,
10,
1,
+ 11,
};
static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
{ 0, 16, -1, sizeof(::Command::Command)},
@@ -318,7 +321,7 @@ static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protode
{ 65, 72, -1, sizeof(::Command::Seek)},
{ 73, 84, -1, sizeof(::Command::Track)},
{ 89, -1, -1, sizeof(::Command::Playlist)},
- { 96, 113, -1, sizeof(::Command::Status)},
+ { 96, 114, -1, sizeof(::Command::Status)},
};
static const ::_pb::Message* const file_default_instances[] = {
@@ -354,30 +357,30 @@ const char descriptor_table_protodef_command_2eproto[] PROTOBUF_SECTION_VARIABLE
"\004Seek\022\r\n\005value\030\001 \002(\005\"W\n\005Track\022\r\n\005Track\030\001"
" \002(\005\022\016\n\006Artist\030\002 \002(\t\022\r\n\005Title\030\003 \002(\t\022\020\n\010D"
"uration\030\004 \002(\005\022\016\n\006Active\030\005 \002(\010\")\n\010Playlis"
- "t\022\035\n\005Track\030\001 \003(\0132\016.Command.Track\"\327\001\n\006Sta"
+ "t\022\035\n\005Track\030\001 \003(\0132\016.Command.Track\"\352\001\n\006Sta"
"tus\022\r\n\005Codec\030\001 \002(\t\022\017\n\007Elapsed\030\002 \002(\005\022\021\n\tP"
"layState\030\003 \002(\005\022\r\n\005HiRes\030\004 \002(\010\022\017\n\007Shuffle"
"\030\005 \002(\010\022\016\n\006Repeat\030\006 \002(\005\022\016\n\006Volume\030\007 \002(\005\022\017"
"\n\007BitRate\030\010 \001(\005\022\022\n\nSampleRate\030\t \001(\002\022\020\n\010B"
"itDepth\030\n \001(\005\022#\n\010Playlist\030\013 \002(\0132\021.Comman"
- "d.Playlist*\212\003\n\004Type\022\017\n\013CMD_UNKNOWN\020\000\022\031\n\025"
- "CMD_GET_WINDOW_STATUS\020\001\022\023\n\017CMD_HIDE_WIND"
- "OW\020\002\022\023\n\017CMD_SHOW_WINDOW\020\003\022\022\n\016CMD_GET_STA"
- "TUS\020\004\022\014\n\010CMD_TEST\020\005\022\022\n\016CMD_SET_VOLUME\020\006\022"
- "\014\n\010CMD_SEEK\020\007\022\026\n\022CMD_TOGGLE_SHUFFLE\020\010\022\025\n"
- "\021CMD_TOGGLE_REPEAT\020\t\022\022\n\016CMD_NEXT_TRACK\020\n"
- "\022\022\n\016CMD_PREV_TRACK\020\013\022\014\n\010CMD_PLAY\020\014\022\r\n\tCM"
- "D_PAUSE\020\r\022\014\n\010CMD_STOP\020\016\022\031\n\025CMD_FEATURE_B"
- "IG_COVER\020\017\022\032\n\026CMD_FEATURE_SHOW_CLOCK\020\020\022\017"
- "\n\013CMD_FAILURE\020\021\022\036\n\032CMD_FEATURE_SET_MAX_V"
- "OLUME\020\022*-\n\014ResponseCode\022\013\n\007UNKNOWN\020\000\022\010\n\004"
- "FAIL\020\001\022\006\n\002OK\020\002*N\n\rWindowVisible\022\026\n\022VISIB"
- "ILITY_UNKNOWN\020\000\022\022\n\016VISIBILITY_YES\020\001\022\021\n\rV"
- "ISIBILITY_NO\020\002"
+ "d.Playlist\022\021\n\tVolumeRaw\030\014 \002(\005*\212\003\n\004Type\022\017"
+ "\n\013CMD_UNKNOWN\020\000\022\031\n\025CMD_GET_WINDOW_STATUS"
+ "\020\001\022\023\n\017CMD_HIDE_WINDOW\020\002\022\023\n\017CMD_SHOW_WIND"
+ "OW\020\003\022\022\n\016CMD_GET_STATUS\020\004\022\014\n\010CMD_TEST\020\005\022\022"
+ "\n\016CMD_SET_VOLUME\020\006\022\014\n\010CMD_SEEK\020\007\022\026\n\022CMD_"
+ "TOGGLE_SHUFFLE\020\010\022\025\n\021CMD_TOGGLE_REPEAT\020\t\022"
+ "\022\n\016CMD_NEXT_TRACK\020\n\022\022\n\016CMD_PREV_TRACK\020\013\022"
+ "\014\n\010CMD_PLAY\020\014\022\r\n\tCMD_PAUSE\020\r\022\014\n\010CMD_STOP"
+ "\020\016\022\031\n\025CMD_FEATURE_BIG_COVER\020\017\022\032\n\026CMD_FEA"
+ "TURE_SHOW_CLOCK\020\020\022\017\n\013CMD_FAILURE\020\021\022\036\n\032CM"
+ "D_FEATURE_SET_MAX_VOLUME\020\022*-\n\014ResponseCo"
+ "de\022\013\n\007UNKNOWN\020\000\022\010\n\004FAIL\020\001\022\006\n\002OK\020\002*N\n\rWin"
+ "dowVisible\022\026\n\022VISIBILITY_UNKNOWN\020\000\022\022\n\016VI"
+ "SIBILITY_YES\020\001\022\021\n\rVISIBILITY_NO\020\002"
;
static ::_pbi::once_flag descriptor_table_command_2eproto_once;
const ::_pbi::DescriptorTable descriptor_table_command_2eproto = {
- false, false, 1534, descriptor_table_protodef_command_2eproto,
+ false, false, 1553, descriptor_table_protodef_command_2eproto,
"command.proto",
&descriptor_table_command_2eproto_once, nullptr, 0, 10,
schemas, file_default_instances, TableStruct_command_2eproto::offsets,
@@ -3011,8 +3014,11 @@ class Status::_Internal {
static void set_has_playlist(HasBits* has_bits) {
(*has_bits)[0] |= 2u;
}
+ static void set_has_volumeraw(HasBits* has_bits) {
+ (*has_bits)[0] |= 2048u;
+ }
static bool MissingRequiredFields(const HasBits& has_bits) {
- return ((has_bits[0] & 0x000000ff) ^ 0x000000ff) != 0;
+ return ((has_bits[0] & 0x000008ff) ^ 0x000008ff) != 0;
}
};
@@ -3042,7 +3048,8 @@ Status::Status(const Status& from)
, decltype(_impl_.volume_){}
, decltype(_impl_.bitrate_){}
, decltype(_impl_.samplerate_){}
- , decltype(_impl_.bitdepth_){}};
+ , decltype(_impl_.bitdepth_){}
+ , decltype(_impl_.volumeraw_){}};
_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
_impl_.codec_.InitDefault();
@@ -3057,8 +3064,8 @@ Status::Status(const Status& from)
_this->_impl_.playlist_ = new ::Command::Playlist(*from._impl_.playlist_);
}
::memcpy(&_impl_.elapsed_, &from._impl_.elapsed_,
- static_cast(reinterpret_cast(&_impl_.bitdepth_) -
- reinterpret_cast(&_impl_.elapsed_)) + sizeof(_impl_.bitdepth_));
+ static_cast(reinterpret_cast(&_impl_.volumeraw_) -
+ reinterpret_cast(&_impl_.elapsed_)) + sizeof(_impl_.volumeraw_));
// @@protoc_insertion_point(copy_constructor:Command.Status)
}
@@ -3080,6 +3087,7 @@ inline void Status::SharedCtor(
, decltype(_impl_.bitrate_){0}
, decltype(_impl_.samplerate_){0}
, decltype(_impl_.bitdepth_){0}
+ , decltype(_impl_.volumeraw_){0}
};
_impl_.codec_.InitDefault();
#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
@@ -3127,10 +3135,10 @@ void Status::Clear() {
reinterpret_cast(&_impl_.volume_) -
reinterpret_cast(&_impl_.elapsed_)) + sizeof(_impl_.volume_));
}
- if (cached_has_bits & 0x00000700u) {
+ if (cached_has_bits & 0x00000f00u) {
::memset(&_impl_.bitrate_, 0, static_cast(
- reinterpret_cast(&_impl_.bitdepth_) -
- reinterpret_cast(&_impl_.bitrate_)) + sizeof(_impl_.bitdepth_));
+ reinterpret_cast(&_impl_.volumeraw_) -
+ reinterpret_cast(&_impl_.bitrate_)) + sizeof(_impl_.volumeraw_));
}
_impl_._has_bits_.Clear();
_internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
@@ -3244,6 +3252,15 @@ const char* Status::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
} else
goto handle_unusual;
continue;
+ // required int32 VolumeRaw = 12;
+ case 12:
+ if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 96)) {
+ _Internal::set_has_volumeraw(&has_bits);
+ _impl_.volumeraw_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
default:
goto handle_unusual;
} // switch
@@ -3346,6 +3363,12 @@ uint8_t* Status::_InternalSerialize(
_Internal::playlist(this).GetCachedSize(), target, stream);
}
+ // required int32 VolumeRaw = 12;
+ if (cached_has_bits & 0x00000800u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt32ToArray(12, this->_internal_volumeraw(), target);
+ }
+
if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
_internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
@@ -3402,13 +3425,18 @@ size_t Status::RequiredFieldsByteSizeFallback() const {
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_volume());
}
+ if (_internal_has_volumeraw()) {
+ // required int32 VolumeRaw = 12;
+ total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_volumeraw());
+ }
+
return total_size;
}
size_t Status::ByteSizeLong() const {
// @@protoc_insertion_point(message_byte_size_start:Command.Status)
size_t total_size = 0;
- if (((_impl_._has_bits_[0] & 0x000000ff) ^ 0x000000ff) == 0) { // All required fields are present.
+ if (((_impl_._has_bits_[0] & 0x000008ff) ^ 0x000008ff) == 0) { // All required fields are present.
// required string Codec = 1;
total_size += 1 +
::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
@@ -3437,6 +3465,9 @@ size_t Status::ByteSizeLong() const {
// required int32 Volume = 7;
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_volume());
+ // required int32 VolumeRaw = 12;
+ total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_volumeraw());
+
} else {
total_size += RequiredFieldsByteSizeFallback();
}
@@ -3509,7 +3540,7 @@ void Status::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBU
}
_this->_impl_._has_bits_[0] |= cached_has_bits;
}
- if (cached_has_bits & 0x00000700u) {
+ if (cached_has_bits & 0x00000f00u) {
if (cached_has_bits & 0x00000100u) {
_this->_impl_.bitrate_ = from._impl_.bitrate_;
}
@@ -3519,6 +3550,9 @@ void Status::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBU
if (cached_has_bits & 0x00000400u) {
_this->_impl_.bitdepth_ = from._impl_.bitdepth_;
}
+ if (cached_has_bits & 0x00000800u) {
+ _this->_impl_.volumeraw_ = from._impl_.volumeraw_;
+ }
_this->_impl_._has_bits_[0] |= cached_has_bits;
}
_this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
@@ -3550,8 +3584,8 @@ void Status::InternalSwap(Status* other) {
&other->_impl_.codec_, rhs_arena
);
::PROTOBUF_NAMESPACE_ID::internal::memswap<
- PROTOBUF_FIELD_OFFSET(Status, _impl_.bitdepth_)
- + sizeof(Status::_impl_.bitdepth_)
+ PROTOBUF_FIELD_OFFSET(Status, _impl_.volumeraw_)
+ + sizeof(Status::_impl_.volumeraw_)
- PROTOBUF_FIELD_OFFSET(Status, _impl_.playlist_)>(
reinterpret_cast(&_impl_.playlist_),
reinterpret_cast(&other->_impl_.playlist_));
diff --git a/server/armv5-unknown-linux-gnueabihf/command.pb.h b/server/armv5-unknown-linux-gnueabihf/command.pb.h
index e0f874f..80e252f 100644
--- a/server/armv5-unknown-linux-gnueabihf/command.pb.h
+++ b/server/armv5-unknown-linux-gnueabihf/command.pb.h
@@ -2024,6 +2024,7 @@ class Status final :
kBitRateFieldNumber = 8,
kSampleRateFieldNumber = 9,
kBitDepthFieldNumber = 10,
+ kVolumeRawFieldNumber = 12,
};
// required string Codec = 1;
bool has_codec() const;
@@ -2178,6 +2179,19 @@ class Status final :
void _internal_set_bitdepth(int32_t value);
public:
+ // required int32 VolumeRaw = 12;
+ bool has_volumeraw() const;
+ private:
+ bool _internal_has_volumeraw() const;
+ public:
+ void clear_volumeraw();
+ int32_t volumeraw() const;
+ void set_volumeraw(int32_t value);
+ private:
+ int32_t _internal_volumeraw() const;
+ void _internal_set_volumeraw(int32_t value);
+ public:
+
// @@protoc_insertion_point(class_scope:Command.Status)
private:
class _Internal;
@@ -2202,6 +2216,7 @@ class Status final :
int32_t bitrate_;
float samplerate_;
int32_t bitdepth_;
+ int32_t volumeraw_;
};
union { Impl_ _impl_; };
friend struct ::TableStruct_command_2eproto;
@@ -3677,6 +3692,34 @@ inline void Status::set_allocated_playlist(::Command::Playlist* playlist) {
// @@protoc_insertion_point(field_set_allocated:Command.Status.Playlist)
}
+// required int32 VolumeRaw = 12;
+inline bool Status::_internal_has_volumeraw() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000800u) != 0;
+ return value;
+}
+inline bool Status::has_volumeraw() const {
+ return _internal_has_volumeraw();
+}
+inline void Status::clear_volumeraw() {
+ _impl_.volumeraw_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000800u;
+}
+inline int32_t Status::_internal_volumeraw() const {
+ return _impl_.volumeraw_;
+}
+inline int32_t Status::volumeraw() const {
+ // @@protoc_insertion_point(field_get:Command.Status.VolumeRaw)
+ return _internal_volumeraw();
+}
+inline void Status::_internal_set_volumeraw(int32_t value) {
+ _impl_._has_bits_[0] |= 0x00000800u;
+ _impl_.volumeraw_ = value;
+}
+inline void Status::set_volumeraw(int32_t value) {
+ _internal_set_volumeraw(value);
+ // @@protoc_insertion_point(field_set:Command.Status.VolumeRaw)
+}
+
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
diff --git a/server/armv5-unknown-linux-gnueabihf/command.proto b/server/armv5-unknown-linux-gnueabihf/command.proto
index ab0aa33..40c0552 100644
--- a/server/armv5-unknown-linux-gnueabihf/command.proto
+++ b/server/armv5-unknown-linux-gnueabihf/command.proto
@@ -100,4 +100,5 @@ message Status {
optional float SampleRate = 9;
optional int32 BitDepth = 10;
required Playlist Playlist = 11;
+ required int32 VolumeRaw = 12;
}
\ No newline at end of file
diff --git a/src/cassette/cassette.cpp b/src/cassette/cassette.cpp
index ddb119e..e8236d1 100644
--- a/src/cassette/cassette.cpp
+++ b/src/cassette/cassette.cpp
@@ -73,6 +73,9 @@ namespace Cassette {
range.AddRanges(io.Fonts->GetGlyphRangesDefault());
range.AddRanges(rangesPunctuation);
+ range.AddChar(ImWchar(0x266a));
+ range.AddChar(ImWchar(0x266b));
+ range.AddChar(ImWchar(0x24c8));
if (fontRanges) {
if (fontRanges->Japanese)
@@ -754,13 +757,15 @@ namespace Cassette {
if (ActiveAtlas) {
if (!ActiveAtlas->atlas.images.empty()) {
- AtlasImage vvv;
+ AtlasImage vvv{};
if (reelIndex > (ActiveAtlas->atlas.images.size() - 1)) {
vvv = ActiveAtlas->atlas.images.at(0);
} else {
vvv = ActiveAtlas->atlas.images.at(reelIndex);
}
ImGui::SetCursorPos(ActiveTape->reelCoords);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
ImGui::Image(
(ImTextureID)ActiveAtlas->atlas.textureID,
ImVec2(vvv.width, vvv.height),
@@ -768,6 +773,7 @@ namespace Cassette {
ImVec2(vvv.u1, vvv.v1),
ImVec4(1, 1, 1, 1)
);
+#pragma GCC diagnostic pop
}
}
diff --git a/src/connector/connector.h b/src/connector/connector.h
index 49f8e63..71aff2e 100644
--- a/src/connector/connector.h
+++ b/src/connector/connector.h
@@ -38,7 +38,8 @@ struct Status {
int Duration{};
int Elapsed{}; // seconds
int Bitrate{};
- int Volume{}; // percentage
+ int Volume{}; // percentage
+ int VolumeRaw{}; // as is
int Shuffle{};
int Repeat{};
int songID{};
diff --git a/src/connector/hagoromo.cpp b/src/connector/hagoromo.cpp
index ab939a2..fde8e02 100644
--- a/src/connector/hagoromo.cpp
+++ b/src/connector/hagoromo.cpp
@@ -353,6 +353,7 @@ namespace Hagoromo {
if (updateVolume) {
status.Volume = c.status().volume();
+ status.VolumeRaw = c.status().volumeraw();
}
status.Duration = playlist.at(0).Duration;
diff --git a/src/connector/mpd.cpp b/src/connector/mpd.cpp
index 945586f..ce0e7f5 100644
--- a/src/connector/mpd.cpp
+++ b/src/connector/mpd.cpp
@@ -171,6 +171,7 @@ namespace MPD {
} catch (...) {
status->Volume = 0;
}
+ status->VolumeRaw = status->Volume;
#endif
break;
case hash("audio"): {
diff --git a/src/dac/cxd3778gf_common.h b/src/dac/cxd3778gf_common.h
new file mode 100644
index 0000000..628d363
--- /dev/null
+++ b/src/dac/cxd3778gf_common.h
@@ -0,0 +1,192 @@
+/*
+ * cxd3778gf_common.h
+ *
+ * CXD3778GF CODEC driver
+ *
+ * Copyright 2013-2016, 2017 Sony Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CXD3778GF_COMMON_HEADER_
+#define _CXD3778GF_COMMON_HEADER_
+
+/* #define TRACE_PRINT_ON */
+/* #define DEBUG_PRINT_ON */
+
+#define CODEC_RAM_WORD_SIZE 5
+#define CODEC_RAM_SIZE (CODEC_RAM_WORD_SIZE * 32 * 2)
+
+/* for 44.1kHz */
+#define CODEC_RAM_441_AREA 0x00 /* off */
+/* for 48kHz */
+#define CODEC_RAM_480_AREA 0x20 /* air plane */
+
+/***************/
+/* definitions */
+/***************/
+
+/* basic */
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#define OFF 0
+#define ON 1
+#define LOW 0
+#define HIGH 1
+
+/* nc headphone type */
+#define NCHP_TYPE_NW750N 0
+#define NCHP_TYPE_NC31 1
+#define NCHP_TYPE_NW500N 2
+#define NCHP_TYPE_OTHER 3
+#define NCHP_TYPE_MAX 3
+
+/* jack mode */
+#define JACK_MODE_HEADPHONE 0
+#define JACK_MODE_ANTENNA 1
+#define JACK_MODE_MAX 1
+
+/* tone control */
+#define TONE_CONTROL_NON_HP 0
+#define TONE_CONTROL_GENERAL_HP 1
+#define TONE_CONTROL_NC_HP 2 /* add NCHP_TYPE_XX to use */
+#define TONE_CONTROL_MAX (2 + NCHP_TYPE_MAX)
+
+/* noise cancel mode */
+#define NOISE_CANCEL_MODE_OFF 0
+#define NOISE_CANCEL_MODE_AIRPLANE 1
+#define NOISE_CANCEL_MODE_TRAIN 2
+#define NOISE_CANCEL_MODE_OFFICE 3
+#define NOISE_CANCEL_MODE_AINC 4
+#define NOISE_CANCEL_MODE_AMBIENT1 5
+#define NOISE_CANCEL_MODE_AMBIENT2 6
+#define NOISE_CANCEL_MODE_AMBIENT3 7
+#define NOISE_CANCEL_MODE_MAX 7
+
+/* noise cancel active */
+#define NC_NON_ACTIVE 0
+#define NC_ACTIVE 1
+#define AMBIENT 2
+#define NC_ACTIVE_MAX 2
+
+/* input device */
+#define INPUT_DEVICE_NONE 0
+#define INPUT_DEVICE_TUNER 1
+#define INPUT_DEVICE_MIC 2
+#define INPUT_DEVICE_LINE 3
+#define INPUT_DEVICE_DIRECTMIC 4
+#define INPUT_DEVICE_MAX 4
+
+/* output device */
+#define OUTPUT_DEVICE_NONE 0
+#define OUTPUT_DEVICE_HEADPHONE 1
+#define OUTPUT_DEVICE_LINE 2
+#define OUTPUT_DEVICE_SPEAKER 3
+#define OUTPUT_DEVICE_FIXEDLINE 4
+#define OUTPUT_DEVICE_MAX 4
+
+/* headphone amp */
+#define HEADPHONE_AMP_NORMAL 0
+#define HEADPHONE_AMP_SMASTER_SE 1
+#define HEADPHONE_AMP_SMASTER_BTL 2
+#define HEADPHONE_AMP_MAX 2
+
+/* headphone smaster_gain_mode */
+#define HEADPHONE_SMASTER_SE_GAIN_MODE_NORMAL 0
+#define HEADPHONE_SMASTER_SE_GAIN_MODE_HIGH 1
+#define HEADPHONE_SMASTER_SE_GAIN_MODE_MAX 1
+
+#define HEADPHONE_SMASTER_BTL_GAIN_MODE_NORMAL 0
+#define HEADPHONE_SMASTER_BTL_GAIN_MODE_HIGH 1
+#define HEADPHONE_SMASTER_BTL_GAIN_MODE_MAX 1
+/* jack status */
+#define JACK_STATUS_SE_NONE 0
+#define JACK_STATUS_SE_3PIN 1
+#define JACK_STATUS_SE_4PIN 2
+#define JACK_STATUS_SE_5PIN 3
+#define JACK_STATUS_SE_ANTENNA 4
+#define JACK_STATUS_SE_MAX 4
+
+#define JACK_STATUS_BTL_NONE 0
+#define JACK_STATUS_BTL 1
+#define JACK_STATUS_BTL_MAX 1
+
+/* master volume */
+#define MASTER_VOLUME_MIN 0
+#define MASTER_VOLUME_MAX 120
+
+/* lr balance volume */
+#define L_BALANCE_VOLUME_MAX 88
+#define R_BALANCE_VOLUME_MAX 88
+
+/* master gain */
+#define MASTER_GAIN_MIN 0
+#define MASTER_GAIN_MAX 30
+
+/* base noise cancel gain index */
+#define BASE_NOISE_CANCEL_GAIN_INDEX_MAX 50
+
+/* user noise cancel / ambient gain index */
+#define USER_DNC_GAIN_INDEX_DEFAULT 15
+#define USER_DNC_GAIN_INDEX_MAX 30
+
+/* external OSC */
+#define EXTERNAL_OSC_441 0
+#define EXTERNAL_OSC_480 1
+#define EXTERNAL_OSC_KEEP 2
+#define EXTERNAL_OSC_OFF 3
+
+/* PCM or DSD mode */
+#define PCM_MODE 0
+#define DSD_MODE 1
+
+/* headphone detect mode */
+#define HEADPHONE_DETECT_POLLING 0
+#define HEADPHONE_DETECT_SELECT 1
+#define HEADPHONE_DETECT_INTERRUPT 2
+#define HEADPHONE_DETECT_MAX 2
+
+/* dnc block inactive mode */
+#define DNC_INACTIVE_OFF 0
+#define DNC1_INACTIVE 1
+#define DNC2_INACTIVE 2
+#define DNC_INACTIVE_MAX 2
+
+/* PCM or DSD mode */
+#define PLAYBACK_LATENCY_NORMAL 0
+#define PLAYBACK_LATENCY_LOW 1
+#define PLAYBACK_LATENCY_MAX 2
+
+/* DSD timed mute params */
+enum MUTE_ID {
+ MUTE_ID_DEFAULT = 0,
+ MUTE_ID_DSD064,
+ MUTE_ID_DSD128,
+ MUTE_ID_DSD256,
+ MUTE_ID_MAX,
+};
+extern int cxd3778gf_timed_mute_ms[MUTE_ID_MAX];
+
+/* basic macro */
+#define minimum(_a, _b) ((_a) < (_b) ? (_a) : (_b))
+#define maximum(_a, _b) ((_a) > (_b) ? (_a) : (_b))
+#define absolute(_a) ((_a) >= 0 ? (_a) : (-(_a)))
+
+/***********/
+/* headers */
+/***********/
+
+#endif
diff --git a/src/dac/cxd3778gf_table.cpp b/src/dac/cxd3778gf_table.cpp
new file mode 100644
index 0000000..06b4d79
--- /dev/null
+++ b/src/dac/cxd3778gf_table.cpp
@@ -0,0 +1,713 @@
+/*
+ * Copyright 2013,2014,2015,2016,2017 Sony Corporation
+ */
+/*
+ * cxd3778gf_table.c
+ *
+ * CXD3778GF CODEC driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "cxd3778gf_table.h"
+// #include "../util/util.h"
+#include "cxd3778gf_common.h"
+#include "fstream"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define TABLE_SIZE_OUTPUT_VOLUME (sizeof(struct cxd3778gf_master_volume) * (MASTER_VOLUME_MAX + 1) * (MASTER_VOLUME_TABLE_MAX + 1) * 2)
+#define TABLE_SIZE_OUTPUT_VOLUME_DSD (sizeof(unsigned int) * (MASTER_VOLUME_TABLE_MAX + 1) * (MASTER_VOLUME_MAX + 1))
+#define TABLE_SIZE_DEVICE_GAIN (sizeof(struct cxd3778gf_device_gain) * (INPUT_DEVICE_MAX + 1))
+#define TABLE_SIZE_TONE_CONTROL (sizeof(unsigned char) * (TONE_CONTROL_TABLE_MAX + 1) * CODEC_RAM_SIZE)
+
+struct port_info {
+ const char *name;
+ void *entry;
+ int node;
+ int size;
+ unsigned char *table;
+ int columns; /* for debug */
+ int rows; /* for debug */
+ int width; /* for debug */
+};
+
+// we are NOT including util.h with all gl deps
+int do_mkdir1(const char *path, mode_t mode) {
+ struct stat st {};
+ int status = 0;
+
+ if (stat(path, &st) != 0) {
+ /* Directory does not exist. EEXIST for race condition */
+ if (mkdir(path, mode) != 0 && errno != EEXIST)
+ status = -1;
+ } else if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ status = -1;
+ }
+
+ return (status);
+}
+
+int mkpath1(const char *path, mode_t mode) {
+ char *pp;
+ char *sp;
+ int status;
+ char *copypath = strdup(path);
+
+ status = 0;
+ pp = copypath;
+ while (status == 0 && (sp = strchr(pp, '/')) != nullptr) {
+ if (sp != pp) {
+ /* Neither root nor double slash in path */
+ *sp = '\0';
+ status = do_mkdir1(copypath, mode);
+ *sp = '/';
+ }
+ pp = sp + 1;
+ }
+ if (status == 0)
+ status = do_mkdir1(path, mode);
+ free(copypath);
+ return (status);
+}
+
+static int check_data(const unsigned char *buf, int size, unsigned int *sum, unsigned int *xr);
+static void dump_data(unsigned char *buf, int size, int columns, int rows, int width);
+
+#if 0
+/* muting all */
+struct cxd3778gf_master_volume cxd3778gf_master_volume_table
+ [2]
+ [MASTER_VOLUME_TABLE_MAX+1]
+ [MASTER_VOLUME_MAX+1]
+ = {{{{0x33,0x33,0x33,0x33,0xC0,0xC0,0x00,0x3F,0x00,0x00}}}};
+#endif
+
+struct cxd3778gf_master_volume cxd3778gf_master_volume_table[2][MASTER_VOLUME_TABLE_MAX + 1][MASTER_VOLUME_MAX + 1] = {
+ {{{0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}};
+
+unsigned int cxd3778gf_master_volume_dsd_table[MASTER_VOLUME_TABLE_MAX + 1][MASTER_VOLUME_MAX + 1] = {{0x00}};
+
+unsigned char cxd3778gf_master_gain_table[MASTER_GAIN_MAX + 1] = {
+ /* 00 */ 0xC0,
+ /* 01 */ 0xC5,
+ /* 02 */ 0xC8,
+ /* 03 */ 0xCC,
+ /* 04 */ 0xD0,
+ /* 05 */ 0xD3,
+ /* 06 */ 0xD6,
+ /* 07 */ 0xD9,
+ /* 08 */ 0xDC,
+ /* 09 */ 0xDF,
+ /* 10 */ 0xE2,
+ /* 11 */ 0xE4,
+ /* 12 */ 0xE6,
+ /* 13 */ 0xE8,
+ /* 14 */ 0xEA,
+ /* 15 */ 0xEC,
+ /* 16 */ 0xEE,
+ /* 17 */ 0xF0,
+ /* 18 */ 0xF2,
+ /* 19 */ 0xF3,
+ /* 20 */ 0xF4,
+ /* 21 */ 0xF5,
+ /* 22 */ 0xF6,
+ /* 23 */ 0xF7,
+ /* 24 */ 0xF8,
+ /* 25 */ 0xF9,
+ /* 26 */ 0xFA,
+ /* 27 */ 0xFB,
+ /* 28 */ 0xFC,
+ /* 29 */ 0xFE,
+ /* 30 */ 0x00,
+};
+
+struct cxd3778gf_device_gain cxd3778gf_device_gain_table[INPUT_DEVICE_MAX + 1] = {
+ /* PGA1 ADC1 */
+ {0x00, 0x00}, /* OFF */
+ {0x06, 0x00}, /* TUNER */
+ {0x00, 0x00}, /* MIC */
+ {0xF8, 0x00}, /* LINE */
+ {0x00, 0x00}, /* DIRECTMIC */
+};
+
+unsigned char cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_MAX + 1][CODEC_RAM_SIZE] = {{0x00}};
+
+static struct port_info port_info_table[] = {
+ {"ovt", nullptr, -1, TABLE_SIZE_OUTPUT_VOLUME, (unsigned char *)cxd3778gf_master_volume_table, 13, 121, 1},
+ {"dgt", nullptr, -1, TABLE_SIZE_DEVICE_GAIN, (unsigned char *)cxd3778gf_device_gain_table, 2, 5, 1},
+ {"tct", nullptr, -1, TABLE_SIZE_TONE_CONTROL, (unsigned char *)cxd3778gf_tone_control_table, 20, 8, 1},
+ {"ovt_dsd", nullptr, -1, TABLE_SIZE_OUTPUT_VOLUME_DSD, (unsigned char *)cxd3778gf_master_volume_dsd_table, 1, 121, 4},
+ {"tct_nh", nullptr, -1, CODEC_RAM_SIZE, (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_NO_HP], 20, 6, 1},
+ {"tct_ng", nullptr, -1, CODEC_RAM_SIZE, (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_NAMP_GENERAL_HP], 20, 6, 1},
+ {"tct_nnw500",
+ nullptr,
+ -1,
+ CODEC_RAM_SIZE,
+ (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_NAMP_NW500N_NCHP],
+ 20,
+ 6,
+ 1},
+ {"tct_nnw750",
+ nullptr,
+ -1,
+ CODEC_RAM_SIZE,
+ (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_NAMP_NW750N_NCHP],
+ 20,
+ 6,
+ 1},
+ {"tct_nnc31", nullptr, -1, CODEC_RAM_SIZE, (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_NAMP_NC31_NCHP], 20, 6, 1},
+ {"tct_sg", nullptr, -1, CODEC_RAM_SIZE, (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_SAMP_GENERAL_HP], 20, 6, 1},
+ {"tct_snw500",
+ nullptr,
+ -1,
+ CODEC_RAM_SIZE,
+ (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_SAMP_NW500N_NCHP],
+ 20,
+ 6,
+ 1},
+ {"tct_snw750",
+ nullptr,
+ -1,
+ CODEC_RAM_SIZE,
+ (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_SAMP_NW750N_NCHP],
+ 20,
+ 6,
+ 1},
+ {"tct_snc31", nullptr, -1, CODEC_RAM_SIZE, (unsigned char *)cxd3778gf_tone_control_table[TONE_CONTROL_TABLE_SAMP_NC31_NCHP], 20, 6, 1},
+ {nullptr, nullptr, -1, 0, nullptr, 0, 0, 0}};
+
+static int initialized = FALSE;
+static struct mutex *global_mutex = nullptr;
+static struct proc_dir_entry *port_directory = nullptr;
+static unsigned char *work_buf = nullptr;
+
+static ssize_t write_table(const std::string &data, const char *buf, size_t size, loff_t *pos) {
+ int index;
+ int count = 0;
+ int rv;
+
+ index = 0;
+
+ *pos = *pos + count;
+
+ if (*pos >= port_info_table[index].size + 8) {
+
+ unsigned int sum;
+ unsigned int xr;
+ rv = check_data(work_buf, port_info_table[index].size, &sum, &xr);
+ if (rv < 0) {
+ return (-1);
+ }
+
+ dump_data(
+ (unsigned char *)port_info_table[index].table,
+ port_info_table[index].size,
+ port_info_table[index].columns,
+ port_info_table[index].rows,
+ port_info_table[index].width
+ );
+ // cxd3778gf_apply_table_change(index);
+
+ printf("done\n");
+ }
+
+ return (count);
+}
+
+// void read_table(const std::string &data, bool dump, master_volume* m) {
+// int index;
+//
+// index = 0;
+//
+// if (dump) {
+// printf("target table = %s\n", port_info_table[index].name);
+// dump_data(
+// (unsigned char *)data.c_str(),
+// port_info_table[index].size,
+// port_info_table[index].columns,
+// port_info_table[index].rows,
+// port_info_table[index].width
+// );
+// }
+//
+// auto m = master_volume{};
+//
+// memcpy(&m.v, data.c_str(), sizeof(m.v));
+// // memcpy(&m.v, cxd3778gf_master_volume_table, sizeof(cxd3778gf_master_volume_table));
+//
+// return m;
+// }
+
+int checksum(const unsigned char *buf, int size, unsigned int *sum, unsigned int *xr) {
+ *sum = 0;
+ *xr = 0;
+ int n;
+
+ for (n = 0; n < size; n++) {
+ *sum = *sum + buf[n];
+ *xr = *xr ^ (buf[n] << (n % 4) * 8);
+ }
+
+ // printf("sum = 0x%08X, xor = 0x%08X\n", sum, xr);
+
+ if (*sum == 0) {
+ printf("all zero\n");
+ return (-1);
+ }
+
+ return 0;
+}
+
+static int check_data(const unsigned char *buf, int size, unsigned int *sum, unsigned int *xr) {
+ if (checksum(buf, size, sum, xr) != 0) {
+ printf("failed to calc checksum\n");
+ return -1;
+ }
+
+ if (*sum != *(unsigned int *)(buf + size)) {
+ printf("sum error %08X,%08X\n", *sum, *(unsigned int *)(buf + size));
+ return (-1);
+ }
+
+ if (*xr != *(unsigned int *)(buf + size + 4)) {
+ printf("xor error %08X,%08X\n", xr, *(unsigned int *)(buf + size + 4));
+ return (-1);
+ }
+
+ return 0;
+}
+
+static void dump_data(unsigned char *buf, int size, int columns, int rows, int width) {
+ unsigned char *uc;
+ unsigned int *ui;
+ int n;
+ int m;
+
+ printf("target = %08X, size = %d\n", buf, size);
+
+ if (width == 1) {
+
+ uc = buf;
+
+ for (n = 0; n < size; n = n + columns) {
+
+ printf("%02d:", (n / columns) % rows);
+
+ for (m = 0; m < columns; m++) {
+
+ if (n + m >= size)
+ break;
+
+ printf(" %02X", uc[n + m]);
+ }
+
+ printf("\n");
+ }
+ }
+
+ if (width == 4) {
+
+ ui = (unsigned int *)buf;
+ size = size / width;
+
+ for (n = 0; n < size; n = n + columns) {
+
+ printf("%02d:", (n / columns) % rows);
+
+ for (m = 0; m < columns; m++) {
+
+ if (n + m >= size)
+ break;
+
+ printf(" %08X", ui[n + m]);
+ }
+
+ printf("\n");
+ }
+ }
+}
+
+uint master_volume::GetValue(int tableIndex, int volumeTable, int volume, MASTER_VOLUME_VALUE valueType) {
+ unsigned char val;
+
+ switch (valueType) {
+ case MASTER_VOLUME_VALUE_LINEIN:
+ val = v[tableIndex][volumeTable][volume].linein;
+ break;
+ case MASTER_VOLUME_VALUE_SDIN1:
+ val = v[tableIndex][volumeTable][volume].sdin1;
+ break;
+ case MASTER_VOLUME_VALUE_SDIN2:
+ val = v[tableIndex][volumeTable][volume].sdin2;
+ break;
+ case MASTER_VOLUME_VALUE_PLAY:
+ val = v[tableIndex][volumeTable][volume].play;
+ break;
+ case MASTER_VOLUME_VALUE_LINEOUT:
+ val = v[tableIndex][volumeTable][volume].lineout;
+ break;
+ case MASTER_VOLUME_VALUE_HPOUT:
+ val = v[tableIndex][volumeTable][volume].hpout;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_500:
+ val = v[tableIndex][volumeTable][volume].cmx1_500;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_500:
+ val = v[tableIndex][volumeTable][volume].cmx2_500;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_750:
+ val = v[tableIndex][volumeTable][volume].cmx1_750;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_750:
+ val = v[tableIndex][volumeTable][volume].cmx2_750;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_31:
+ val = v[tableIndex][volumeTable][volume].cmx1_31;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_31:
+ val = v[tableIndex][volumeTable][volume].cmx2_31;
+ break;
+ case MASTER_VOLUME_VALUE_HPOUT2_CTRL3:
+ case MASTER_VOLUME_VALUE_MAX:
+ val = v[tableIndex][volumeTable][volume].hpout2_ctrl3;
+ break;
+ }
+
+ // if (val > 0) {
+ // printf("%d %d %d: %d\n", tableIndex, volumeTable, volume, val);
+ // }
+ return (uint)val;
+}
+
+void master_volume::SetValue(int tableIndex, int volumeTable, int volume, MASTER_VOLUME_VALUE valueType, uint value) {
+ if (value > 255) {
+ value = 0;
+ }
+
+ switch (valueType) {
+ case MASTER_VOLUME_VALUE_LINEIN:
+ v[tableIndex][volumeTable][volume].linein = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_SDIN1:
+ v[tableIndex][volumeTable][volume].sdin1 = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_SDIN2:
+ v[tableIndex][volumeTable][volume].sdin2 = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_PLAY:
+ v[tableIndex][volumeTable][volume].play = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_LINEOUT:
+ v[tableIndex][volumeTable][volume].lineout = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_HPOUT:
+ v[tableIndex][volumeTable][volume].hpout = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_500:
+ v[tableIndex][volumeTable][volume].cmx1_500 = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_500:
+ v[tableIndex][volumeTable][volume].cmx2_500 = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_750:
+ v[tableIndex][volumeTable][volume].cmx1_750 = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_750:
+ v[tableIndex][volumeTable][volume].cmx2_750 = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_31:
+ v[tableIndex][volumeTable][volume].cmx1_31 = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_31:
+ v[tableIndex][volumeTable][volume].cmx2_31 = (u_char)value;
+ break;
+ case MASTER_VOLUME_VALUE_HPOUT2_CTRL3:
+ case MASTER_VOLUME_VALUE_MAX:
+ v[tableIndex][volumeTable][volume].hpout2_ctrl3 = (u_char)value;
+ break;
+ }
+
+ // printf("set %d for %d %d %d: %d\n", tableIndex, volumeTable, volume, value);
+}
+
+int master_volume::ToFile(const std::string &path) {
+ char *c = (char *)malloc(path.length() + 1);
+ strcpy(c, path.c_str());
+ auto d = dirname(c);
+ mkpath1(d, 0755);
+
+ std::fstream f;
+ f.open(path, std::ios::binary | std::ios::out);
+
+ if (!f.good()) {
+ printf("bad file\n");
+ return -1;
+ }
+
+ f.write((const char *)v, sizeof(v));
+
+ checksum((const unsigned char *)v, sizeof(v), &sum, &xr);
+
+ f.write((const char *)&sum, sizeof(sum));
+ f.write((const char *)&xr, sizeof(xr));
+ f.close();
+
+ return 0;
+}
+
+int master_volume::Apply(const std::string &path) {
+ auto f = open(path.c_str(), O_RDWR);
+ if (f < 0) {
+ printf("cannot open %s: %d\n", path.c_str(), f);
+ return f;
+ }
+
+ if (write(f, (const char *)v, sizeof(v)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ checksum((const unsigned char *)v, sizeof(v), &sum, &xr);
+
+ if (write(f, (const char *)&sum, sizeof(sum)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ if (write(f, (const char *)&xr, sizeof(xr)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ close(f);
+
+ return 0;
+}
+
+void master_volume::Reset() {
+ memset(&v, 0, sizeof(v));
+ sum = 0;
+ xr = 0;
+}
+int master_volume::FromFile(const std::string &path) {
+ std::ifstream f;
+
+ f.open(path, std::ios::binary);
+
+ if (!f.good()) {
+ printf("bad file\n");
+ return -1;
+ }
+
+ std::string buf((std::istreambuf_iterator(f)), std::istreambuf_iterator());
+ f.close();
+
+ auto length = buf.length();
+ if (length != (TABLE_SIZE_OUTPUT_VOLUME + CHECKSUM_SIZE)) {
+ printf("invalid table size\n");
+ return -1;
+ }
+
+ memcpy(v, buf.c_str(), sizeof(v));
+
+ if (check_data((const unsigned char *)buf.c_str(), (int)buf.length() - CHECKSUM_SIZE, &sum, &xr) != 0) {
+ printf("bad checksum\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int master_volume_dsd::FromFile(const std::string &path) {
+ std::ifstream f;
+
+ f.open(path, std::ios::binary);
+
+ if (!f.good()) {
+ printf("bad file\n");
+ return -1;
+ }
+
+ std::string buf((std::istreambuf_iterator(f)), std::istreambuf_iterator());
+ f.close();
+
+ auto length = buf.length();
+ if (length != (TABLE_SIZE_OUTPUT_VOLUME_DSD + CHECKSUM_SIZE)) {
+ printf("invalid dsd table size\n");
+ return -1;
+ }
+
+ memcpy(v, buf.c_str(), sizeof(v));
+
+ if (check_data((const unsigned char *)buf.c_str(), (int)buf.length() - CHECKSUM_SIZE, &sum, &xr) != 0) {
+ printf("bad checksum\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int master_volume_dsd::ToFile(const std::string &path) {
+ char *c = (char *)malloc(path.length() + 1);
+ strcpy(c, path.c_str());
+ auto d = dirname(c);
+ mkpath1(d, 0755);
+
+ std::fstream f;
+
+ f.open(path, std::ios::binary | std::ios::out);
+
+ if (!f.good()) {
+ printf("bad file\n");
+ return -1;
+ }
+
+ f.write((const char *)v, sizeof(v));
+
+ checksum((const unsigned char *)v, sizeof(v), &sum, &xr);
+
+ f.write((const char *)&sum, sizeof(sum));
+ f.write((const char *)&xr, sizeof(xr));
+ f.close();
+
+ return 0;
+}
+
+int master_volume_dsd::Apply(const std::string &path) {
+ auto f = open(path.c_str(), O_RDWR);
+ if (f < 0) {
+ printf("cannot open %s: %d\n", path.c_str(), f);
+ return f;
+ }
+
+ if (write(f, (const char *)v, sizeof(v)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ checksum((const unsigned char *)v, sizeof(v), &sum, &xr);
+
+ if (write(f, (const char *)&sum, sizeof(sum)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ if (write(f, (const char *)&xr, sizeof(xr)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ close(f);
+
+ return 0;
+}
+void master_volume_dsd::Reset() {
+ memset(&v, 0, sizeof(v));
+ sum = 0;
+ xr = 0;
+}
+
+int tone_control::FromFile(const std::string &path) {
+ std::ifstream f;
+
+ f.open(path, std::ios::binary);
+
+ if (!f.good()) {
+ printf("bad file\n");
+ return -1;
+ }
+
+ std::string buf((std::istreambuf_iterator(f)), std::istreambuf_iterator());
+ f.close();
+
+ auto length = buf.length();
+ if (length != (TABLE_SIZE_TONE_CONTROL + CHECKSUM_SIZE)) {
+ printf("invalid tone control table size\n");
+ return -1;
+ }
+
+ memcpy(v, buf.c_str(), sizeof(v));
+
+ if (check_data((const unsigned char *)buf.c_str(), (int)buf.length() - CHECKSUM_SIZE, &sum, &xr) != 0) {
+ printf("bad checksum\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int tone_control::ToFile(const std::string &path) {
+ char *c = (char *)malloc(path.length() + 1);
+ strcpy(c, path.c_str());
+ auto d = dirname(c);
+ mkpath1(d, 0755);
+
+ std::fstream f;
+
+ f.open(path, std::ios::binary | std::ios::out);
+
+ if (!f.good()) {
+ printf("bad file\n");
+ return -1;
+ }
+
+ f.write((const char *)v, sizeof(v));
+
+ checksum((const unsigned char *)v, sizeof(v), &sum, &xr);
+
+ f.write((const char *)&sum, sizeof(sum));
+ f.write((const char *)&xr, sizeof(xr));
+ f.close();
+
+ return 0;
+}
+
+int tone_control::Apply(const std::string &path) {
+ auto f = open(path.c_str(), O_RDWR);
+ if (f < 0) {
+ printf("cannot open %s: %d\n", path.c_str(), f);
+ return f;
+ }
+
+ if (write(f, (const char *)v, sizeof(v)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ checksum((const unsigned char *)v, sizeof(v), &sum, &xr);
+
+ if (write(f, (const char *)&sum, sizeof(sum)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ if (write(f, (const char *)&xr, sizeof(xr)) < 0) {
+ printf("cannot write to %s\n", path.c_str());
+ return -1;
+ }
+
+ close(f);
+
+ return 0;
+}
+void tone_control::Reset() {
+ memset(&v, 0, sizeof(v));
+ sum = 0;
+ xr = 0;
+}
diff --git a/src/dac/cxd3778gf_table.h b/src/dac/cxd3778gf_table.h
new file mode 100644
index 0000000..c93443e
--- /dev/null
+++ b/src/dac/cxd3778gf_table.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2013,2014,2015,2016,2017 Sony Corporation
+ */
+/*
+ * cxd3778gf_table.h
+ *
+ * CXD3778GF CODEC driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CXD3778GF_TABLE_HEADER_
+#define _CXD3778GF_TABLE_HEADER_
+
+#include "cxd3778gf_common.h"
+#include
+#include
+
+enum MASTER_VOLUME_VALUE {
+ MASTER_VOLUME_VALUE_MIN = 0,
+ MASTER_VOLUME_VALUE_LINEIN = 0,
+ MASTER_VOLUME_VALUE_SDIN1 = 1,
+ MASTER_VOLUME_VALUE_SDIN2,
+ MASTER_VOLUME_VALUE_PLAY,
+ MASTER_VOLUME_VALUE_LINEOUT,
+ MASTER_VOLUME_VALUE_HPOUT,
+ MASTER_VOLUME_VALUE_CMX1_500,
+ MASTER_VOLUME_VALUE_CMX2_500,
+ MASTER_VOLUME_VALUE_CMX1_750,
+ MASTER_VOLUME_VALUE_CMX2_750,
+ MASTER_VOLUME_VALUE_CMX1_31,
+ MASTER_VOLUME_VALUE_CMX2_31,
+ MASTER_VOLUME_VALUE_HPOUT2_CTRL3,
+ MASTER_VOLUME_VALUE_MAX,
+};
+
+struct cxd3778gf_master_volume {
+ unsigned char linein;
+ unsigned char sdin1;
+ unsigned char sdin2;
+ unsigned char play;
+ unsigned char lineout;
+ unsigned char hpout;
+ unsigned char cmx1_500;
+ unsigned char cmx2_500;
+ unsigned char cmx1_750;
+ unsigned char cmx2_750;
+ unsigned char cmx1_31;
+ unsigned char cmx2_31;
+ unsigned char hpout2_ctrl3;
+};
+
+struct cxd3778gf_device_gain {
+ unsigned char pga;
+ unsigned char adc;
+};
+
+struct cxd3778gf_deq_coefficient {
+ unsigned char b0[3];
+ unsigned char b1[3];
+ unsigned char b2[3];
+ unsigned char a1[3];
+ unsigned char a2[3];
+};
+
+#define TABLE_ID_MASTER_VOLUME 0
+#define TABLE_ID_DEVICE_GAIN 1
+#define TABLE_ID_TONE_CONTROL 2
+
+#define TABLE_ID_MASTER_VOLUME_DSD 3 // :unknown
+
+#define MASTER_VOLUME_TABLE_OFF 0
+#define MASTER_VOLUME_TABLE_SMASTER_SE_LG 1 // low gain
+#define MASTER_VOLUME_TABLE_SMASTER_SE_HG 2 // high gain
+#define MASTER_VOLUME_TABLE_SMASTER_SE_NC 3 // noise cancel
+#define MASTER_VOLUME_TABLE_SMASTER_SE_AM 4 // ambient
+#define MASTER_VOLUME_TABLE_SMASTER_BTL_50 5
+#define MASTER_VOLUME_TABLE_SMASTER_BTL_100 6
+#define MASTER_VOLUME_TABLE_CLASSAB 7
+#define MASTER_VOLUME_TABLE_CLASSAB_NC 8
+#define MASTER_VOLUME_TABLE_CLASSAB_AM 9
+#define MASTER_VOLUME_TABLE_LINE 10
+#define MASTER_VOLUME_TABLE_FIXEDLINE 11
+#define MASTER_VOLUME_TABLE_SMASTER_SE_DSD64_LG 12
+#define MASTER_VOLUME_TABLE_SMASTER_SE_DSD64_HG 13
+#define MASTER_VOLUME_TABLE_SMASTER_SE_DSD128_LG 14
+#define MASTER_VOLUME_TABLE_SMASTER_SE_DSD128_HG 15
+#define MASTER_VOLUME_TABLE_SMASTER_SE_DSD256_LG 16
+#define MASTER_VOLUME_TABLE_SMASTER_SE_DSD256_HG 17
+#define MASTER_VOLUME_TABLE_SMASTER_BTL_50_DSD64 18
+#define MASTER_VOLUME_TABLE_SMASTER_BTL_100_DSD64 19
+#define MASTER_VOLUME_TABLE_SMASTER_BTL_50_DSD128 20
+#define MASTER_VOLUME_TABLE_SMASTER_BTL_100_DSD128 21
+#define MASTER_VOLUME_TABLE_SMASTER_BTL_50_DSD256 22
+#define MASTER_VOLUME_TABLE_SMASTER_BTL_100_DSD256 23
+#define MASTER_VOLUME_TABLE_LINE_DSD64 24
+#define MASTER_VOLUME_TABLE_LINE_DSD128 25
+#define MASTER_VOLUME_TABLE_LINE_DSD256 26
+#define MASTER_VOLUME_TABLE_MAX 26
+
+#define TONE_CONTROL_TABLE_NO_HP 0
+#define TONE_CONTROL_TABLE_NAMP_GENERAL_HP 1
+#define TONE_CONTROL_TABLE_NAMP_NW500N_NCHP 2
+#define TONE_CONTROL_TABLE_NAMP_NW750N_NCHP 3
+#define TONE_CONTROL_TABLE_NAMP_NC31_NCHP 4
+#define TONE_CONTROL_TABLE_SAMP_GENERAL_HP 5
+#define TONE_CONTROL_TABLE_SAMP_NW500N_NCHP 6
+#define TONE_CONTROL_TABLE_SAMP_NW750N_NCHP 7
+#define TONE_CONTROL_TABLE_SAMP_NC31_NCHP 8
+#define TONE_CONTROL_TABLE_MAX 8
+
+#define SOUND_EFFECT_ON 1
+#define SOUND_EFFECT_OFF 0
+
+extern struct cxd3778gf_master_volume cxd3778gf_master_volume_table[2][MASTER_VOLUME_TABLE_MAX + 1][MASTER_VOLUME_MAX + 1];
+extern unsigned char cxd3778gf_master_gain_table[MASTER_GAIN_MAX + 1];
+extern struct cxd3778gf_device_gain cxd3778gf_device_gain_table[INPUT_DEVICE_MAX + 1];
+extern unsigned char cxd3778gf_tone_control_table[(TONE_CONTROL_TABLE_MAX + 1)][CODEC_RAM_SIZE];
+extern unsigned int cxd3778gf_master_volume_dsd_table[MASTER_VOLUME_TABLE_MAX + 1][MASTER_VOLUME_MAX + 1];
+
+int checksum(const unsigned char *buf, int size, unsigned int *sum, unsigned int *xr);
+
+#define CHECKSUM_SIZE 8
+
+class TableLike {
+ public:
+ virtual int ToFile(const std::string &path) = 0;
+ virtual int Apply(const std::string &path) = 0;
+ virtual void Reset() = 0;
+};
+
+class master_volume : public TableLike {
+ public:
+ cxd3778gf_master_volume v[2][MASTER_VOLUME_TABLE_MAX + 1][MASTER_VOLUME_MAX + 1]{};
+ uint sum{};
+ uint xr{};
+
+ uint GetValue(int tableIndex, int volumeTable, int volume, MASTER_VOLUME_VALUE valueType);
+ void SetValue(int tableIndex, int volumeTable, int volume, MASTER_VOLUME_VALUE valueType, uint value);
+ int FromFile(const std::string &path);
+ int ToFile(const std::string &path) override;
+ int Apply(const std::string &path) override;
+ void Reset() override;
+};
+
+class master_volume_dsd : public TableLike {
+ public:
+ unsigned int v[MASTER_VOLUME_TABLE_MAX + 1][MASTER_VOLUME_MAX + 1]{};
+ uint sum{};
+ uint xr{};
+
+ int FromFile(const std::string &path);
+ int ToFile(const std::string &path) override;
+ int Apply(const std::string &path) override;
+ void Reset() override;
+};
+
+class tone_control : public TableLike {
+ public:
+ unsigned char v[TONE_CONTROL_TABLE_MAX + 1][CODEC_RAM_SIZE]{};
+ uint sum{};
+ uint xr{};
+
+ int FromFile(const std::string &path);
+ int ToFile(const std::string &path) override;
+ int Apply(const std::string &path) override;
+ void Reset() override;
+};
+
+#endif
diff --git a/src/dac/dac.cpp b/src/dac/dac.cpp
new file mode 100644
index 0000000..b0e3e71
--- /dev/null
+++ b/src/dac/dac.cpp
@@ -0,0 +1,193 @@
+#include "dac.h"
+#include "cxd3778gf_table.h"
+#include
+#include
+
+#define TABLE_SIZE_OUTPUT_VOLUME (sizeof(struct cxd3778gf_master_volume) * (MASTER_VOLUME_MAX + 1) * (MASTER_VOLUME_TABLE_MAX + 1) * 2)
+#define TABLE_SIZE_OUTPUT_VOLUME_DSD (sizeof(unsigned int) * (MASTER_VOLUME_TABLE_MAX + 1) * (MASTER_VOLUME_MAX + 1))
+#define TABLE_SIZE_DEVICE_GAIN (sizeof(struct cxd3778gf_device_gain) * (INPUT_DEVICE_MAX + 1))
+#define TABLE_SIZE_TONE_CONTROL (sizeof(unsigned char) * (TONE_CONTROL_TABLE_MAX + 1) * CODEC_RAM_SIZE)
+
+namespace Dac {
+
+#ifdef DESKTOP
+ std::string volumeTableOutPath = "/tmp/out_ovt";
+ std::string volumeTableDSDOutPath = "/tmp/out_ovt_dsd";
+ std::string toneControlOutPath = "/tmp/tct";
+#else
+ std::string volumeTableOutPath = "/proc/icx_audio_cxd3778gf_data/ovt";
+ std::string volumeTableDSDOutPath = "/proc/icx_audio_cxd3778gf_data/ovt_dsd";
+ std::string toneControlOutPath = "/proc/icx_audio_cxd3778gf_data/tct";
+#endif
+
+ std::map TableTypeToString = {
+ {TABLE_ID_MASTER_VOLUME, "Master volume"},
+ // {TABLE_ID_DEVICE_GAIN, "Device gain"}, // used for audio recording
+ {TABLE_ID_TONE_CONTROL, "Tone control"},
+ {TABLE_ID_MASTER_VOLUME_DSD, "Master volume (DSD)"},
+ };
+
+ std::map MasterVolumeTableTypeToString = {
+ {MASTER_VOLUME_TABLE_OFF, "No output device (OFF)"},
+ {MASTER_VOLUME_TABLE_SMASTER_SE_LG, "Headphones, low gain (SMASTER_SE_LG)"},
+ {MASTER_VOLUME_TABLE_SMASTER_SE_HG, "Headphones, high gain (SMASTER_SE_HG)"},
+ {MASTER_VOLUME_TABLE_SMASTER_SE_NC, "Headphones, noise cancel ON (SMASTER_SE_NC)"},
+ {MASTER_VOLUME_TABLE_SMASTER_SE_AM, "Headphones, ambient ON (SMASTER_SE_AM)"},
+ {MASTER_VOLUME_TABLE_SMASTER_BTL_50, "Headphones, bluetooth low gain (SMASTER_BTL_50)"},
+ {MASTER_VOLUME_TABLE_SMASTER_BTL_100, "Headphones, bluetooth high gain (SMASTER_BTL_100)"},
+ {MASTER_VOLUME_TABLE_CLASSAB, "Headphones, ?, (CLASSAB)"},
+ {MASTER_VOLUME_TABLE_CLASSAB_NC, "Headphones, ?, noise cancel ON (CLASSAB_NC)"},
+ {MASTER_VOLUME_TABLE_CLASSAB_AM, "Headphones, ?, ambient ON (CLASSAB_AM)"},
+ {MASTER_VOLUME_TABLE_LINE, "Line output (LINE)"},
+ {MASTER_VOLUME_TABLE_FIXEDLINE, "Fixed line output (FIXEDLINE)"},
+ {MASTER_VOLUME_TABLE_SMASTER_SE_DSD64_LG, "DSD64 low gain (SMASTER_SE_DSD64_LG)"}, // sample rate 88200
+ {MASTER_VOLUME_TABLE_SMASTER_SE_DSD64_HG, "DSD64 high gain (SMASTER_SE_DSD64_HG)"}, // sample rate 176400
+ {MASTER_VOLUME_TABLE_SMASTER_SE_DSD128_LG, "DSD128 low gain (SMASTER_SE_DSD128_LG)"},
+ {MASTER_VOLUME_TABLE_SMASTER_SE_DSD128_HG, "DSD128 high gain (SMASTER_SE_DSD128_HG)"},
+ {MASTER_VOLUME_TABLE_SMASTER_SE_DSD256_LG, "DSD256 low gain (SMASTER_SE_DSD256_LG)"},
+ {MASTER_VOLUME_TABLE_SMASTER_SE_DSD256_HG, "DSD256 high gain (SMASTER_SE_DSD256_HG)"},
+ {MASTER_VOLUME_TABLE_SMASTER_BTL_50_DSD64, "DSD64, bluetooth, low gain (SMASTER_BTL_50_DSD64)"},
+ {MASTER_VOLUME_TABLE_SMASTER_BTL_100_DSD64, "DSD64, bluetooth, high gain (SMASTER_BTL_100_DSD64)"},
+ {MASTER_VOLUME_TABLE_SMASTER_BTL_50_DSD128, "DSD128, bluetooth, low gain (SMASTER_BTL_50_DSD128)"},
+ {MASTER_VOLUME_TABLE_SMASTER_BTL_100_DSD128, "DSD128, bluetooth, high gain (SMASTER_BTL_100_DSD128)"},
+ {MASTER_VOLUME_TABLE_SMASTER_BTL_50_DSD256, "DSD256, bluetooth, low gain (SMASTER_BTL_50_DSD256)"},
+ {MASTER_VOLUME_TABLE_SMASTER_BTL_100_DSD256, "DSD256, bluetooth, high gain (SMASTER_BTL_100_DSD256)"},
+ {MASTER_VOLUME_TABLE_LINE_DSD64, "DSD64, line output (LINE_DSD64)"},
+ {MASTER_VOLUME_TABLE_LINE_DSD128, "DSD128, line output (LINE_DSD128)"},
+ {MASTER_VOLUME_TABLE_LINE_DSD256, "DSD256, line output (LINE_DSD256)"},
+ };
+
+ std::map ToneControlTableTypeToString = {
+ {TONE_CONTROL_TABLE_NO_HP, "NO_HP"},
+ {TONE_CONTROL_TABLE_NAMP_GENERAL_HP, "NAMP_GENERAL_HP"},
+ {TONE_CONTROL_TABLE_NAMP_NW500N_NCHP, "NAMP_NW500N_NCHP"},
+ {TONE_CONTROL_TABLE_NAMP_NW750N_NCHP, "NAMP_NW750N_NCHP"},
+ {TONE_CONTROL_TABLE_NAMP_NC31_NCHP, "NAMP_NC31_NCHP"},
+ {TONE_CONTROL_TABLE_SAMP_GENERAL_HP, "SAMP_GENERAL_HP"},
+ {TONE_CONTROL_TABLE_SAMP_NW500N_NCHP, "SAMP_NW500N_NCHP"},
+ {TONE_CONTROL_TABLE_SAMP_NW750N_NCHP, "SAMP_NW750N_NCHP"},
+ {TONE_CONTROL_TABLE_SAMP_NC31_NCHP, "SAMP_NC31_NCHP"},
+ };
+
+ std::map MasterVolumeValueTypeToString = {
+ {MASTER_VOLUME_VALUE_HPOUT, "Headphones (HpOut)"},
+ {MASTER_VOLUME_VALUE_PLAY, "Play"},
+ {MASTER_VOLUME_VALUE_LINEIN, "LineIn"},
+ {MASTER_VOLUME_VALUE_SDIN1, "SdIn1"},
+ {MASTER_VOLUME_VALUE_SDIN2, "SdIn2"},
+ {MASTER_VOLUME_VALUE_LINEOUT, "LineOut"},
+ {MASTER_VOLUME_VALUE_CMX1_500, "CMX1_500"},
+ {MASTER_VOLUME_VALUE_CMX2_500, "CMX2_500"},
+ {MASTER_VOLUME_VALUE_CMX1_750, "CMX1_750"},
+ {MASTER_VOLUME_VALUE_CMX2_750, "CMX2_750"},
+ {MASTER_VOLUME_VALUE_CMX1_31, "CMX1_31"},
+ {MASTER_VOLUME_VALUE_CMX2_31, "CMX2_31"},
+ {MASTER_VOLUME_VALUE_HPOUT2_CTRL3, "HpOut2_CTRL3"},
+ };
+
+ void printTableValue(master_volume *t, MASTER_VOLUME_VALUE valType, const std::string &outfile) {
+ std::fstream f;
+ if (!outfile.empty()) {
+ f.open(outfile, std::ios::trunc | std::ios::out);
+ f << "valueid " << MasterVolumeValueTypeToString.at(valType) << "\n";
+ }
+
+ int soundEffect = SOUND_EFFECT_ON;
+ // maxGain = 2;
+ for (int tableID = 0; tableID <= MASTER_VOLUME_TABLE_MAX; tableID++) {
+ int counter = 0;
+ for (int i = MASTER_VOLUME_MIN; i < MASTER_VOLUME_MAX + 1; i++) {
+ unsigned char val;
+
+ switch (valType) {
+ case MASTER_VOLUME_VALUE_LINEIN:
+ val = t->v[soundEffect][tableID][i].linein;
+ break;
+ case MASTER_VOLUME_VALUE_SDIN1:
+ val = t->v[soundEffect][tableID][i].sdin1;
+ break;
+ case MASTER_VOLUME_VALUE_SDIN2:
+ val = t->v[soundEffect][tableID][i].sdin2;
+ break;
+ case MASTER_VOLUME_VALUE_PLAY:
+ val = t->v[soundEffect][tableID][i].play;
+ break;
+ case MASTER_VOLUME_VALUE_LINEOUT:
+ val = t->v[soundEffect][tableID][i].lineout;
+ break;
+ case MASTER_VOLUME_VALUE_HPOUT:
+ val = t->v[soundEffect][tableID][i].hpout;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_500:
+ val = t->v[soundEffect][tableID][i].cmx1_500;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_500:
+ val = t->v[soundEffect][tableID][i].cmx2_500;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_750:
+ val = t->v[soundEffect][tableID][i].cmx1_750;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_750:
+ val = t->v[soundEffect][tableID][i].cmx2_750;
+ break;
+ case MASTER_VOLUME_VALUE_CMX1_31:
+ val = t->v[soundEffect][tableID][i].cmx1_31;
+ break;
+ case MASTER_VOLUME_VALUE_CMX2_31:
+ val = t->v[soundEffect][tableID][i].cmx2_31;
+ break;
+ case MASTER_VOLUME_VALUE_MAX:
+ case MASTER_VOLUME_VALUE_HPOUT2_CTRL3:
+ val = t->v[soundEffect][tableID][i].hpout2_ctrl3;
+ break;
+ }
+
+ counter++;
+
+ // if (val != 0) {
+ printf("vol: %03d | table: %02d | val: %02d\n", i, tableID, val);
+ // printf("vol: %03d | gain: %02d | table: %02d | val: %02d\n", i, gainID, tableID, val);
+ if (!outfile.empty()) {
+ f << tableID << " " << (int)val << "\n";
+ // f << "foo[" << counter << "]=ImVec2(" << counter << "," << (int)val << ");\n";
+ }
+ // }
+ }
+ }
+
+ if (!outfile.empty()) {
+ f.close();
+ }
+ }
+
+ std::string getStatus(std::vector *volumeTableFiles, const std::string &outPath) {
+ std::fstream f(outPath, std::ios::in | std::ios::binary);
+ std::string currentVT((std::istreambuf_iterator(f)), std::istreambuf_iterator());
+ f.close();
+
+ auto vth = std::hash{}(currentVT);
+ std::string res = "?";
+ DLOG("current is %d len, hash %d\n", currentVT.length(), vth);
+
+ for (const auto &entry : *volumeTableFiles) {
+ f.open(entry.fullPath, std::ios::in | std::ios::binary);
+ std::string c((std::istreambuf_iterator(f)), std::istreambuf_iterator());
+ f.close();
+
+#ifndef DESKTOP
+ // device driver does not return checksum
+ if (c.length() > 0) {
+ c.erase(c.length() - CHECKSUM_SIZE, CHECKSUM_SIZE);
+ }
+#endif
+ auto h = std::hash{}(c);
+ // DLOG("checking %s, hash %d\n", entry.fullPath.c_str(), h);
+ if (h == vth) {
+ res = entry.name;
+ break;
+ }
+ }
+
+ return res;
+ }
+} // namespace Dac
diff --git a/src/dac/dac.h b/src/dac/dac.h
new file mode 100644
index 0000000..25af2e1
--- /dev/null
+++ b/src/dac/dac.h
@@ -0,0 +1,24 @@
+#ifndef WAMPY_DAC_H
+#define WAMPY_DAC_H
+
+#include "../util/util.h"
+#include "cxd3778gf_table.h"
+#include "map"
+#include "string"
+#include "vector"
+
+namespace Dac {
+ extern std::map TableTypeToString;
+ extern std::map MasterVolumeTableTypeToString;
+ extern std::map MasterVolumeValueTypeToString;
+ extern std::map ToneControlTableTypeToString;
+ extern std::string volumeTableOutPath;
+ extern std::string volumeTableDSDOutPath;
+ extern std::string toneControlOutPath;
+
+ void printTableValue(master_volume *t, MASTER_VOLUME_VALUE valType, const std::string &outfile);
+
+ std::string getStatus(std::vector *volumeTableFiles, const std::string &outPath);
+} // namespace Dac
+
+#endif // WAMPY_DAC_H
diff --git a/src/dac/main.cpp b/src/dac/main.cpp
new file mode 100644
index 0000000..d2175b2
--- /dev/null
+++ b/src/dac/main.cpp
@@ -0,0 +1,9 @@
+#include "dac.h"
+
+int main(int, char **) {
+ auto m = master_volume{};
+ m.FromFile("/tmp/dfdfdf/ov_1291_cew.tbl");
+ for (int i = MASTER_VOLUME_VALUE_MIN; i < MASTER_VOLUME_VALUE_MAX; i++) {
+ Dac::printTableValue(&m, (MASTER_VOLUME_VALUE)i, "/tmp/compare_cew/data_" + Dac::MasterVolumeValueTypeToString.at(i));
+ }
+}
diff --git a/src/dac/plot.py b/src/dac/plot.py
new file mode 100644
index 0000000..456d5a8
--- /dev/null
+++ b/src/dac/plot.py
@@ -0,0 +1,43 @@
+import plotly.graph_objects as go
+from plotly.subplots import make_subplots
+
+
+def main():
+ tables = {}
+ title = ""
+ with open("/tmp/data", "r") as f:
+ lines = f.readlines()
+ title = lines[0].split(" ")[1].rstrip("\n")
+ for line in lines[1:]:
+ parts = line.split(" ")
+ if str(parts[0]) not in tables:
+ tables[str(parts[0])] = []
+
+ tables[parts[0]].append(int(parts[1].rstrip("\n")))
+
+ cols = 0
+ for k, v in tables.items():
+ if sum(v) > 0:
+ cols += 1
+
+ fig = make_subplots(rows=1, cols=cols, shared_yaxes=True)
+
+ row = 1
+ col = 1
+ for k, v in tables.items():
+ if sum(v) == 0:
+ continue
+
+ x = list(range(0, len(v)))
+ print(len(x), len(v))
+ fig.add_trace(go.Scatter(x=x, y=v, name="table {}".format(k)), row=row, col=col)
+ col = col + 1
+
+ # fig.update_layout(autosize=True, height=20000)
+ fig.update_xaxes(rangeslider=dict(visible=False))
+ fig.update_layout(title_text=title)
+
+ fig.show()
+
+
+main()
diff --git a/src/digital_clock/digital_clock.cpp b/src/digital_clock/digital_clock.cpp
index b6db73b..846c684 100644
--- a/src/digital_clock/digital_clock.cpp
+++ b/src/digital_clock/digital_clock.cpp
@@ -348,6 +348,9 @@ namespace DigitalClock {
range.AddRanges(io.Fonts->GetGlyphRangesDefault());
range.AddRanges(rangesPunctuation);
+ range.AddChar(ImWchar(0x266a));
+ range.AddChar(ImWchar(0x266b));
+ range.AddChar(ImWchar(0x24c8));
range.BuildRanges(&gr);
*fontRegular = io.Fonts->AddFontFromFileTTF(FontPath.c_str(), fontSizeTTF, nullptr, gr.Data);
diff --git a/src/imgui_curve.cpp b/src/imgui_curve.cpp
new file mode 100644
index 0000000..d59fb70
--- /dev/null
+++ b/src/imgui_curve.cpp
@@ -0,0 +1,444 @@
+#include "imgui_curve.h"
+
+#include
+// https://github.com/nem0/LumixEngine/blob/f07b128b10b7edddb61220549d231e9ad9ea7840/external/imgui/imgui_user.inl#L618
+namespace ImGui {
+ static constexpr float HANDLE_RADIUS = 4;
+ int CurveEditor(
+ const char *label, float *values, int points_count, int capacity, const ImVec2 &editor_size, ImU32 flags, int *new_count,
+ int *selected_point, int *hovered_point, const int *zoomDirection, int nodeSize, bool editable, float Ylimit, const int *volume
+ ) {
+ enum class StorageValues : ImGuiID { FROM_X = 100, FROM_Y, WIDTH, HEIGHT, IS_PANNING, POINT_START_X, POINT_START_Y };
+
+ const float HEIGHT = 100;
+ static ImVec2 start_pan;
+
+ ImGuiContext &g = *GImGui;
+ const ImGuiStyle &style = g.Style;
+ ImVec2 size = editor_size;
+ size.x = size.x < 0 ? CalcItemWidth() + (style.FramePadding.x * 2) : size.x;
+ size.y = size.y < 0 ? HEIGHT : size.y;
+ if (hovered_point)
+ *hovered_point = -1;
+
+ ImGuiWindow *parent_window = GetCurrentWindow();
+ ImGuiID id = parent_window->GetID(label);
+ if (new_count)
+ *new_count = points_count;
+ if (!BeginChildFrame(id, size, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
+ EndChildFrame();
+ return -1;
+ }
+
+ int hovered_idx = -1;
+
+ ImGuiWindow *window = GetCurrentWindow();
+ if (window->SkipItems) {
+ EndChildFrame();
+ return -1;
+ }
+
+ ImVec2 points_min(FLT_MAX, FLT_MAX);
+ ImVec2 points_max(-FLT_MAX, -FLT_MAX);
+ for (int point_idx = 0; point_idx < points_count; ++point_idx) {
+ ImVec2 point;
+ if (flags & (int)CurveEditorFlags::NO_TANGENTS) {
+ point = ((ImVec2 *)values)[point_idx];
+ } else {
+ point = ((ImVec2 *)values)[1 + point_idx * 3];
+ }
+ points_max = ImMax(points_max, point);
+ points_min = ImMin(points_min, point);
+ }
+ points_max.y = ImMax(points_max.y, points_min.y + 0.0001f);
+
+ ImVec2 points_range = points_max - points_min;
+ points_min -= points_range * 0.05f;
+ points_max += points_range * 0.05f;
+
+ if (flags & (int)CurveEditorFlags::RESET)
+ window->StateStorage.Clear();
+
+ float from_x = window->StateStorage.GetFloat((ImGuiID)StorageValues::FROM_X, points_min.x);
+ float from_y = window->StateStorage.GetFloat((ImGuiID)StorageValues::FROM_Y, points_min.y);
+ float width = window->StateStorage.GetFloat((ImGuiID)StorageValues::WIDTH, points_max.x - points_min.x);
+ float height = window->StateStorage.GetFloat((ImGuiID)StorageValues::HEIGHT, points_max.y - points_min.y);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::FROM_X, from_x);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::FROM_Y, from_y);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::WIDTH, width);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::HEIGHT, height);
+
+ const ImRect inner_bb = window->InnerClipRect;
+ if (inner_bb.GetWidth() == 0 || inner_bb.GetHeight() == 0) {
+ EndChildFrame();
+ return -1;
+ }
+ const ImRect frame_bb(inner_bb.Min - style.FramePadding, inner_bb.Max + style.FramePadding);
+
+ auto transform = [&](const ImVec2 &pos) -> ImVec2 {
+ float x = (pos.x - from_x) / width;
+ float y = (pos.y - from_y) / height;
+
+ return ImVec2(inner_bb.Min.x * (1 - x) + inner_bb.Max.x * x, inner_bb.Min.y * y + inner_bb.Max.y * (1 - y));
+ };
+
+ auto invTransform = [&](const ImVec2 &pos) -> ImVec2 {
+ float x = (pos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x);
+ float y = (inner_bb.Max.y - pos.y) / (inner_bb.Max.y - inner_bb.Min.y);
+
+ return ImVec2(from_x + width * x, from_y + height * y);
+ };
+
+ const ImU32 color_text = GetColorU32(ImGuiCol_TextDisabled);
+ if (flags & (int)CurveEditorFlags::SHOW_GRID) {
+ int exp;
+ std::frexp(width / 5, &exp);
+ auto step_x = (float)std::ldexp(1.0, exp);
+ int cell_cols = int(width / step_x);
+
+ float x = step_x * int(from_x / step_x);
+ for (int i = -1; i < cell_cols + 2; ++i) {
+ ImVec2 a = transform({x + i * step_x, from_y});
+ ImVec2 b = transform({x + i * step_x, from_y + height});
+ // unknown: on 32bit arm width is zero, x is overflowing
+ if (a.x > editor_size.x) {
+ break;
+ }
+ window->DrawList->AddLine(a, b, 0x55000000);
+ char buf[64];
+ if (exp > 0) {
+ ImFormatString(buf, sizeof(buf), " %d", int(x + i * step_x));
+ } else {
+ ImFormatString(buf, sizeof(buf), " %.2f", x + i * step_x);
+ }
+ window->DrawList->AddText(b, color_text, buf);
+ }
+
+ frexp(height / 5, &exp);
+ float step_y = (float)ldexp(1.0, exp);
+ int cell_rows = int(height / step_y);
+
+ float y = step_y * int(from_y / step_y);
+ for (int i = -1; i < cell_rows + 2; ++i) {
+ ImVec2 a = transform({from_x, y + i * step_y});
+ ImVec2 b = transform({from_x + width, y + i * step_y});
+ window->DrawList->AddLine(a, b, 0x55000000);
+ char buf[64];
+ if (exp > 0) {
+ ImFormatString(buf, sizeof(buf), " %d", int(y + i * step_y));
+ } else {
+ ImFormatString(buf, sizeof(buf), " %.2f", y + i * step_y);
+ }
+ window->DrawList->AddText(a, color_text, buf);
+ }
+ }
+
+ const ImGuiID dragger_id = GetID("##_node_dragger");
+ ImGui::ItemAdd(inner_bb, dragger_id);
+
+ if (*zoomDirection == 1) {
+ float scale = powf(2, -1);
+ width *= scale;
+ height *= scale;
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::WIDTH, width);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::HEIGHT, height);
+ }
+
+ if (*zoomDirection == -1) {
+ float scale = powf(2, 1);
+ width *= scale;
+ height *= scale;
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::WIDTH, width);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::HEIGHT, height);
+ }
+
+ // reset
+ if (*zoomDirection == 2) {
+ width = points_max.x - points_min.x;
+ height = points_max.y - points_min.y;
+
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::WIDTH, width);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::HEIGHT, height);
+ }
+
+ if (IsMouseReleased(2)) {
+ window->StateStorage.SetBool((ImGuiID)StorageValues::IS_PANNING, false);
+ }
+ if (window->StateStorage.GetBool((ImGuiID)StorageValues::IS_PANNING, false)) {
+ ImVec2 drag_offset = GetMouseDragDelta(2);
+ from_x = start_pan.x;
+ from_y = start_pan.y;
+ from_x -= drag_offset.x * width / (inner_bb.Max.x - inner_bb.Min.x);
+ from_y += drag_offset.y * height / (inner_bb.Max.y - inner_bb.Min.y);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::FROM_X, from_x);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::FROM_Y, from_y);
+ } else if (IsMouseDragging(2) && IsItemHovered()) {
+ window->StateStorage.SetBool((ImGuiID)StorageValues::IS_PANNING, true);
+ start_pan.x = from_x;
+ start_pan.y = from_y;
+ }
+
+ int changed_idx = -1;
+ for (int point_idx = points_count - 2; point_idx >= 0; --point_idx) {
+ ImVec2 *points;
+ if (flags & (int)CurveEditorFlags::NO_TANGENTS) {
+ points = ((ImVec2 *)values) + point_idx;
+ } else {
+ points = ((ImVec2 *)values) + 1 + point_idx * 3;
+ }
+
+ ImVec2 p_prev = points[0];
+ ImVec2 tangent_last;
+ ImVec2 tangent;
+ ImVec2 p;
+ if (flags & (int)CurveEditorFlags::NO_TANGENTS) {
+ p = points[1];
+ } else {
+ tangent_last = points[1];
+ tangent = points[2];
+ p = points[3];
+ }
+
+ auto handlePoint = [&](ImVec2 &p, int idx) -> bool {
+ static const float SIZE = nodeSize;
+
+ ImVec2 cursor_pos = GetCursorScreenPos();
+ ImVec2 pos = transform(p);
+
+ SetCursorScreenPos(pos - ImVec2(SIZE, SIZE));
+ PushID(idx);
+ InvisibleButton("", ImVec2(10 * HANDLE_RADIUS, 10 * HANDLE_RADIUS));
+
+ bool is_selected = selected_point && *selected_point == point_idx + idx;
+ float thickness = is_selected ? 2.0f : 1.0f;
+ ImU32 col = IsItemActive() || IsItemHovered() ? GetColorU32(ImGuiCol_PlotLinesHovered) : GetColorU32(ImGuiCol_PlotLines);
+
+ window->DrawList->AddLine(pos + ImVec2(-SIZE, 0), pos + ImVec2(0, SIZE), col, thickness);
+ window->DrawList->AddLine(pos + ImVec2(SIZE, 0), pos + ImVec2(0, SIZE), col, thickness);
+ window->DrawList->AddLine(pos + ImVec2(SIZE, 0), pos + ImVec2(0, -SIZE), col, thickness);
+ window->DrawList->AddLine(pos + ImVec2(-SIZE, 0), pos + ImVec2(0, -SIZE), col, thickness);
+
+ if (IsItemHovered())
+ hovered_idx = point_idx + idx;
+
+ bool changed = false;
+ if (IsItemActive() && IsMouseClicked(0)) {
+ if (selected_point)
+ *selected_point = point_idx + idx;
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::POINT_START_X, pos.x);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::POINT_START_Y, pos.y);
+ }
+
+ if (IsItemHovered() || (IsItemActive() && IsMouseDragging(0))) {
+ char tmp[64];
+ ImFormatString(tmp, sizeof(tmp), "%d, %d", int(p.x), int(p.y));
+ window->DrawList->AddText({pos.x, pos.y - GetTextLineHeight()}, color_text, tmp);
+ }
+
+ if (IsItemActive() && IsMouseDragging(0)) {
+ // stick to Y, keep X
+ pos.x = window->StateStorage.GetFloat((ImGuiID)StorageValues::POINT_START_X, pos.x);
+ pos.y = window->StateStorage.GetFloat((ImGuiID)StorageValues::POINT_START_Y, pos.y);
+ pos.y += round(GetMouseDragDelta().y);
+ ImVec2 v = invTransform(pos);
+
+ p.y = round(v.y);
+ if (p.y < 0) {
+ p.y = 0;
+ }
+ if (p.y > Ylimit) {
+ p.y = Ylimit;
+ }
+ changed = true;
+ }
+ PopID();
+
+ SetCursorScreenPos(cursor_pos);
+ return changed;
+ };
+
+ auto handleTangent = [&](ImVec2 &t, const ImVec2 &p, int idx) -> bool {
+ static const float SIZE = 2;
+ static const float LENGTH = 18;
+
+ auto normalized = [](const ImVec2 &v) -> ImVec2 {
+ float len = 1.0f / sqrtf(v.x * v.x + v.y * v.y);
+ return ImVec2(v.x * len, v.y * len);
+ };
+
+ ImVec2 cursor_pos = GetCursorScreenPos();
+ ImVec2 pos = transform(p);
+ ImVec2 tang = pos + normalized(ImVec2(t.x, -t.y)) * LENGTH;
+
+ SetCursorScreenPos(tang - ImVec2(SIZE, SIZE));
+ PushID(-idx);
+ InvisibleButton("", ImVec2(2 * HANDLE_RADIUS, 2 * HANDLE_RADIUS));
+
+ window->DrawList->AddLine(pos, tang, GetColorU32(ImGuiCol_PlotLines));
+
+ ImU32 col = IsItemHovered() ? GetColorU32(ImGuiCol_PlotLinesHovered) : GetColorU32(ImGuiCol_PlotLines);
+
+ window->DrawList->AddLine(tang + ImVec2(-SIZE, SIZE), tang + ImVec2(SIZE, SIZE), col);
+ window->DrawList->AddLine(tang + ImVec2(SIZE, SIZE), tang + ImVec2(SIZE, -SIZE), col);
+ window->DrawList->AddLine(tang + ImVec2(SIZE, -SIZE), tang + ImVec2(-SIZE, -SIZE), col);
+ window->DrawList->AddLine(tang + ImVec2(-SIZE, -SIZE), tang + ImVec2(-SIZE, SIZE), col);
+
+ bool changed = false;
+ if (IsItemActive() && IsMouseDragging(0)) {
+ tang = GetIO().MousePos - pos;
+ tang = normalized(tang);
+ tang.y *= -1;
+
+ t = tang;
+ changed = true;
+ }
+ PopID();
+
+ SetCursorScreenPos(cursor_pos);
+ return changed;
+ };
+
+ PushID(point_idx);
+
+ if ((flags & (int)CurveEditorFlags::NO_TANGENTS) == 0) {
+ window->DrawList->AddBezierCubic(
+ transform(p_prev),
+ transform(p_prev + tangent_last),
+ transform(p + tangent),
+ transform(p),
+ GetColorU32(ImGuiCol_PlotLines),
+ 1.0f,
+ 20
+ );
+
+ if (handleTangent(tangent_last, p_prev, 0)) {
+ points[1] = ImClamp(tangent_last, ImVec2(0, -1), ImVec2(1, 1));
+ changed_idx = point_idx;
+ }
+ if (handleTangent(tangent, p, 1)) {
+ points[2] = ImClamp(tangent, ImVec2(-1, -1), ImVec2(0, 1));
+ changed_idx = point_idx + 1;
+ }
+ if (editable) {
+ if (handlePoint(p, 1)) {
+ if (p.x <= p_prev.x)
+ p.x = p_prev.x + 0.001f;
+ if (point_idx < points_count - 2 && p.x >= points[6].x) {
+ p.x = points[6].x - 0.001f;
+ }
+ points[3] = p;
+ changed_idx = point_idx + 1;
+ }
+ }
+
+ } else {
+ window->DrawList->AddLine(transform(p_prev), transform(p), GetColorU32(ImGuiCol_PlotLines), 1.0f);
+ if (volume != nullptr) {
+ if (point_idx == *volume) {
+ auto np = transform(p);
+ window->DrawList->AddLine(ImVec2(np.x, 0), ImVec2(np.x, 480), ImColor(255, 0, 0, 255));
+ }
+ }
+ if (editable) {
+ if (handlePoint(p, 1)) {
+ if (p.x <= p_prev.x)
+ p.x = p_prev.x + 0.001f;
+ if (point_idx < points_count - 2 && p.x >= points[2].x) {
+ p.x = points[2].x - 0.001f;
+ }
+ points[1] = p;
+ changed_idx = point_idx + 1;
+ }
+ }
+ }
+ if (point_idx == 0) {
+ if (editable) {
+ if (handlePoint(p_prev, 0)) {
+ if (p.x <= p_prev.x)
+ p_prev.x = p.x - 0.001f;
+ points[0] = p_prev;
+ changed_idx = point_idx;
+ }
+ }
+ }
+ PopID();
+ }
+
+ SetCursorScreenPos(inner_bb.Min);
+
+ InvisibleButton("bg", inner_bb.Max - inner_bb.Min);
+
+ if (editable) {
+ // dragging by background
+ if (IsItemActive() && IsMouseDragging(0)) {
+ ImVec2 drag_offset = GetMouseDragDelta(0);
+ from_x = start_pan.x;
+ from_y = start_pan.y;
+ from_x -= drag_offset.x * editor_size.x * 0.1 / (inner_bb.Max.x - inner_bb.Min.x);
+ from_y += drag_offset.y * editor_size.y * 0.1 / (inner_bb.Max.y - inner_bb.Min.y);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::FROM_X, from_x);
+ window->StateStorage.SetFloat((ImGuiID)StorageValues::FROM_Y, from_y);
+ } else {
+ start_pan.x = from_x;
+ start_pan.y = from_y;
+ }
+ }
+
+ if (IsItemActive() && IsMouseDoubleClicked(0) && new_count && points_count < capacity) {
+ ImVec2 mp = GetMousePos();
+ ImVec2 new_p = invTransform(mp);
+ ImVec2 *points = (ImVec2 *)values;
+
+ if ((flags & (int)CurveEditorFlags::NO_TANGENTS) == 0) {
+ points[points_count * 3 + 0] = ImVec2(-0.2f, 0);
+ points[points_count * 3 + 1] = new_p;
+ points[points_count * 3 + 2] = ImVec2(0.2f, 0);
+ ;
+ ++*new_count;
+
+ auto compare = [](const void *a, const void *b) -> int {
+ float fa = (((const ImVec2 *)a) + 1)->x;
+ float fb = (((const ImVec2 *)b) + 1)->x;
+ return fa < fb ? -1 : (fa > fb) ? 1 : 0;
+ };
+
+ qsort(values, points_count + 1, sizeof(ImVec2) * 3, compare);
+
+ } else {
+ points[points_count] = new_p;
+ ++*new_count;
+
+ auto compare = [](const void *a, const void *b) -> int {
+ float fa = ((const ImVec2 *)a)->x;
+ float fb = ((const ImVec2 *)b)->x;
+ return fa < fb ? -1 : (fa > fb) ? 1 : 0;
+ };
+
+ qsort(values, points_count + 1, sizeof(ImVec2), compare);
+ }
+ }
+
+ if (hovered_idx >= 0 && IsMouseDoubleClicked(0) && new_count && points_count > 2) {
+ ImVec2 *points = (ImVec2 *)values;
+ --*new_count;
+ if ((flags & (int)CurveEditorFlags::NO_TANGENTS) == 0) {
+ for (int j = hovered_idx * 3; j < points_count * 3 - 3; j += 3) {
+ points[j + 0] = points[j + 3];
+ points[j + 1] = points[j + 4];
+ points[j + 2] = points[j + 5];
+ }
+ } else {
+ for (int j = hovered_idx; j < points_count - 1; ++j) {
+ points[j] = points[j + 1];
+ }
+ }
+ }
+
+ if (hovered_point)
+ *hovered_point = hovered_idx;
+
+ EndChildFrame();
+ RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
+ return changed_idx;
+ }
+} // namespace ImGui
\ No newline at end of file
diff --git a/src/imgui_curve.h b/src/imgui_curve.h
new file mode 100644
index 0000000..6802863
--- /dev/null
+++ b/src/imgui_curve.h
@@ -0,0 +1,16 @@
+#ifndef WAMPY_IMGUI_CURVE_H
+#define WAMPY_IMGUI_CURVE_H
+
+#include "imgui_internal.h"
+namespace ImGui {
+
+ enum class CurveEditorFlags { NO_TANGENTS = 1 << 0, SHOW_GRID = 1 << 1, RESET = 1 << 2 };
+
+ int CurveEditor(
+ const char *label, float *values, int points_count, int capacity, const ImVec2 &editor_size, ImU32 flags, int *new_count,
+ int *selected_point, int *hovered_point, const int *zoomDirection, int nodeSize = 10, bool editable = true, float Ylimit = 255.0f,
+ const int *volume = nullptr
+ );
+}; // namespace ImGui
+
+#endif
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index d132089..18a5661 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -243,6 +243,9 @@ void setupProfiling() {
SkinList skinList{};
SkinList reelList{};
SkinList tapeList{};
+std::vector masterVolumeTableDSDFiles{};
+std::vector masterVolumeTableFiles{};
+std::vector toneControlFiles{};
int main(int, char **) {
DLOG("starting, commit %s\n", SOFTWARE_VERSION);
@@ -291,6 +294,13 @@ int main(int, char **) {
listdir("../skins/", &skinList, ".wsz");
listdirs("../cassette/cassetteunpacker/res/reel/", &reelList);
listdirs("../cassette/cassetteunpacker/res/tape/", &tapeList);
+ listdir("../ss/system/master_volume_dsd/", &masterVolumeTableDSDFiles, ".tbl");
+ listdir("../ss/system/master_volume/", &masterVolumeTableFiles, ".tbl");
+ listdir("../ss/system/tone_control/", &toneControlFiles, ".tbl");
+
+ listdir("../ss/user/master_volume_dsd/", &masterVolumeTableDSDFiles, ".tbl");
+ listdir("../ss/user/master_volume/", &masterVolumeTableFiles, ".tbl");
+ listdir("../ss/user/tone_control/", &toneControlFiles, ".tbl");
#else
connector = new Hagoromo::HagoromoConnector();
auto v = (Hagoromo::HagoromoConnector *)connector;
@@ -302,16 +312,34 @@ int main(int, char **) {
socket = WAMPY_SOCKET;
listdir("/system/vendor/unknown321/usr/share/wampy/skins/winamp/", &skinList, ".wsz");
listdir("/contents/wampy/skins/winamp/", &skinList, ".wsz");
- // listdir("/contents_ext/wampy/skins/winamp/", &skinList, ".wsz");
listdirs("/system/vendor/unknown321/usr/share/wampy/skins/cassette/reel/", &reelList);
listdirs("/contents/wampy/skins/cassette/reel/", &reelList);
- // listdirs("/contents_ext/wampy/skins/cassette/reel/", &reelList);
listdirs("/system/vendor/unknown321/usr/share/wampy/skins/cassette/tape/", &tapeList);
listdirs("/contents/wampy/skins/cassette/tape/", &tapeList);
- // listdirs("/contents_ext/wampy/skins/cassette/tape/", &tapeList);
+ listdir("/system/vendor/unknown321/usr/share/wampy/sound_settings/master_volume_dsd/", &masterVolumeTableDSDFiles, ".tbl");
+ listdir("/contents/wampy/sound_settings/master_volume_dsd/", &masterVolumeTableDSDFiles, ".tbl");
+
+ listdir("/system/vendor/unknown321/usr/share/wampy/sound_settings/master_volume/", &masterVolumeTableFiles, ".tbl");
+ listdir("/contents/wampy/sound_settings/master_volume/", &masterVolumeTableFiles, ".tbl");
+
+ listdir("/system/vendor/unknown321/usr/share/wampy/sound_settings/tone_control/", &toneControlFiles, ".tbl");
+ listdir("/contents/wampy/sound_settings/tone_control/", &toneControlFiles, ".tbl");
+
+ // device fallback, desktop will just crash
+ if (masterVolumeTableDSDFiles.empty()) {
+ listdir("/system/usr/share/audio_dac/", &masterVolumeTableDSDFiles, ".tbl");
+ }
+
+ if (masterVolumeTableFiles.empty()) {
+ listdir("/system/usr/share/audio_dac/", &masterVolumeTableFiles, ".tbl");
+ }
+
+ if (toneControlFiles.empty()) {
+ listdir("/system/usr/share/audio_dac/", &toneControlFiles, ".tbl");
+ }
#endif
connector->render = &render;
@@ -354,6 +382,9 @@ int main(int, char **) {
skin.skinList = &skinList;
skin.reelList = &reelList;
skin.tapeList = &tapeList;
+ skin.masterVolumeDSDFiles = &masterVolumeTableDSDFiles;
+ skin.masterVolumeFiles = &masterVolumeTableFiles;
+ skin.toneControlFiles = &toneControlFiles;
skin.hold_toggled = &hold_toggled;
skin.power_pressed = &power_pressed;
skin.hold_value = &hold_value;
@@ -386,7 +417,8 @@ int main(int, char **) {
skin.ReadLicense();
skin.ReadQR();
-
+ skin.PreprocessTableFilenames();
+ skin.GetLogsDirSize();
skin.Load();
if (socket.empty()) {
diff --git a/src/skin.h b/src/skin.h
index dada8f4..4c0934a 100644
--- a/src/skin.h
+++ b/src/skin.h
@@ -4,7 +4,9 @@
#include "Version.h"
#include "cassette/cassette.h"
#include "config.h"
+#include "dac/dac.h"
#include "digital_clock/digital_clock.h"
+#include "imgui_curve.h"
#include "skinVariant.h"
#include "w1/w1.h"
#include "winamp/winamp.h"
@@ -20,6 +22,8 @@ enum SettingsTab {
TabLicense = 4,
TabWebsite = 5,
TabWalkmanOne = 6,
+ TabSoundSettings = 7,
+ TabCurveEditor = 8,
};
struct Skin {
@@ -33,12 +37,11 @@ struct Skin {
int selectedSkinIdx{}; // winamp skin idx
std::string loadStatusStr{}; // skin loading status in settings
Connector *connector{};
- bool loading{};
bool needLoad{};
bool *hold_toggled = nullptr;
int *hold_value = nullptr;
bool *power_pressed = nullptr;
- int displaySettings{};
+ int displaySettings = 0;
SettingsTab displayTab = SettingsTab::SkinOpts;
ESkinVariant activeSkinVariant = EMPTY;
@@ -61,17 +64,82 @@ struct Skin {
std::string licensePath = "../LICENSE";
std::string license3rdPath = "../LICENSE_3rdparty";
std::string qrPath = "../qr.bmp";
+ std::string qrDonatePath = "../qrDonate.bmp";
+ std::string soundSettingsPathSystemWampy = "../ss/system/";
+ std::string soundSettingsPathSystemHagoromo = "../ss/system/";
+ std::string soundSettingsPathUser = "../ss/user/";
#else
std::string licensePath = "/system/vendor/unknown321/usr/share/wampy/doc/LICENSE";
std::string license3rdPath = "/system/vendor/unknown321/usr/share/wampy/doc/LICENSE_3rdparty";
std::string qrPath = "/system/vendor/unknown321/usr/share/wampy/qr.bmp";
+ std::string qrDonatePath = "/system/vendor/unknown321/usr/share/wampy/qrDonate.bmp";
+ std::string soundSettingsPathUser = "/contents/wampy/sound_settings/";
+ std::string soundSettingsPathSystemWampy = "/system/vendor/unknown321/usr/share/wampy/sound_settings/";
+ std::string soundSettingsPathSystemHagoromo = "/system/usr/share/audio_dac/";
#endif
GLuint qrTexture{};
+ GLuint qrDonateTexture{};
float qrSide{};
bool isWalkmanOne{};
+ int zoomDirection = 0;
+ int zoomZero = 0;
+
+ std::string systemMark = "Ⓢ ";
+ std::string systemMarkHagoromo = "Ⓗ ";
+
+ std::vector *masterVolumeFiles;
+ int masterVolumeFileSelected = 0;
+ bool soundEffectOn = true;
+ int MasterVolumeTableType = MASTER_VOLUME_TABLE_SMASTER_SE_HG;
+ int MasterVolumeValueType = MASTER_VOLUME_VALUE_HPOUT;
+ master_volume masterVolume{};
+ ImVec2 masterVolumeValues[2][MASTER_VOLUME_TABLE_MAX + 1][MASTER_VOLUME_VALUE_MAX][MASTER_VOLUME_MAX + 1 + 1] = {
+ {{{{-FLT_MIN, -FLT_MIN}}}}};
+ ImVec2 masterVolumeValueBuffer[MASTER_VOLUME_MAX + 1 + 1] = {{0, 0}};
+ std::string statusStringMasterVolume;
+
+ std::vector *masterVolumeDSDFiles;
+ int masterVolumeDSDFileSelected;
+ master_volume_dsd masterVolumeDSD{};
+ ImVec2 masterVolumeDSDValues[MASTER_VOLUME_TABLE_MAX + 1][MASTER_VOLUME_MAX + 1] = {{{-FLT_MIN, -FLT_MIN}}};
+ ImVec2 masterVolumeDSDValueBuffer[MASTER_VOLUME_MAX + 1] = {{0, 0}};
+ int MasterVolumeDSDTableType = MASTER_VOLUME_TABLE_SMASTER_SE_DSD64_HG;
+ std::string statusStringMasterVolumeDSD;
+
+ std::vector *toneControlFiles;
+ int toneControlFileSelected;
+ tone_control toneControl{};
+ ImVec2 toneControlValues[TONE_CONTROL_TABLE_MAX + 1][CODEC_RAM_SIZE] = {{{-FLT_MIN, -FLT_MIN}}};
+ ImVec2 toneControlValueBuffer[CODEC_RAM_SIZE] = {{0, 0}};
+ int toneControlTableType = TONE_CONTROL_TABLE_SAMP_GENERAL_HP;
+ std::string statusStringToneControl;
+
+ std::string statusMasterVTFile;
+ std::string statusMasterVTDSDFile;
+ std::string statusToneControlFile;
+ std::string deviceProduct;
+ std::string deviceModelID;
+ std::string deviceRegionID;
+ std::string deviceRegionStr;
+ std::pair deviceAudioInUse;
+ std::string minCpuFreq;
+ std::string curCpuFreq;
+ std::string freqStr;
+
+ float *curveEditorTarget = (float *)masterVolumeValues[int(soundEffectOn)][MasterVolumeTableType][MasterVolumeValueType];
+ float curveYLimit;
+ int curveElementCount = 121;
+
+ bool llusbdacLoaded;
+ std::string llusbdacStatus;
+
+ std::string bookmarkExportStatus;
+ std::string logCleanupStatus;
+ std::string logCleanupButtonLabel;
+ std::string refreshStatus;
+
void ReloadFont() {
- loading = true;
std::string filepath{};
switch (activeSkinVariant) {
case CASSETTE:
@@ -93,7 +161,6 @@ struct Skin {
config->Save();
onlyFont = false;
- loading = false;
}
void ReadQR() {
@@ -105,6 +172,14 @@ struct Skin {
i.write(&blob);
qrSide = (float)i.size().width();
LoadTextureFromMagic((unsigned char *)blob.data(), &qrTexture, (int)i.size().width(), (int)i.size().height());
+
+ Magick::Image v;
+ v.read(qrDonatePath);
+ v.magick("RGBA");
+ v.depth(8);
+ Magick::Blob blob2;
+ v.write(&blob2);
+ LoadTextureFromMagic((unsigned char *)blob2.data(), &qrDonateTexture, (int)v.size().width(), (int)v.size().height());
}
void Load() {
@@ -119,8 +194,6 @@ struct Skin {
return;
}
- loading = true;
-
if (config->activeSkin == WINAMP) {
DLOG("loading winamp\n");
cassette.Unload();
@@ -211,6 +284,75 @@ struct Skin {
needLoad = false;
}
+ void MasterVolumeTableToImVec2() {
+ for (int index = 0; index < 2; index++) {
+ for (int tableID = 0; tableID < MASTER_VOLUME_TABLE_MAX; tableID++) {
+ for (int i = MASTER_VOLUME_MIN; i < MASTER_VOLUME_MAX + 1; i++) {
+
+ for (int valType = MASTER_VOLUME_VALUE_MIN; valType < MASTER_VOLUME_VALUE_MAX; valType++) {
+ auto val = masterVolume.GetValue(index, tableID, i, (MASTER_VOLUME_VALUE)valType);
+ // if (val != 0) {
+ // DLOG("Got %d -> %f, %f\n", val, (float)val, float(val));
+ // }
+ masterVolumeValues[index][tableID][valType][i].x = (float)i;
+ masterVolumeValues[index][tableID][valType][i].y = (float)val;
+ }
+ }
+ }
+ }
+ }
+
+ void MasterVolumeDSDTableToImVec2() {
+ for (int tableID = 0; tableID < MASTER_VOLUME_TABLE_MAX; tableID++) {
+ for (int i = MASTER_VOLUME_MIN; i < MASTER_VOLUME_MAX + 1; i++) {
+ auto val = masterVolumeDSD.v[tableID][i];
+ masterVolumeDSDValues[tableID][i] = ImVec2((float)i, (float)val);
+ }
+ }
+ }
+
+ void ToneControlToImVec2() {
+ for (int tableID = 0; tableID < TONE_CONTROL_TABLE_MAX; tableID++) {
+ for (int i = 0; i < CODEC_RAM_SIZE; i++) {
+ auto val = toneControl.v[tableID][i];
+ toneControlValues[tableID][i] = ImVec2((float)i, (float)val);
+ }
+ }
+ }
+
+ void MasterVolumeImVec2ToTable() {
+ for (int index = 0; index < 2; index++) {
+ for (int tableID = 0; tableID < MASTER_VOLUME_TABLE_MAX; tableID++) {
+ for (int i = MASTER_VOLUME_MIN; i < MASTER_VOLUME_MAX + 1; i++) {
+ for (int valType = 1; valType < MASTER_VOLUME_VALUE_MAX; valType++) {
+ auto val = masterVolumeValues[index][tableID][valType][i].y;
+ masterVolume.SetValue(index, tableID, i, (MASTER_VOLUME_VALUE)valType, uint(val));
+ }
+ }
+ }
+ }
+ }
+
+ void MasterVolumeDSDImVec2ToTable() {
+ for (int tableID = 0; tableID < MASTER_VOLUME_TABLE_MAX; tableID++) {
+ for (int i = MASTER_VOLUME_MIN; i < MASTER_VOLUME_MAX + 1; i++) {
+ for (int valType = 1; valType < MASTER_VOLUME_VALUE_MAX; valType++) {
+ auto val = masterVolumeDSDValues[tableID][i];
+ masterVolumeDSD.v[tableID][i] = (int)val.y;
+ }
+ }
+ }
+ }
+
+ void ToneControlImVec2ToTable() {
+ for (int tableID = 0; tableID < TONE_CONTROL_TABLE_MAX; tableID++) {
+ for (int i = 0; i < CODEC_RAM_SIZE; i++) {
+ auto val = toneControlValues[tableID][i];
+ toneControl.v[tableID][i] = (int)val.y;
+ }
+ }
+ }
+
static void ToggleDrawSettings(void *skin, void *) {
auto s = (Skin *)skin;
assert(s);
@@ -249,6 +391,11 @@ struct Skin {
loadStatusStr = "";
displayTab = SettingsTab::TabWalkmanOne;
}
+ ImGui::SameLine();
+ if (ImGui::Button("♪♫")) {
+ loadStatusStr = "";
+ displayTab = SettingsTab::TabSoundSettings;
+ }
auto fontsSize = ImGui::CalcTextSize("Fonts").x + ImGui::GetStyle().FramePadding.x * 2.f;
auto miscSize = ImGui::CalcTextSize("Misc").x + ImGui::GetStyle().FramePadding.x * 2.f;
@@ -327,52 +474,109 @@ struct Skin {
}
void Misc() {
-#ifndef DESKTOP
ImGui::NewLine();
- if (ImGui::Checkbox("Swap prev/next buttons", &config->misc.swapTrackButtons)) {
- config->Save();
- }
+ if (ImGui::BeginTable("##misctable", 4, ImGuiTableFlags_None)) {
+ // #ifndef DESKTOP
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("Swap prev/next buttons", &config->misc.swapTrackButtons)) {
+ config->Save();
+ }
+ ImGui::TableNextColumn();
+ ImGui::Text(" "); // padding
- if (ImGui::Checkbox("Huge cover art", &config->features.bigCover)) {
- config->Save();
- connector->FeatureBigCover(config->features.bigCover);
- }
+ ImGui::TableNextColumn();
+ if (ImGui::Button("Export bookmarks")) {
+ ExportBookmarks();
+ bookmarkExportStatus = "Exported";
+ }
- if (isWalkmanOne == false) {
- if (ImGui::Checkbox("Show time", &config->features.showTime)) {
+ ImGui::TableNextColumn();
+ ImGui::Text(bookmarkExportStatus.c_str());
+ ImGui::SameLine(20);
+ if (ImGui::InvisibleButton("##bookmarkStatus", ImVec2(246, 30))) {
+ bookmarkExportStatus = "";
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("Huge cover art", &config->features.bigCover)) {
config->Save();
- connector->FeatureShowTime(config->features.showTime);
+ connector->FeatureBigCover(config->features.bigCover);
}
- }
- if (ImGui::Checkbox("Limit max volume", &config->features.limitVolume)) {
- config->Save();
- connector->FeatureSetMaxVolume(config->features.limitVolume);
- }
+ ImGui::TableNextColumn();
+ ImGui::TableNextColumn();
+ if (ImGui::Button(logCleanupButtonLabel.c_str())) {
+ RemoveLogs();
+ logCleanupStatus = "Removed!";
+ GetLogsDirSize();
+ }
- if (ImGui::Checkbox("Disable touchscreen", &config->features.touchscreenStaysOFF)) {
- config->Save();
- }
-#endif
+ ImGui::TableNextColumn();
+ ImGui::Text(logCleanupStatus.c_str());
+ ImGui::SameLine(20);
+ if (ImGui::InvisibleButton("##logCleanupStatus", ImVec2(246, 30))) {
+ logCleanupStatus = "";
+ }
- ImGui::NewLine();
- if (ImGui::Checkbox("Debug", &config->debug)) {
- winamp.debug = config->debug;
- cassette.debug = config->debug;
- config->Save();
- }
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (isWalkmanOne == false) {
+ if (ImGui::Checkbox("Show time", &config->features.showTime)) {
+ config->Save();
+ connector->FeatureShowTime(config->features.showTime);
+ }
+ }
+ ImGui::TableNextColumn();
- if (ImGui::Checkbox("Limit fps", &config->limitFPS)) {
- config->Save();
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("Limit max volume", &config->features.limitVolume)) {
+ config->Save();
+ connector->FeatureSetMaxVolume(config->features.limitVolume);
+ }
+ ImGui::TableNextColumn();
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("Disable touchscreen", &config->features.touchscreenStaysOFF)) {
+ config->Save();
+ }
+ ImGui::TableNextColumn();
+ // #endif
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("");
+
+ ImGui::TableNextColumn();
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("Debug", &config->debug)) {
+ winamp.debug = config->debug;
+ cassette.debug = config->debug;
+ config->Save();
+ }
+ ImGui::TableNextColumn();
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("Limit fps", &config->limitFPS)) {
+ config->Save();
+ }
+ ImGui::TableNextColumn();
+
+ ImGui::EndTable();
}
- auto website = ImGui::CalcTextSize("Website");
+ auto website = ImGui::CalcTextSize("Website / Donate");
auto verSize = ImGui::CalcTextSize(SOFTWARE_VERSION);
auto licenseSize = ImGui::CalcTextSize("License");
auto license3Size = ImGui::CalcTextSize("License 3rdparty");
auto offset = 15.0f;
-#ifndef DESKTOP
+ // #ifndef DESKTOP
if (config->debug) {
ImGui::SetCursorPosY(480 - verSize.y - license3Size.y * 2 - ImGui::GetStyle().FramePadding.y * 3 - offset);
@@ -385,13 +589,13 @@ struct Skin {
createDump();
}
}
-#endif
+ // #endif
ImGui::SetCursorPosY(480 - verSize.y - ImGui::GetStyle().FramePadding.y);
printFPS();
ImGui::SetCursorPosY(480 - verSize.y - licenseSize.y * 3 - ImGui::GetStyle().FramePadding.y * 2 - offset * 2);
ImGui::SetCursorPosX(800 - website.x - ImGui::GetStyle().FramePadding.x - offset);
- if (ImGui::Button("Website")) {
+ if (ImGui::Button("Website / Donate")) {
displayTab = SettingsTab::TabWebsite;
loadStatusStr = "";
}
@@ -416,9 +620,43 @@ struct Skin {
}
void Website() const {
- ImGui::Text("https://github.com/unknown321/wampy");
ImGui::NewLine();
- ImGui::Image((void *)(intptr_t)qrTexture, ImVec2(qrSide, qrSide));
+ float columnWidth = 380.0f;
+ if (ImGui::BeginTable("##website", 2, ImGuiTableFlags_None)) {
+ ImGui::TableSetupColumn("Github", ImGuiTableColumnFlags_WidthFixed, 380);
+ ImGui::TableSetupColumn("Donate", ImGuiTableColumnFlags_WidthFixed, 380);
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + columnWidth / 2 - ImGui::CalcTextSize("Github").x / 2);
+ ImGui::Text("Github");
+ ImGui::TableNextColumn();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + columnWidth / 2 - ImGui::CalcTextSize("Donate").x / 2);
+ ImGui::Text("Donate");
+
+ ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10);
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + columnWidth / 2 - qrSide / 2);
+ ImGui::Image((void *)(intptr_t)qrTexture, ImVec2(qrSide, qrSide));
+
+ ImGui::TableNextColumn();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + columnWidth / 2 - qrSide / 2);
+ ImGui::Image((void *)(intptr_t)qrDonateTexture, ImVec2(qrSide, qrSide));
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + columnWidth / 2 - ImGui::CalcTextSize("github.com/unknown321/wampy").x / 2);
+ ImGui::Text("github.com/unknown321/wampy");
+
+ ImGui::TableNextColumn();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + columnWidth / 2 - ImGui::CalcTextSize("boosty.to/unknown321/donate").x / 2);
+ ImGui::Text("boosty.to/unknown321/donate");
+
+ ImGui::EndTable();
+ }
}
void WalkmanOne() {
@@ -873,6 +1111,15 @@ struct Skin {
FontRegular->FontSize = SETTINGS_FONT_SIZE;
ImGui::PushFont(FontRegular);
+
+ // no header for curve editor only
+ if (displayTab == SettingsTab::TabCurveEditor) {
+ CurveEditor();
+
+ ImGui::PopFont();
+ return;
+ }
+
Header();
switch (displayTab) {
case SettingsTab::SkinOpts:
@@ -896,6 +1143,9 @@ struct Skin {
case SettingsTab::TabWalkmanOne:
WalkmanOneTab();
break;
+ case SettingsTab::TabSoundSettings:
+ SoundSettings();
+ break;
default:
break;
}
@@ -903,6 +1153,785 @@ struct Skin {
ImGui::PopFont();
}
+ void PreprocessTableFilenames() {
+ std::vector *> tables = {masterVolumeFiles, masterVolumeDSDFiles, toneControlFiles};
+ for (auto &table : tables) {
+ for (int i = 0; i < table->size(); i++) {
+ auto entry = table->at(i);
+ if (entry.fullPath.rfind(soundSettingsPathSystemWampy, 0) == 0) {
+ table->at(i).name = systemMark + entry.name;
+ continue;
+ }
+ if (entry.fullPath.rfind(soundSettingsPathSystemHagoromo, 0) == 0) {
+ table->at(i).name = systemMarkHagoromo + entry.name;
+ continue;
+ }
+ }
+ }
+ }
+
+ static void CopyTableEntry(TableLike *table, std::vector *fileList, int *selectedIndex, const std::string &outDir) {
+ mkpath(outDir.c_str(), 0755);
+
+ auto d = directoryEntry{};
+ d.name = basename(fileList->at(*selectedIndex).fullPath.c_str());
+ d.fullPath = outDir + d.name;
+ d.valid = true;
+
+ DLOG("copying to %s\n", d.fullPath.c_str());
+
+ table->ToFile(d.fullPath);
+
+ bool exists{};
+ for (int i = 0; i < fileList->size(); i++) {
+ if (fileList->at(i).fullPath == d.fullPath) {
+ exists = true;
+ *selectedIndex = i;
+ break;
+ }
+ }
+
+ if (!exists) {
+ fileList->push_back(d);
+ *selectedIndex = (int)fileList->size() - 1;
+ }
+ }
+
+ void TabMasterVolume() {
+ if (ImGui::BeginTabItem(Dac::TableTypeToString.at(TABLE_ID_MASTER_VOLUME).c_str())) {
+ curveEditorTarget = (float *)masterVolumeValues[int(soundEffectOn)][MasterVolumeTableType][MasterVolumeValueType];
+ curveYLimit = (1 << 8) - 1; // 255
+ curveElementCount = MASTER_VOLUME_MAX + 1;
+
+ if (ImGui::BeginTable("##mvtable", 3, ImGuiTableFlags_None)) {
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_None);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 384);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_None);
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("File:");
+
+ ImGui::TableNextColumn();
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 40.0f);
+ ImGui::PushItemWidth(-FLT_MIN);
+ if (ImGui::BeginCombo(
+ "##masterVolumeFileCombo",
+ masterVolumeFiles->at(masterVolumeFileSelected).name.c_str(),
+ ImGuiComboFlags_HeightRegular
+ )) {
+ for (int idx = 0; idx < masterVolumeFiles->size(); idx++) {
+ auto entry = masterVolumeFiles->at(idx);
+ if (ImGui::Selectable(entry.name.c_str(), false)) {
+ masterVolumeFileSelected = idx;
+ DLOG("selected file %s\n", entry.name.c_str());
+ masterVolume.Reset();
+ memset(&masterVolumeValues, 0, sizeof(masterVolumeValues));
+ if (masterVolume.FromFile(entry.fullPath) != 0) {
+ DLOG("failed to load master volume table file %s\n", entry.fullPath.c_str());
+ statusStringMasterVolume = "Failed";
+ } else {
+ MasterVolumeTableToImVec2();
+ statusStringMasterVolume = "Loaded";
+ }
+ }
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::PopItemWidth();
+ ImGui::PopStyleVar(2);
+
+ ImGui::TableNextColumn();
+
+ if (!statusStringMasterVolume.empty()) {
+ ImGui::Text(statusStringMasterVolume.c_str());
+ ImGui::SameLine(20);
+ if (ImGui::InvisibleButton("##statusmastervolume", ImVec2(246, 30))) {
+ statusStringMasterVolume = "";
+ }
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Table type:");
+ ImGui::TableNextColumn();
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 40.0f);
+ const char *preview;
+ preview = Dac::MasterVolumeTableTypeToString.at(MasterVolumeTableType).c_str();
+ ImGui::PushItemWidth(-FLT_MIN);
+ if (ImGui::BeginCombo("##masterVolumeTypeCombo", preview, ImGuiComboFlags_HeightRegular)) {
+ for (const auto &entry : Dac::MasterVolumeTableTypeToString) {
+ if (ImGui::Selectable(entry.second.c_str(), false)) {
+ MasterVolumeTableType = entry.first;
+ DLOG("selected type %s\n", entry.second.c_str());
+ }
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::PopItemWidth();
+ ImGui::PopStyleVar(2);
+ ImGui::TableNextColumn();
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Value type:");
+ ImGui::TableNextColumn();
+
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 40.0f);
+ ImGui::PushItemWidth(-FLT_MIN);
+ if (ImGui::BeginCombo(
+ "##masterVolumeValueTypeCombo",
+ Dac::MasterVolumeValueTypeToString.at(MasterVolumeValueType).c_str(),
+ ImGuiComboFlags_HeightRegular
+ )) {
+ for (const auto &entry : Dac::MasterVolumeValueTypeToString) {
+ if (ImGui::Selectable(entry.second.c_str(), false)) {
+ MasterVolumeValueType = entry.first;
+ DLOG("selected value type %s (%d)\n", entry.second.c_str(), entry.first);
+ }
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::PopItemWidth();
+ ImGui::PopStyleVar(2);
+
+ ImGui::TableNextColumn();
+ ImGui::Checkbox("Sound effect", &soundEffectOn);
+ }
+ ImGui::EndTable();
+
+ if (ImGui::BeginTable("##mvtcontent", 2, ImGuiTableFlags_None)) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+
+ if (masterVolumeValues[0][0][0][0].x == -FLT_MIN) {
+ ImGui::NewLine();
+ if (ImGui::Button("Load", ImVec2(512, 150))) {
+ if (masterVolume.FromFile(masterVolumeFiles->at(masterVolumeFileSelected).fullPath) != 0) {
+ DLOG(
+ "failed to load master volume table file %s\n",
+ masterVolumeFiles->at(masterVolumeFileSelected).fullPath.c_str()
+ );
+ statusStringMasterVolume = "Failed";
+ } else {
+ MasterVolumeTableToImVec2();
+ statusStringMasterVolume = "Loaded";
+ }
+ }
+ } else {
+ CurveEditorSmall(&connector->status.VolumeRaw, ImVec2(512, 270));
+
+ ImGui::TableNextColumn();
+
+ // not efficient at all
+ if (masterVolumeFiles->at(masterVolumeFileSelected).name.rfind(systemMark, 0) == 0) {
+ if (ImGui::Button("Copy and edit", ImVec2(246, 60))) {
+ auto outDir = soundSettingsPathUser + "master_volume/";
+ CopyTableEntry(&masterVolume, masterVolumeFiles, &masterVolumeFileSelected, outDir);
+ displayTab = TabCurveEditor;
+ }
+ } else {
+ if (ImGui::Button("Edit", ImVec2(246, 60))) {
+ displayTab = TabCurveEditor;
+ }
+ }
+
+ if (masterVolumeFiles->at(masterVolumeFileSelected).name.rfind(systemMark, 0) == 0) {
+ ImGui::BeginDisabled();
+ }
+
+ if (ImGui::Button("Save", ImVec2(246, 60))) {
+ auto out = masterVolumeFiles->at(masterVolumeFileSelected).fullPath;
+ DLOG("Saving to %s\n", out.c_str());
+ MasterVolumeImVec2ToTable();
+ if (masterVolume.ToFile(out) == 0) {
+ statusStringMasterVolume = "Saved";
+ } else {
+ statusStringMasterVolume = "Failed";
+ }
+ }
+
+ if (masterVolumeFiles->at(masterVolumeFileSelected).name.rfind(systemMark, 0) == 0) {
+ ImGui::EndDisabled();
+ }
+
+ if (ImGui::Button("Apply", ImVec2(246, 60))) {
+ DLOG("Applying\n");
+ MasterVolumeImVec2ToTable();
+ if (masterVolume.Apply(Dac::volumeTableOutPath) == 0) {
+ statusStringMasterVolume = "Applied";
+ } else {
+ statusStringMasterVolume = "Failed";
+ }
+ }
+
+ if (ImGui::Button("Copy val", ImVec2(121, 60))) {
+ memcpy(
+ masterVolumeValueBuffer,
+ masterVolumeValues[(int)soundEffectOn][MasterVolumeTableType][MasterVolumeValueType],
+ sizeof(masterVolumeValueBuffer)
+ );
+ statusStringMasterVolume = "Copied";
+ }
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 0));
+
+ ImGui::SameLine();
+ if (ImGui::Button("Paste val", ImVec2(121, 60))) {
+ memcpy(
+ masterVolumeValues[(int)soundEffectOn][MasterVolumeTableType][MasterVolumeValueType],
+ masterVolumeValueBuffer,
+ sizeof(masterVolumeValueBuffer)
+ );
+ statusStringMasterVolume = "Pasted";
+ }
+ ImGui::PopStyleVar();
+ }
+ ImGui::EndTable();
+ }
+ ImGui::EndTabItem();
+ }
+ }
+
+ void TabMasterDSDVolume() {
+ if (ImGui::BeginTabItem(Dac::TableTypeToString.at(TABLE_ID_MASTER_VOLUME_DSD).c_str())) {
+ curveEditorTarget = (float *)masterVolumeDSDValues[MasterVolumeDSDTableType];
+ curveYLimit = (1 << 15) - 1; // 32767
+ curveElementCount = MASTER_VOLUME_MAX + 1;
+
+ if (ImGui::BeginTable("##mvDSDtable", 3, ImGuiTableFlags_None)) {
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_None);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 384);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_None);
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("File:");
+
+ ImGui::TableNextColumn();
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 40.0f);
+ ImGui::PushItemWidth(-FLT_MIN);
+ if (ImGui::BeginCombo(
+ "##masterVolumeDSDFileCombo",
+ masterVolumeDSDFiles->at(masterVolumeDSDFileSelected).name.c_str(),
+ ImGuiComboFlags_HeightRegular
+ )) {
+ for (int idx = 0; idx < masterVolumeDSDFiles->size(); idx++) {
+ auto entry = masterVolumeDSDFiles->at(idx);
+ if (ImGui::Selectable(entry.name.c_str(), false)) {
+ DLOG("selected dsd file %s\n", entry.fullPath.c_str());
+ masterVolumeDSDFileSelected = idx;
+ masterVolumeDSD.Reset();
+ memset(&masterVolumeDSDValues, 0, sizeof(masterVolumeDSDValues));
+ if (masterVolumeDSD.FromFile(entry.fullPath) != 0) {
+ DLOG("failed to load master volume dsd table file %s\n", entry.fullPath.c_str());
+ statusStringMasterVolumeDSD = "Failed";
+ } else {
+ MasterVolumeDSDTableToImVec2();
+ statusStringMasterVolumeDSD = "Loaded";
+ }
+ }
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::PopItemWidth();
+ ImGui::PopStyleVar(2);
+
+ ImGui::TableNextColumn();
+ if (!statusStringMasterVolumeDSD.empty()) {
+ ImGui::Text(statusStringMasterVolumeDSD.c_str());
+ ImGui::SameLine(20);
+ if (ImGui::InvisibleButton("##statusmastervolumeDSD", ImVec2(246, 30))) {
+ statusStringMasterVolumeDSD = "";
+ }
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Table type:");
+
+ ImGui::TableNextColumn();
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 40.0f);
+ ImGui::PushItemWidth(-FLT_MIN);
+ const char *preview;
+ preview = Dac::MasterVolumeTableTypeToString.at(MasterVolumeDSDTableType).c_str();
+ if (ImGui::BeginCombo("##masterVolumeDSDTypeCombo", preview, ImGuiComboFlags_HeightRegular)) {
+ for (const auto &entry : Dac::MasterVolumeTableTypeToString) {
+ if (ImGui::Selectable(entry.second.c_str(), false)) {
+ MasterVolumeDSDTableType = entry.first;
+ DLOG("selected dsd type %s\n", entry.second.c_str());
+ }
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::PopItemWidth();
+ ImGui::PopStyleVar(2);
+ ImGui::TableNextColumn();
+
+ ImGui::EndTable();
+ }
+
+ if (ImGui::BeginTable("##mvtDSDcontent", 2, ImGuiTableFlags_None)) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+
+ if (masterVolumeDSDValues[0][0].x == -FLT_MIN) {
+ ImGui::NewLine();
+ if (ImGui::Button("Load", ImVec2(512, 150))) {
+ if (masterVolumeDSD.FromFile(masterVolumeDSDFiles->at(masterVolumeDSDFileSelected).fullPath) != 0) {
+ DLOG(
+ "failed to load master volume DSD table file %s\n",
+ masterVolumeDSDFiles->at(masterVolumeDSDFileSelected).fullPath.c_str()
+ );
+ statusStringMasterVolumeDSD = "Failed";
+ } else {
+ MasterVolumeDSDTableToImVec2();
+ statusStringMasterVolumeDSD = "Loaded";
+ }
+ }
+ } else {
+ CurveEditorSmall(&connector->status.VolumeRaw, ImVec2(512, 305));
+
+ ImGui::TableNextColumn();
+
+ // not efficient at all
+ if (masterVolumeDSDFiles->at(masterVolumeDSDFileSelected).name.rfind(systemMark, 0) == 0) {
+ if (ImGui::Button("Copy and edit", ImVec2(246, 60))) {
+ auto outDir = soundSettingsPathUser + "master_volume_dsd/";
+ CopyTableEntry(&masterVolumeDSD, masterVolumeDSDFiles, &masterVolumeDSDFileSelected, outDir);
+ displayTab = TabCurveEditor;
+ }
+ } else {
+ if (ImGui::Button("Edit", ImVec2(246, 60))) {
+ displayTab = TabCurveEditor;
+ }
+ }
+
+ if (masterVolumeDSDFiles->at(masterVolumeDSDFileSelected).name.rfind(systemMark, 0) == 0) {
+ ImGui::BeginDisabled();
+ }
+
+ if (ImGui::Button("Save", ImVec2(246, 60))) {
+ auto out = masterVolumeDSDFiles->at(masterVolumeDSDFileSelected).fullPath;
+ DLOG("Saving to %s\n", out.c_str());
+ MasterVolumeDSDImVec2ToTable();
+ if (masterVolumeDSD.ToFile(out) == 0) {
+ statusStringMasterVolumeDSD = "Saved";
+ } else {
+ statusStringMasterVolumeDSD = "Failed";
+ }
+ }
+
+ if (masterVolumeDSDFiles->at(masterVolumeDSDFileSelected).name.rfind(systemMark, 0) == 0) {
+ ImGui::EndDisabled();
+ }
+
+ if (ImGui::Button("Apply", ImVec2(246, 60))) {
+ DLOG("Applying to %s\n", Dac::volumeTableDSDOutPath.c_str());
+ MasterVolumeDSDImVec2ToTable();
+ if (masterVolumeDSD.Apply(Dac::volumeTableDSDOutPath) == 0) {
+ statusStringMasterVolumeDSD = "Applied";
+ } else {
+ statusStringMasterVolumeDSD = "Failed";
+ }
+ }
+
+ if (ImGui::Button("Copy val", ImVec2(121, 60))) {
+ memcpy(
+ masterVolumeDSDValueBuffer, masterVolumeDSDValues[MasterVolumeDSDTableType], sizeof(masterVolumeDSDValueBuffer)
+ );
+ statusStringMasterVolumeDSD = "Copied";
+ }
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 0));
+
+ ImGui::SameLine();
+ if (ImGui::Button("Paste val", ImVec2(121, 60))) {
+ memcpy(
+ masterVolumeDSDValues[MasterVolumeDSDTableType], masterVolumeDSDValueBuffer, sizeof(masterVolumeDSDValueBuffer)
+ );
+ statusStringMasterVolumeDSD = "Pasted";
+ }
+ ImGui::PopStyleVar();
+ }
+
+ ImGui::EndTable();
+ }
+
+ ImGui::EndTabItem();
+ }
+ }
+
+ void TabToneControl() {
+ if (ImGui::BeginTabItem(Dac::TableTypeToString.at(TABLE_ID_TONE_CONTROL).c_str())) {
+ curveEditorTarget = (float *)toneControlValues[toneControlTableType];
+ curveYLimit = (1 << 8) - 1; // 255
+ curveElementCount = CODEC_RAM_SIZE;
+
+ if (ImGui::BeginTable("##mvtable", 3, ImGuiTableFlags_None)) {
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_None);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 384);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_None);
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("File:");
+
+ ImGui::TableNextColumn();
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 40.0f);
+ ImGui::PushItemWidth(-FLT_MIN);
+ if (ImGui::BeginCombo(
+ "##toneControlFileCombo", toneControlFiles->at(toneControlFileSelected).name.c_str(), ImGuiComboFlags_HeightRegular
+ )) {
+ for (int idx = 0; idx < toneControlFiles->size(); idx++) {
+ auto entry = toneControlFiles->at(idx);
+ if (ImGui::Selectable(entry.name.c_str(), false)) {
+ DLOG("selected tone control file %s\n", entry.fullPath.c_str());
+ toneControlFileSelected = idx;
+ toneControl.Reset();
+ memset(&toneControlValues, 0, sizeof(toneControlValues));
+ if (toneControl.FromFile(entry.fullPath) != 0) {
+ DLOG("failed to load tone control table file %s\n", entry.fullPath.c_str());
+ statusStringToneControl = "Failed";
+ } else {
+ ToneControlToImVec2();
+ statusStringToneControl = "Loaded";
+ }
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ ImGui::PopItemWidth();
+ ImGui::PopStyleVar(2);
+
+ ImGui::TableNextColumn();
+ if (!statusStringToneControl.empty()) {
+ ImGui::Text(statusStringToneControl.c_str());
+ ImGui::SameLine(20);
+ if (ImGui::InvisibleButton("##statusToneControl", ImVec2(246, 30))) {
+ statusStringToneControl = "";
+ }
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Table type:");
+
+ ImGui::TableNextColumn();
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 40.0f);
+ const char *preview;
+ preview = Dac::ToneControlTableTypeToString.at(toneControlTableType).c_str();
+ ImGui::PushItemWidth(-FLT_MIN);
+ if (ImGui::BeginCombo("##toneControlTypeCombo", preview, ImGuiComboFlags_HeightRegular)) {
+ for (const auto &entry : Dac::ToneControlTableTypeToString) {
+ if (ImGui::Selectable(entry.second.c_str(), false)) {
+ toneControlTableType = entry.first;
+ DLOG("selected tone control type %s\n", entry.second.c_str());
+ }
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::PopItemWidth();
+ ImGui::PopStyleVar(2);
+
+ ImGui::EndTable();
+ }
+
+ if (ImGui::BeginTable("##ttcontent", 2, ImGuiTableFlags_None)) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+
+ if (toneControlValues[0][0].x == -FLT_MIN) {
+ ImGui::NewLine();
+ if (ImGui::Button("Load", ImVec2(512, 150))) {
+ if (toneControl.FromFile(toneControlFiles->at(toneControlFileSelected).fullPath) != 0) {
+ DLOG(
+ "failed to load tone control table file %s\n",
+ toneControlFiles->at(toneControlFileSelected).fullPath.c_str()
+ );
+ statusStringToneControl = "Failed";
+ } else {
+ ToneControlToImVec2();
+ statusStringToneControl = "Loaded";
+ }
+ }
+ } else {
+ CurveEditorSmall(nullptr, ImVec2(512, 305));
+
+ ImGui::TableNextColumn();
+
+ // not efficient at all
+ if (toneControlFiles->at(toneControlFileSelected).name.rfind(systemMark, 0) == 0) {
+ if (ImGui::Button("Copy and edit", ImVec2(246, 60))) {
+ auto outDir = soundSettingsPathUser + "tone_control/";
+ CopyTableEntry(&toneControl, toneControlFiles, &toneControlFileSelected, outDir);
+ displayTab = TabCurveEditor;
+ }
+ } else {
+ if (ImGui::Button("Edit", ImVec2(246, 60))) {
+ displayTab = TabCurveEditor;
+ }
+ }
+
+ if (toneControlFiles->at(toneControlFileSelected).name.rfind(systemMark, 0) == 0) {
+ ImGui::BeginDisabled();
+ }
+
+ if (ImGui::Button("Save", ImVec2(246, 60))) {
+ auto out = toneControlFiles->at(toneControlFileSelected).fullPath;
+ DLOG("Saving to %s\n", out.c_str());
+ ToneControlImVec2ToTable();
+ if (toneControl.ToFile(out) == 0) {
+ statusStringToneControl = "Saved";
+ } else {
+ statusStringToneControl = "Failed";
+ }
+ }
+
+ if (toneControlFiles->at(toneControlFileSelected).name.rfind(systemMark, 0) == 0) {
+ ImGui::EndDisabled();
+ }
+
+ if (ImGui::Button("Apply", ImVec2(246, 60))) {
+ DLOG("Applying to %s\n", Dac::toneControlOutPath.c_str());
+ ToneControlImVec2ToTable();
+ if (toneControl.Apply(Dac::toneControlOutPath) == 0) {
+ statusStringToneControl = "Applied";
+ } else {
+ statusStringToneControl = "Failed";
+ }
+ }
+
+ if (ImGui::Button("Copy val", ImVec2(121, 60))) {
+ memcpy(toneControlValueBuffer, toneControlValues[toneControlTableType], sizeof(toneControlValueBuffer));
+ statusStringToneControl = "Copied";
+ }
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 0));
+
+ ImGui::SameLine();
+ if (ImGui::Button("Paste val", ImVec2(121, 60))) {
+ memcpy(toneControlValues[toneControlTableType], toneControlValueBuffer, sizeof(toneControlValueBuffer));
+ statusStringToneControl = "Pasted";
+ }
+ ImGui::PopStyleVar();
+ }
+
+ ImGui::EndTable();
+ }
+ ImGui::EndTabItem();
+ }
+ }
+
+ void TabSoundStatus() {
+ if (ImGui::BeginTabItem("Status")) {
+ ImGui::NewLine();
+ if (ImGui::BeginTable("##soundstatustable", 2, ImGuiTableFlags_None)) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::Button("Refresh", ImVec2(240, 60))) {
+ statusMasterVTFile = Dac::getStatus(masterVolumeFiles, Dac::volumeTableOutPath);
+ statusMasterVTDSDFile = Dac::getStatus(masterVolumeDSDFiles, Dac::volumeTableDSDOutPath);
+ statusToneControlFile = Dac::getStatus(toneControlFiles, Dac::toneControlOutPath);
+ deviceProduct = GetProduct();
+ deviceModelID = GetModelID();
+ deviceRegionID = GetRegionID();
+ deviceRegionStr = GetRegionIDStr();
+ deviceAudioInUse = AudioDeviceInUse();
+ minCpuFreq = ReadFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq");
+ rstrip(&minCpuFreq, '\n');
+ curCpuFreq = ReadFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
+ rstrip(&curCpuFreq, '\n');
+ freqStr = minCpuFreq + " (" + curCpuFreq + ") Hz";
+
+ refreshStatus = "Refreshed";
+ }
+
+ ImGui::TableNextColumn();
+
+ ImGui::Text(refreshStatus.c_str());
+ ImGui::SameLine(20);
+ if (ImGui::InvisibleButton("##refreshStatus", ImVec2(246, 30))) {
+ refreshStatus = "";
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Model ID");
+ ImGui::TableNextColumn();
+ ImGui::Text(deviceModelID.c_str());
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Region ID");
+ ImGui::TableNextColumn();
+ if (!deviceRegionID.empty() && !deviceRegionStr.empty()) {
+ ImGui::Text("%s (%s)", deviceRegionID.c_str(), deviceRegionStr.c_str());
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Device product");
+ ImGui::TableNextColumn();
+ ImGui::Text(deviceProduct.c_str());
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Master volume table");
+ ImGui::TableNextColumn();
+ ImGui::Text(statusMasterVTFile.c_str());
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Master volume table (DSD)");
+ ImGui::TableNextColumn();
+ ImGui::Text(statusMasterVTDSDFile.c_str());
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Tone control");
+ ImGui::TableNextColumn();
+ ImGui::Text(statusToneControlFile.c_str());
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Audio card");
+ ImGui::TableNextColumn();
+ ImGui::Text(deviceAudioInUse.first.c_str());
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("Audio device");
+ ImGui::TableNextColumn();
+ ImGui::Text(deviceAudioInUse.second.c_str());
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("CPU frequency (min/cur)");
+ ImGui::TableNextColumn();
+ ImGui::Text(freqStr.c_str(), ImVec2(200, 100));
+
+ ImGui::EndTable();
+ }
+
+ ImGui::EndTabItem();
+ }
+ }
+
+ void TabLlusbdac() {
+ if (ImGui::BeginTabItem("llusbdac")) {
+ ImGui::Text(llusbdacStatus.c_str());
+
+ auto label = llusbdacLoaded ? "Disable" : "Enable";
+ if (ImGui::Button(label, ImVec2(756, 350))) {
+ if (llusbdacLoaded) {
+ llusbdacLoaded = !DisableLLUSBDAC();
+ llusbdacStatus = !llusbdacLoaded ? "Unloaded" : "Failure";
+ } else {
+ llusbdacLoaded = EnableLLUSBDAC();
+ llusbdacStatus = llusbdacLoaded ? "Loaded" : "Failure";
+ }
+ }
+
+ ImGui::EndTabItem();
+ }
+ }
+
+ void SoundSettings() {
+ ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 15);
+ ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;
+ if (ImGui::BeginTabBar("SoundSettingsTabBar", tab_bar_flags)) {
+ TabMasterVolume();
+ TabMasterDSDVolume();
+ TabToneControl();
+ TabSoundStatus();
+ TabLlusbdac();
+ ImGui::EndTabBar();
+ }
+ }
+
+ void CurveEditorSmall(const int *volume, ImVec2 size) {
+ int newCount;
+ int selected;
+ int hovered;
+
+ if (size.x == 0 || size.y == 0) {
+ size = ImVec2(512, 270);
+ }
+
+ if (ImGui::CurveEditor(
+ "##curvaSmall",
+ curveEditorTarget,
+ curveElementCount,
+ curveElementCount,
+ size,
+ (int)ImGui::CurveEditorFlags::NO_TANGENTS | (int)ImGui::CurveEditorFlags::SHOW_GRID | (int)ImGui::CurveEditorFlags::RESET,
+ &newCount,
+ &selected,
+ &hovered,
+ &zoomZero,
+ 0,
+ false,
+ curveYLimit,
+ volume
+ )) {
+ }
+ }
+
+ void CurveEditor() {
+ int newCount;
+ int selected;
+ int hovered;
+
+ if (ImGui::Button("Zoom in")) {
+ zoomDirection = 1;
+ }
+
+ ImGui::SameLine();
+ if (ImGui::Button("Zoom out")) {
+ zoomDirection = -1;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Reset zoom")) {
+ zoomDirection = 2;
+ }
+
+ float offset = 15.0f;
+ auto closeSize = ImGui::CalcTextSize("Back").x + ImGui::GetStyle().FramePadding.x * 2.f;
+ auto resetSize = ImGui::CalcTextSize("Reset").x + ImGui::GetStyle().FramePadding.x * 2.f;
+ ImGui::SameLine(800.0f - closeSize - offset);
+ if (ImGui::Button("Back")) {
+ displayTab = TabSoundSettings;
+ }
+
+ ImGui::SameLine(800.0f - resetSize - closeSize - offset * 2);
+ if (ImGui::Button("Reset")) {
+ MasterVolumeTableToImVec2();
+ }
+
+ if (ImGui::CurveEditor(
+ "##curva",
+ curveEditorTarget,
+ curveElementCount,
+ curveElementCount,
+ ImVec2(780, 430),
+ (int)ImGui::CurveEditorFlags::NO_TANGENTS | (int)ImGui::CurveEditorFlags::SHOW_GRID,
+ &newCount,
+ &selected,
+ &hovered,
+ &zoomDirection,
+ 10,
+ true,
+ curveYLimit
+ )) {
+ }
+
+ zoomDirection = 0;
+ }
+
void Draw() {
if (displaySettings == 0) {
switch (activeSkinVariant) {
@@ -932,7 +1961,6 @@ struct Skin {
}
void KeyHandler() const {
-
if (*hold_toggled) {
*hold_toggled = false;
@@ -987,6 +2015,26 @@ struct Skin {
return;
}
}
+
+ void GetLogsDirSize() {
+ std::vector files{};
+ listdir("/contents/wampy/log/", &files, "*");
+
+ int res = 0;
+ struct stat sb {};
+ for (const auto &e : files) {
+ if (stat(e.fullPath.c_str(), &sb) != 0) {
+ DLOG("cannot stat %s\n", e.fullPath.c_str());
+ continue;
+ }
+
+ res += (int)sb.st_size;
+ }
+
+ res = res / 1024 / 1024;
+
+ logCleanupButtonLabel = "Remove wampy logs (" + std::to_string(res) + " MB)";
+ }
};
#endif // WAMPY_SKIN_H
\ No newline at end of file
diff --git a/src/util/util.cpp b/src/util/util.cpp
index 16c323e..fd39770 100644
--- a/src/util/util.cpp
+++ b/src/util/util.cpp
@@ -20,15 +20,22 @@ void listdir(const char *dirname, std::vector *list, const std::
continue;
}
- const char *ext = strrchr(dir->d_name, '.');
- if ((!ext) || (ext == dir->d_name))
- continue;
- else {
- if (strcmp(ext, extension.c_str()) == 0) {
- auto a = directoryEntry{};
- a.fullPath = std::string(dirname) + std::string(dir->d_name);
- a.name = std::string(dir->d_name);
- list->emplace_back(a);
+ if (extension.empty() || extension == "*") {
+ auto a = directoryEntry{};
+ a.fullPath = std::string(dirname) + std::string(dir->d_name);
+ a.name = std::string(dir->d_name);
+ list->emplace_back(a);
+ } else {
+ const char *ext = strrchr(dir->d_name, '.');
+ if ((!ext) || (ext == dir->d_name))
+ continue;
+ else {
+ if (strcmp(ext, extension.c_str()) == 0) {
+ auto a = directoryEntry{};
+ a.fullPath = std::string(dirname) + std::string(dir->d_name);
+ a.name = std::string(dir->d_name);
+ list->emplace_back(a);
+ }
}
}
}
@@ -308,7 +315,6 @@ int mkpath(const char *path, mode_t mode) {
}
void recoverDumps(const std::string &outdir) {
- struct stat sb {};
std::string corePath = "/var/log/core......gz";
std::string hdumpstatePath = "/var/log/hdumpstate......log";
std::string out;
@@ -322,6 +328,7 @@ void recoverDumps(const std::string &outdir) {
strftime(buffer, sizeof(buffer), ".%Y-%m-%d_%H.%M.%S", timeinfo);
auto t = std::string(buffer);
+ struct stat sb {};
if (stat(corePath.c_str(), &sb) == 0) {
mkpath(outdir.c_str(), 0755);
out = outdir + "/core......gz" + t;
@@ -373,16 +380,20 @@ void createDump() {
void startADB() { system("/system/vendor/sony/bin/AdbEnabler"); }
+std::string ReadFile(const std::string &path) {
+ std::ifstream f(path, std::ios::binary | std::ios::in);
+ std::string m((std::istreambuf_iterator(f)), std::istreambuf_iterator());
+ f.close();
+ return m;
+}
+
void getModel(std::string *model, bool *isWalkmanOne) {
#ifdef DESKTOP
*model = "desktop";
return;
#endif
- std::ifstream f;
- f.open("/dev/icx_nvp/033");
- std::string m((std::istreambuf_iterator(f)), std::istreambuf_iterator());
- *model = m;
- f.close();
+
+ *model = ReadFile("/dev/icx_nvp/033");
struct stat info {};
@@ -609,4 +620,212 @@ Atlas LoadAtlas(const std::string &texturePath, const std::string &coordsPath) {
a.images = coords;
return a;
-}
\ No newline at end of file
+}
+
+std::string RunWithOutput(const std::string &command) {
+ FILE *fp;
+ char buf[1035];
+
+ std::string res;
+
+ fp = popen(command.c_str(), "r");
+ if (fp == nullptr) {
+ printf("Failed to run command\n");
+ exit(1);
+ }
+
+ while (fgets(buf, sizeof(buf), fp) != nullptr) {
+ res.append(buf);
+ }
+
+ pclose(fp);
+
+ if (res.empty()) {
+ return "";
+ }
+
+ if (res[res.length() - 1] == '\n') {
+ res.erase(res.length() - 1);
+ }
+
+ return res;
+}
+
+std::string GetProduct() {
+#ifdef DESKTOP
+ return "Desktop";
+#endif
+ return RunWithOutput("getprop ro.product.device");
+}
+
+std::string GetModelID() {
+#ifdef DESKTOP
+ return "Desktop";
+#endif
+ return RunWithOutput("nvpflag -x mid");
+}
+
+std::string GetRegionID() {
+#ifdef DESKTOP
+ return "Desktop";
+#endif
+ auto full = RunWithOutput("nvpflag -x shp");
+ return split(full, " ")[0];
+}
+
+std::string GetRegionIDStr() {
+#ifdef DESKTOP
+ return "Desktop";
+#endif
+
+ auto r = GetRegionID();
+ if (RegionIDToString.find(r) == RegionIDToString.end()) {
+ return DefaultRegion;
+ }
+
+ return RegionIDToString.at(r);
+}
+
+std::string DefaultRegion = "MX3";
+std::map RegionIDToString = {
+ {"0x00000000", "J"},
+ {"0x00000001", "U"}, // UC
+ {"0x00000101", "U2"},
+ {"0x00000201", "U3"},
+ {"0x00000301", "CA"},
+ {"0x00000002", "CEV"},
+ {"0x00000102", "CE7"},
+ {"0x00000003", "CEW"},
+ {"0x00000103", "CEW2"},
+ {"0x00000004", "CN"},
+ {"0x00000005", "KR"},
+ {"0x00000203", "KR3"},
+ {"0x00000006", "E"},
+ {"0x00000206", "E2"},
+ {"0x00000306", "MX3"}, // aka LA (load_sony_driver script)
+ {"0x00000007", "TW"},
+};
+
+// getting data directly from alsa is uncomfortable
+std::pair AudioDeviceInUse() {
+#ifdef DESKTOP
+ return {"Desktop", "Desktop"};
+#endif
+
+ auto r = RunWithOutput("aplay -l");
+
+ std::string prevLine;
+ bool found;
+ for (const auto &line : split(r, "\n")) {
+ if (line == " Subdevices: 0/1") {
+ found = true;
+ break;
+ }
+
+ prevLine = line;
+ }
+
+ if (!found) {
+ return {"No audio playing?", "No audio playing?"};
+ }
+
+ if (prevLine == " Subdevice #0: subdevice #0") {
+ return {"No audio playing?", "No audio playing?"};
+ }
+
+ auto cardDev = split(prevLine, ",");
+ if (cardDev.size() != 2) {
+ DLOG("%s\n", prevLine.c_str());
+ return {"Malformed data?", "Malformed data?"};
+ }
+
+ auto words = split(cardDev[0], ":");
+ if (words.size() != 2) {
+ return {"Malformed card data", "Malformed card data"};
+ }
+ auto card = words[1];
+
+ words = split(cardDev[1], ":");
+ if (words.size() != 2) {
+ return {"Malformed dev data", "Malformed dev data"};
+ }
+ auto dev = words[1];
+
+ return {card, dev};
+}
+
+bool EnableLLUSBDAC() {
+ auto out = RunWithOutput("insmod /system/vendor/unknown321/modules/llusbdac.ko 2>&1");
+ DLOG("%s\n", out.c_str());
+ return out.empty();
+}
+
+bool DisableLLUSBDAC() {
+ auto out = RunWithOutput("rmmod llusbdac 2>&1");
+ DLOG("%s\n", out.c_str());
+ return out.empty();
+}
+
+void rstrip(std::string *s, const char &what) {
+ if (s->empty()) {
+ return;
+ }
+
+ if (s->at(s->length() - 1) == what) {
+ s->erase(s->length() - 1);
+ }
+}
+
+void ExportBookmarks() {
+#ifdef DESKTOP
+ return;
+#endif
+
+ std::vector filenames = {
+ "/cache/bookmark01.json",
+ "/cache/bookmark02.json",
+ "/cache/bookmark03.json",
+ "/cache/bookmark04.json",
+ "/cache/bookmark05.json",
+ "/cache/bookmark06.json",
+ "/cache/bookmark07.json",
+ "/cache/bookmark08.json",
+ "/cache/bookmark09.json",
+ "/cache/bookmark10.json"};
+
+ std::string outdir = "/contents/wampy/bookmarks/";
+ struct stat sb {};
+ if (stat(outdir.c_str(), &sb) != 0) {
+ mkpath(outdir.c_str(), 0755);
+ }
+
+ for (const auto &f : filenames) {
+ // Both dirname() and basename() may modify the contents of path,
+ // so it may be desirable to pass a copy when calling one of these functions.
+ char bn[f.length() + 1];
+ memset(bn, 0, sizeof bn);
+ memcpy(bn, f.c_str(), f.length());
+ auto out = outdir + basename(bn);
+
+ DLOG("in: %s, out %s\n", f.c_str(), out.c_str());
+ std::ifstream src(f, std::ios::binary);
+ std::ofstream dst(out, std::ios::binary);
+
+ dst << src.rdbuf();
+ dst.close();
+ src.close();
+ }
+}
+
+void RemoveLogs() {
+#ifdef DESKTOP
+ return;
+#endif
+
+ std::vector files{};
+ listdir("/contents/wampy/log/", &files, "*");
+ for (const auto &e : files) {
+ DLOG("removing log %s\n", e.fullPath.c_str());
+ std::remove(e.fullPath.c_str());
+ }
+}
diff --git a/src/util/util.h b/src/util/util.h
index d0a7d26..389f656 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -127,4 +127,29 @@ void swapbyte(uint16_t t);
bool exists(const std::string &s);
+std::string GetProduct();
+
+std::string GetModelID();
+
+std::string GetRegionID();
+std::string GetRegionIDStr();
+
+std::pair AudioDeviceInUse();
+
+extern std::string DefaultRegion;
+extern std::map RegionIDToString;
+
+std::string RunWithOutput(const std::string &command);
+
+std::string ReadFile(const std::string &path);
+
+bool DisableLLUSBDAC();
+
+bool EnableLLUSBDAC();
+
+void rstrip(std::string *s, const char &what);
+
+void ExportBookmarks();
+
+void RemoveLogs();
#endif // IMGUITEST_UTIL_H
diff --git a/src/winamp/winamp.cpp b/src/winamp/winamp.cpp
index a92756d..2cb9f05 100644
--- a/src/winamp/winamp.cpp
+++ b/src/winamp/winamp.cpp
@@ -355,15 +355,17 @@ namespace Winamp {
}
}
- DLOG("found %zu numbers out of expected %d\n", pointList.size(), numPoints * 2);
- if (pointList.size() % 2 != 0) {
- DLOG("uneven point count, ignoring\n");
- pointList.clear();
- }
+ if (numPoints != 0) {
+ DLOG("found %zu numbers out of expected %d\n", pointList.size(), numPoints * 2);
+ if (pointList.size() % 2 != 0) {
+ DLOG("uneven point count, ignoring\n");
+ pointList.clear();
+ }
- if (pointList.size() != (numPoints * 2)) {
- DLOG("NumPoints and points found do not match, ignoring\n");
- pointList.clear();
+ if (pointList.size() != (numPoints * 2)) {
+ DLOG("NumPoints and points found do not match, ignoring\n");
+ pointList.clear();
+ }
}
}
@@ -1503,6 +1505,9 @@ namespace Winamp {
gr.clear();
range.AddRanges(io.Fonts->GetGlyphRangesDefault());
range.AddRanges(rangesPunctuation);
+ range.AddChar(ImWchar(0x266a));
+ range.AddChar(ImWchar(0x266b));
+ range.AddChar(ImWchar(0x24c8));
if (fontRanges) {
if (fontRanges->Japanese)
diff --git a/tunings/.gitignore b/tunings/.gitignore
new file mode 100644
index 0000000..0369cb2
--- /dev/null
+++ b/tunings/.gitignore
@@ -0,0 +1,2 @@
+*.tbl
+*.tar.gz
diff --git a/tunings/Makefile b/tunings/Makefile
new file mode 100644
index 0000000..2a294ea
--- /dev/null
+++ b/tunings/Makefile
@@ -0,0 +1,9 @@
+collect:
+ python3 tunings.py
+
+tunings.tar.gz: collect
+ tar -czvf tunings.tar.gz master_volume master_volume_dsd tone_control
+
+all: tunings.tar.gz
+
+.DEFAULT_GOAL := all
diff --git a/tunings/tunings.py b/tunings/tunings.py
new file mode 100644
index 0000000..a8c6cdd
--- /dev/null
+++ b/tunings/tunings.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+
+import os
+import shutil
+from os.path import join
+from hashlib import md5
+
+# $ tree -d -L 1 --noreport
+# .
+# ├── 1walkmanOne
+# ├── di-avlos
+# ├── eclipse
+# ├── nw-a30
+# ├── nw-a40
+# ├── nw-a50
+# ├── nw-wm1a
+# ├── seasons
+# └── zx-300
+
+# nw-30 tables are smaller and therefore fail on everything else
+mvt_size = 84950
+dsd_size = 13076
+tct_size = 2888
+
+rootdir = "/media/ssd/dev/nw/tunings"
+hashes = {}
+filelist = []
+for root, dirs, files in os.walk(rootdir):
+ for name in files:
+ if name[-4:] == ".tbl":
+ filelist.append(join(root, name))
+
+for file in filelist:
+ with open(file, "rb") as f:
+ d = f.read()
+ h = md5(d).hexdigest()
+ if h not in hashes:
+ hashes[h] = []
+ hashes[h].append(file)
+
+outdirs = {}
+for k, v in hashes.items():
+ last = sorted(v)[-1]
+ if "ambgain" not in last and "ncgain" not in last:
+ fname = os.path.split(last)[1]
+ model = os.path.split(os.path.split(last)[0])[1]
+ newname = os.path.splitext(fname)[0] + "_" + model + os.path.splitext(fname)[1]
+ s = os.stat(last)
+ if newname[:2] == "tc":
+ if s.st_size != tct_size:
+ continue
+ newname = os.path.join("tone_control", newname)
+ elif newname[:6] == "ov_dsd":
+ if s.st_size != dsd_size:
+ continue
+ newname = os.path.join("master_volume_dsd", newname)
+ elif newname[:2] == "ov":
+ if s.st_size != mvt_size:
+ continue
+ newname = os.path.join("master_volume", newname)
+ outdirs[last] = newname
+
+os.makedirs("tone_control", exist_ok=True)
+os.makedirs("master_volume_dsd", exist_ok=True)
+os.makedirs("master_volume", exist_ok=True)
+
+for k, v in outdirs.items():
+ shutil.copy(k, v)
diff --git a/tunings/uniq.txt b/tunings/uniq.txt
new file mode 100644
index 0000000..96a27f1
--- /dev/null
+++ b/tunings/uniq.txt
@@ -0,0 +1,94 @@
+05858758014f7021d422c0a796fd2a01 ./eclipse2/tbl/ov_dsd_1280.tbl
+05858758014f7021d422c0a796fd2a01 ./nw-a40/ov_dsd_1290.tbl
+05858758014f7021d422c0a796fd2a01 ./nw-a50/ov_dsd_1291.tbl
+05858758014f7021d422c0a796fd2a01 ./nw-wm1a/ov_dsd_1280.tbl
+05858758014f7021d422c0a796fd2a01 ./walkmanOne/mod/gain/gain_n/ov_dsd_127x_cew.tbl
+05858758014f7021d422c0a796fd2a01 ./walkmanOne/mod/gain/gain_n/ov_dsd_127x.tbl
+05858758014f7021d422c0a796fd2a01 ./walkmanOne/system/audio_dac/ov_dsd_127x_cew.tbl
+05858758014f7021d422c0a796fd2a01 ./walkmanOne/system/audio_dac/ov_dsd_127x.tbl
+05858758014f7021d422c0a796fd2a01 ./walkmanOne/system/audio_dac/ov_dsd_1280.tbl
+05858758014f7021d422c0a796fd2a01 ./zx-300/ov_dsd_1290.tbl
+
+05bcde3d052582b0451817f7ebcd775b ./nw-a40/tc_1290.tbl
+05bcde3d052582b0451817f7ebcd775b ./nw-a50/tc_1291.tbl
+05bcde3d052582b0451817f7ebcd775b ./zx-300/tc_1290.tbl
+
+12399a1deb62306b9135a7f70e20fb25 ./nw-a30/tc_127x.tbl
+
+142c8a330a7b7a4d90b2581d72ce3d49 ./eclipse2/tbl/ov_dsd_127x.tbl
+142c8a330a7b7a4d90b2581d72ce3d49 ./nw-a40/ov_dsd_1288.tbl
+142c8a330a7b7a4d90b2581d72ce3d49 ./nw-wm1a/ov_dsd_127x.tbl
+142c8a330a7b7a4d90b2581d72ce3d49 ./walkmanOne/mod/gain/gain_l/ov_dsd_127x_cew.tbl
+142c8a330a7b7a4d90b2581d72ce3d49 ./walkmanOne/mod/gain/gain_l/ov_dsd_127x.tbl
+142c8a330a7b7a4d90b2581d72ce3d49 ./zx-300/ov_dsd_1288.tbl
+
+22d850f26b625cf3be98fe1e9638d613 ./nw-a30/ov_dsd_127x_cew.tbl
+
+39a60adc7240be8deab95c39becf4419 ./eclipse2/tbl/ov_127x.tbl
+39a60adc7240be8deab95c39becf4419 ./nw-a40/ov_1288.tbl
+39a60adc7240be8deab95c39becf4419 ./nw-wm1a/ov_127x.tbl
+39a60adc7240be8deab95c39becf4419 ./walkmanOne/mod/gain/gain_l/ov_127x_cew.tbl
+39a60adc7240be8deab95c39becf4419 ./walkmanOne/mod/gain/gain_l/ov_127x.tbl
+39a60adc7240be8deab95c39becf4419 ./zx-300/ov_1288.tbl
+
+4ab93bdcd509c3cc4bc0157d92a3eaa0 ./nw-a40/ov_1290_cew.tbl
+4ab93bdcd509c3cc4bc0157d92a3eaa0 ./nw-a50/ov_1291_cew.tbl
+4ab93bdcd509c3cc4bc0157d92a3eaa0 ./zx-300/ov_1290_cew.tbl
+
+4e99bb279d17906084d8701a088ae5b4 ./nw-a30/ov_1280_cew.tbl
+
+5793c7c5ec554044e19cff2f3d02a9e0 ./nw-a30/ov_dsd_1280.tbl
+
+5bf930c0209cbe4b7ba871e74e6b2b30 ./eclipse2/tbl/ov_1280.tbl
+5bf930c0209cbe4b7ba871e74e6b2b30 ./nw-wm1a/ov_1280.tbl
+5bf930c0209cbe4b7ba871e74e6b2b30 ./walkmanOne/system/audio_dac/ov_1280.tbl
+
+741e6d9167445f7d1842dbe57ae8d8ce ./eclipse2/tbl/ov_dsd_1280_cew.tbl
+741e6d9167445f7d1842dbe57ae8d8ce ./nw-a40/ov_dsd_1290_cew.tbl
+741e6d9167445f7d1842dbe57ae8d8ce ./nw-a50/ov_dsd_1291_cew.tbl
+741e6d9167445f7d1842dbe57ae8d8ce ./nw-wm1a/ov_dsd_1280_cew.tbl
+741e6d9167445f7d1842dbe57ae8d8ce ./walkmanOne/system/audio_dac/ov_dsd_1280_cew.tbl
+741e6d9167445f7d1842dbe57ae8d8ce ./zx-300/ov_dsd_1290_cew.tbl
+
+84614ebdf1a8f85e17478f9de6863748 ./nw-a30/ov_dsd_1280_cew.tbl
+
+864e0fb3872b6ccbb8f16f64268c0c96 ./eclipse2/tbl/ov_127x_cew.tbl
+864e0fb3872b6ccbb8f16f64268c0c96 ./nw-a40/ov_1288_cew.tbl
+864e0fb3872b6ccbb8f16f64268c0c96 ./nw-wm1a/ov_127x_cew.tbl
+864e0fb3872b6ccbb8f16f64268c0c96 ./zx-300/ov_1288_cew.tbl
+
+86dc28f9379d74c532de21954fd0a4f1 ./eclipse2/tbl/ov_1280_cew.tbl
+86dc28f9379d74c532de21954fd0a4f1 ./nw-wm1a/ov_1280_cew.tbl
+86dc28f9379d74c532de21954fd0a4f1 ./walkmanOne/system/audio_dac/ov_1280_cew.tbl
+
+9a73d2e23ec8b891bdb48b0636cd2154 ./nw-a30/ov_127x_cew.tbl
+
+a3bee4f2a78d96a720b4ebe66d326894 ./nw-a30/tc_1280.tbl
+
+bb5ccae7b1a147b3507cb787cda522a6 ./nw-a40/ov_1290.tbl
+bb5ccae7b1a147b3507cb787cda522a6 ./nw-a50/ov_1291.tbl
+bb5ccae7b1a147b3507cb787cda522a6 ./walkmanOne/mod/gain/gain_n/ov_127x_cew.tbl
+bb5ccae7b1a147b3507cb787cda522a6 ./walkmanOne/mod/gain/gain_n/ov_127x.tbl
+bb5ccae7b1a147b3507cb787cda522a6 ./walkmanOne/system/audio_dac/ov_127x_cew.tbl
+bb5ccae7b1a147b3507cb787cda522a6 ./walkmanOne/system/audio_dac/ov_127x.tbl
+bb5ccae7b1a147b3507cb787cda522a6 ./zx-300/ov_1290.tbl
+
+c12175bb3e3ac48869661fbc4b048621 ./nw-a30/ov_127x.tbl
+
+d2a191ab97398bbcadb2b2f6c54d3574 ./nw-a30/ov_1280.tbl
+
+f678cb93161a8428ca41fb0d4dc104db ./eclipse2/tbl/tc_127x.tbl
+f678cb93161a8428ca41fb0d4dc104db ./eclipse2/tbl/tc_1280.tbl
+f678cb93161a8428ca41fb0d4dc104db ./nw-a40/tc_1288.tbl
+f678cb93161a8428ca41fb0d4dc104db ./nw-wm1a/tc_127x.tbl
+f678cb93161a8428ca41fb0d4dc104db ./nw-wm1a/tc_1280.tbl
+f678cb93161a8428ca41fb0d4dc104db ./walkmanOne/system/audio_dac/tc_127x.tbl
+f678cb93161a8428ca41fb0d4dc104db ./walkmanOne/system/audio_dac/tc_1280.tbl
+f678cb93161a8428ca41fb0d4dc104db ./zx-300/tc_1288.tbl
+
+f81fb03fc637922f565bcd01bd5041bb ./nw-a30/ov_dsd_127x.tbl
+
+ff5fe61bd181f4e5655fd7a313797258 ./eclipse2/tbl/ov_dsd_127x_cew.tbl
+ff5fe61bd181f4e5655fd7a313797258 ./nw-a40/ov_dsd_1288_cew.tbl
+ff5fe61bd181f4e5655fd7a313797258 ./nw-wm1a/ov_dsd_127x_cew.tbl
+ff5fe61bd181f4e5655fd7a313797258 ./zx-300/ov_dsd_1288_cew.tbl
diff --git a/uninstaller/run.sh b/uninstaller/run.sh
index 1081915..d271a3a 100644
--- a/uninstaller/run.sh
+++ b/uninstaller/run.sh
@@ -60,8 +60,11 @@ uninstall() {
log "no valid libqeglfs backup found, leaving as is"
fi
- log "removing skins, licenses, qr code"
+ log "removing skins, licenses, qr code, tunings"
busybox rm -rf ${VENDOR}/usr/share/${BINARY}/
+
+ log "removing modules"
+ busybox rm -rf ${VENDOR}/modules/
}
log "uninstaller for $(cat product_info)"