From f08be7db824cbac0a1aeafaac1f603a29d743c38 Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 28 Sep 2024 08:16:12 +0300 Subject: [PATCH 01/59] * Android: UI: popup suggestions where applicable --- .../github/stsaz/phiola/ConvertActivity.java | 9 +++ .../com/github/stsaz/phiola/ExplorerMenu.java | 71 +++++++++++++++++++ .../github/stsaz/phiola/ListSaveActivity.java | 6 +- .../github/stsaz/phiola/SettingsActivity.java | 22 +++++- .../java/com/github/stsaz/phiola/VarMenu.java | 37 ++++++++++ 5 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 android/phiola/src/main/java/com/github/stsaz/phiola/ExplorerMenu.java create mode 100644 android/phiola/src/main/java/com/github/stsaz/phiola/VarMenu.java diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/ConvertActivity.java b/android/phiola/src/main/java/com/github/stsaz/phiola/ConvertActivity.java index 6cff157..1464762 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/ConvertActivity.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/ConvertActivity.java @@ -15,6 +15,8 @@ public class ConvertActivity extends AppCompatActivity { private static final String TAG = "phiola.ConvertActivity"; Core core; + private VarMenu varmenu; + private ExplorerMenu explorer; private long length_msec; private ConvertBinding b; @@ -24,6 +26,13 @@ protected void onCreate(Bundle savedInstanceState) { b = ConvertBinding.inflate(getLayoutInflater()); setContentView(b.getRoot()); + varmenu = new VarMenu(this); + explorer = new ExplorerMenu(this); + b.eOutDir.setOnClickListener(v -> explorer.show(b.eOutDir)); + b.eOutName.setOnClickListener(v -> varmenu.show(b.eOutName, new String[]{ + "@filename", "@album", "@artist", "@title", "@tracknumber", + })); + ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item , CoreSettings.conv_format_display); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/ExplorerMenu.java b/android/phiola/src/main/java/com/github/stsaz/phiola/ExplorerMenu.java new file mode 100644 index 0000000..daa731a --- /dev/null +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/ExplorerMenu.java @@ -0,0 +1,71 @@ +/** phiola/Android +2024, Simon Zolin */ + +package com.github.stsaz.phiola; + +import android.app.Activity; +import android.view.MenuItem; +import android.widget.EditText; +import android.widget.PopupMenu; + +import java.util.ArrayList; +import java.util.Arrays; + +public class ExplorerMenu { + private Core core; + private Activity parent; + private EditText ctl; + private String path; + private String[] files; + private boolean up_dir; + + ExplorerMenu(Activity parent) { + core = Core.getInstance(); + this.parent = parent; + } + + void show(EditText v) { + ctl = v; + String[] rows; + + path = ctl.getText().toString(); + if (path.isEmpty()) { + rows = core.storage_paths; + files = core.storage_paths; + up_dir = false; + + } else { + UtilNative.Files f = core.util.dirList(path, 0); + ArrayList a = new ArrayList<>(Arrays.asList(f.display_rows).subList(0, f.n_directories)); + rows = a.toArray(new String[0]); + files = f.file_names; + up_dir = true; + } + + PopupMenu m = new PopupMenu(parent, v); + m.setOnMenuItemClickListener(this::menu_click); + int i = 0; + + if (up_dir) + m.getMenu().add(0, i++, 0, parent.getString(R.string.explorer_up)); + + for (String s : rows) { + m.getMenu().add(0, i++, 0, s); + } + m.show(); + } + + private boolean menu_click(MenuItem item) { + int i = item.getItemId(); + String s; + if (i == 0 && up_dir) { + s = Util.path_split2(path)[0]; + } else { + if (up_dir) + i--; + s = files[i]; + } + ctl.setText(s); + return true; + } +} \ No newline at end of file diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/ListSaveActivity.java b/android/phiola/src/main/java/com/github/stsaz/phiola/ListSaveActivity.java index a8d39d4..aac2ca0 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/ListSaveActivity.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/ListSaveActivity.java @@ -13,7 +13,8 @@ public class ListSaveActivity extends AppCompatActivity { private static final String TAG = "phiola.ListSaveActivity"; - Core core; + private Core core; + private ExplorerMenu explorer; private ListSaveBinding b; protected void onCreate(Bundle savedInstanceState) { @@ -21,7 +22,10 @@ protected void onCreate(Bundle savedInstanceState) { b = ListSaveBinding.inflate(getLayoutInflater()); setContentView(b.getRoot()); + explorer = new ExplorerMenu(this); + b.bSave.setOnClickListener((v) -> save()); + b.eDir.setOnClickListener(v -> explorer.show(b.eDir)); core = Core.getInstance(); load(); diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/SettingsActivity.java b/android/phiola/src/main/java/com/github/stsaz/phiola/SettingsActivity.java index 9d43a3c..c5f3df2 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/SettingsActivity.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/SettingsActivity.java @@ -18,6 +18,7 @@ public class SettingsActivity extends AppCompatActivity { private static final String TAG = "phiola.SettingsActivity"; private Core core; private SettingsBinding b; + private ExplorerMenu explorer; @Override protected void onCreate(Bundle savedInstanceState) { @@ -29,6 +30,20 @@ protected void onCreate(Bundle savedInstanceState) { if (actionBar != null) actionBar.setDisplayHomeAsUpEnabled(true); + explorer = new ExplorerMenu(this); + + play_init(); + + b.eDataDir.setOnClickListener(v -> explorer.show(b.eDataDir)); + b.eQuickMoveDir.setOnClickListener(v -> explorer.show(b.eQuickMoveDir)); + + rec_init(); + + core = Core.getInstance(); + load(); + } + + private void play_init() { b.sbPlayAutoSkip.setMax(auto_skip_progress(200)); b.sbPlayAutoSkip.setOnSeekBarChangeListener(new SBOnSeekBarChangeListener() { @Override @@ -58,6 +73,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } } }); + } + + private void rec_init() { + b.eRecDir.setOnClickListener(v -> explorer.show(b.eRecDir)); ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item , new String[] { @@ -131,9 +150,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { b.eRecGain.setEnabled(false); b.swRecExclusive.setEnabled(false); } - - core = Core.getInstance(); - load(); } @Override diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/VarMenu.java b/android/phiola/src/main/java/com/github/stsaz/phiola/VarMenu.java new file mode 100644 index 0000000..e249d1a --- /dev/null +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/VarMenu.java @@ -0,0 +1,37 @@ +/** phiola/Android +2024, Simon Zolin */ + +package com.github.stsaz.phiola; + +import android.app.Activity; +import android.view.MenuItem; +import android.widget.EditText; +import android.widget.PopupMenu; + +public class VarMenu { + private Activity parent; + private EditText ctl; + private String[] rows; + + VarMenu(Activity parent) { + this.parent = parent; + } + + void show(EditText v, String[] rows) { + ctl = v; + this.rows = rows; + + PopupMenu m = new PopupMenu(parent, v); + m.setOnMenuItemClickListener(this::menu_click); + int i = 0; + for (String s : rows) { + m.getMenu().add(0, i++, 0, s); + } + m.show(); + } + + private boolean menu_click(MenuItem item) { + ctl.getText().append(rows[item.getItemId()]); + return true; + } +} From e0e6db4d21908aee11f5bdf84f7fda960482758a Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 28 Sep 2024 08:16:12 +0300 Subject: [PATCH 02/59] * Android: seek forward on long pressing Next button --- .../com/github/stsaz/phiola/MainActivity.java | 26 +++++++++++++++++-- .../com/github/stsaz/phiola/Settings.java | 7 +++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/MainActivity.java b/android/phiola/src/main/java/com/github/stsaz/phiola/MainActivity.java index 9259ec3..d29a8cc 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/MainActivity.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/MainActivity.java @@ -331,9 +331,17 @@ private void init_ui() { b.bplay.setOnClickListener((v) -> play_pause_click()); - b.bnext.setOnClickListener((v) -> trackctl.next()); - b.bprev.setOnClickListener((v) -> trackctl.prev()); + b.bprev.setOnLongClickListener((v) -> { + seek_back(); + return true; + }); + + b.bnext.setOnClickListener((v) -> trackctl.next()); + b.bnext.setOnLongClickListener((v) -> { + seek_fwd(); + return true; + }); b.bexplorer.setOnClickListener((v) -> explorer_click()); @@ -877,6 +885,20 @@ private void seek(int percent) { trackctl.seek((total_dur_msec / 1000) * percent / 100 * 1000); } + private void seek_back() { + int percent = b.seekbar.getProgress() - core.setts.play_seek_back_percent; + if (percent < 0) + percent = 0; + seek(percent); + } + + private void seek_fwd() { + int percent = b.seekbar.getProgress() + core.setts.play_seek_fwd_percent; + if (percent >= 100) + percent = 99; + seek(percent); + } + // [Playing] // [PLA,STP,REC|RPA,CON] private String state_flags(int st) { diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java b/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java index 21eb38c..0cffa38 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java @@ -67,6 +67,8 @@ void auto_skip_tail_set(String s) { core.phiola.playCmd(Phiola.PC_AUTO_SKIP_TAIL, n); } + int play_seek_fwd_percent, play_seek_back_percent; + String rec_path; // directory for recordings String rec_name_template; String rec_enc, rec_fmt; @@ -284,6 +286,11 @@ void normalize() { if (trash_dir.isEmpty()) trash_dir = "Trash"; + if (play_seek_fwd_percent <= 0 || play_seek_fwd_percent > 50) + play_seek_fwd_percent = 5; + if (play_seek_back_percent <= 0 || play_seek_back_percent > 50) + play_seek_back_percent = 5; + normalize_rec(); normalize_convert(); } From e6643f97b68a90a4e21403ac97abd02a4be63271 Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 28 Sep 2024 08:16:12 +0300 Subject: [PATCH 03/59] + Android: Convert: new setting: `Sample Format` --- .../com/github/stsaz/phiola/ConvertActivity.java | 7 +++++++ .../main/java/com/github/stsaz/phiola/Phiola.java | 1 + .../java/com/github/stsaz/phiola/Settings.java | 14 ++++++++++++++ android/phiola/src/main/res/layout/convert.xml | 12 ++++++++++++ android/phiola/src/main/res/values/strings.xml | 1 + src/jni/queue.h | 6 ++++++ 6 files changed, 41 insertions(+) diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/ConvertActivity.java b/android/phiola/src/main/java/com/github/stsaz/phiola/ConvertActivity.java index 1464762..4429c0f 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/ConvertActivity.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/ConvertActivity.java @@ -61,6 +61,11 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { }); b.bUntilSetCur.setOnClickListener((v) -> pos_set_cur(false)); + adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item + , CoreSettings.conv_sample_formats_str); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + b.spSampleFormat.setAdapter(adapter); + b.sbAacQ.setMax(aac_q_progress(5)); b.sbAacQ.setOnSeekBarChangeListener(new SBOnSeekBarChangeListener() { @Override @@ -89,6 +94,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { }); b.swCopy.setOnCheckedChangeListener((v, checked) -> { + b.spSampleFormat.setEnabled(!checked); b.eSampleRate.setEnabled(!checked); b.sbAacQ.setEnabled(!checked); @@ -261,6 +267,7 @@ private void convert() { Phiola.ConvertParams p = new Phiola.ConvertParams(); p.from_msec = b.eFrom.getText().toString(); p.to_msec = b.eUntil.getText().toString(); + p.sample_format = CoreSettings.conv_sample_formats[b.spSampleFormat.getSelectedItemPosition()]; p.sample_rate = core.str_to_uint(b.eSampleRate.getText().toString(), 0); p.aac_quality = core.setts.conv_aac_quality; p.opus_quality = core.setts.conv_opus_quality; diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/Phiola.java b/android/phiola/src/main/java/com/github/stsaz/phiola/Phiola.java index 824d405..9c57c8a 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/Phiola.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/Phiola.java @@ -70,6 +70,7 @@ static class ConvertParams { String out_name; String from_msec, to_msec; + int sample_format; int sample_rate; int aac_quality; int opus_quality; diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java b/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java index 0cffa38..9327104 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java @@ -99,6 +99,20 @@ void auto_skip_tail_set(String s) { boolean conv_copy; boolean conv_file_date_preserve; boolean conv_new_add_list; + static final int[] conv_sample_formats = { + 0, + 8, // PHI_PCM_8 + 16, // PHI_PCM_16 + 24, // PHI_PCM_24 + 32, // PHI_PCM_32 + }; + static final String[] conv_sample_formats_str = { + "As Source", + "int8", + "int16", + "int24", + "int32", + }; static final int conv_encoders[] = { Phiola.AF_AAC_LC, Phiola.AF_AAC_HE, diff --git a/android/phiola/src/main/res/layout/convert.xml b/android/phiola/src/main/res/layout/convert.xml index dae4b14..c3f2fe4 100644 --- a/android/phiola/src/main/res/layout/convert.xml +++ b/android/phiola/src/main/res/layout/convert.xml @@ -132,6 +132,18 @@ android:text="@string/conv_filters" android:textColor="@color/sett_group" /> + + + End Audio Position: Filters + Sample Format Sample rate (Hz): Encoding diff --git a/src/jni/queue.h b/src/jni/queue.h index 28d3749..65f26bb 100644 --- a/src/jni/queue.h +++ b/src/jni/queue.h @@ -443,6 +443,7 @@ Java_com_github_stsaz_phiola_Phiola_quConvertBegin(JNIEnv *env, jobject thiz, jl struct phi_track_conf conf = { .ifile.preserve_date = !!(flags & F_DATE_PRESERVE), .stream_copy = !!(flags & F_COPY), + .oaudio.format.format = jni_obj_int(jconf, jni_field_int(jc_conf, "sample_format")), .oaudio.format.rate = jni_obj_int(jconf, jni_field_int(jc_conf, "sample_rate")), .aac.quality = jni_obj_int(jconf, jni_field_int(jc_conf, "aac_quality")), .vorbis.quality = jni_obj_int(jconf, jni_field_int(jc_conf, "vorbis_quality")), @@ -470,14 +471,19 @@ Java_com_github_stsaz_phiola_Phiola_quConvertBegin(JNIEnv *env, jobject thiz, jl struct phi_queue_entry *qe; for (i = 0; !!(qe = x->queue.at(q, i)); i++) { struct phi_track_conf *c = &qe->conf; + c->ifile.preserve_date = conf.ifile.preserve_date; c->seek_msec = conf.seek_msec; c->until_msec = conf.until_msec; c->stream_copy = conf.stream_copy; + + c->oaudio.format.format = conf.oaudio.format.format; c->oaudio.format.rate = conf.oaudio.format.rate; + c->aac.quality = conf.aac.quality; c->vorbis.quality = conf.vorbis.quality; c->opus.bitrate = conf.opus.bitrate; + c->ofile.name = ffsz_dup(ofn); c->ofile.name_tmp = 1; c->ofile.overwrite = conf.ofile.overwrite; From 80ee799cf25a07afb48f6d9fc89ac7fa76dc584c Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 28 Sep 2024 08:16:12 +0300 Subject: [PATCH 04/59] * .ogg read: support chained Opus stream --- doc/arch/arch.md | 18 ++++++++ src/acodec/opus-dec.h | 25 +++++++---- src/format/ogg-read.h | 16 +++++-- src/format/opus-meta.h | 94 ++++++++++++++++++++++++++++-------------- src/track.h | 1 + test.sh | 13 ++++++ 6 files changed, 123 insertions(+), 44 deletions(-) diff --git a/doc/arch/arch.md b/doc/arch/arch.md index 7222af3..237ad5e 100644 --- a/doc/arch/arch.md +++ b/doc/arch/arch.md @@ -85,6 +85,24 @@ Key factor: 'fmt' and 'dec' know nothing about `abs_seek`; `AS` - audio offset for the current .cue track (`abs_seek`). +## .ogg chained stream reading + +```C +ogg.wr opus-meta opus.dec +======================================= +... +* detects new stream +RST=1 -hdr-> ?RST + *hdr + -> *tag -hdr-> ?RST + RST=0 <- *skip + -> *data -tag-> *skip + -data-> *decode +``` + +`RST` flag is set when a new logical stream is detected; it signifies that the next 2 packets are opus-info and vorbis-tags. + + ## .ogg write Case 1. Add packets normally with known audio position of every packet. diff --git a/src/acodec/opus-dec.h b/src/acodec/opus-dec.h index a448777..ed378a0 100644 --- a/src/acodec/opus-dec.h +++ b/src/acodec/opus-dec.h @@ -17,14 +17,6 @@ static void* opus_open(phi_track *t) return PHI_OPEN_ERR; struct opus_dec *o = phi_track_allocT(t, struct opus_dec); - - if (ffopus_open(&o->opus)) { - errlog(t, "ffopus_open(): %s", ffopus_errstr(&o->opus)); - phi_track_free(t, o); - return PHI_OPEN_ERR; - } - - o->prev_page_pos = ~0ULL; return o; } @@ -46,7 +38,7 @@ static const char* opus_pkt_mode(const char *d) static int opus_in_decode(void *ctx, phi_track *t) { - enum { R_HDR, R_TAGS, R_DATA1, R_DATA }; + enum { R_INIT, R_HDR, R_TAGS, R_DATA1, R_DATA }; struct opus_dec *o = ctx; int r; const char *opus_mode = NULL; @@ -63,6 +55,15 @@ static int opus_in_decode(void *ctx, phi_track *t) ffstr out; for (;;) { switch (o->state) { + case R_INIT: + if (ffopus_open(&o->opus)) { + errlog(t, "ffopus_open(): %s", ffopus_errstr(&o->opus)); + return PHI_ERR; + } + o->prev_page_pos = ~0ULL; + o->state = R_HDR; + // fallthrough + case R_HDR: case R_TAGS: if (!(t->chain_flags & PHI_FFWD)) @@ -84,6 +85,12 @@ static int opus_in_decode(void *ctx, phi_track *t) // fallthrough case R_DATA: + if (t->audio.ogg_reset) { + ffopus_close(&o->opus); + ffmem_zero_obj(o); + continue; + } + if (t->audio.seek_req) { // a new seek request is received, pass control to UI module o->reset_decoder = 1; diff --git a/src/format/ogg-read.h b/src/format/ogg-read.h index 58c0b1c..f25134e 100644 --- a/src/format/ogg-read.h +++ b/src/format/ogg-read.h @@ -7,6 +7,7 @@ struct ogg_r { oggread og; ffstr in; void *trk; + uint page_serial; uint sample_rate; uint state; uint stmcopy :1; @@ -133,15 +134,24 @@ static int ogg_read(void *ctx, phi_track *t) } return PHI_MORE; - case OGGREAD_HEADER: - case OGGREAD_DATA: + case OGGREAD_HEADER: { + const struct oggread_info *i = oggread_info(&o->og); if (o->state == I_HDR) { o->state = I_INFO; - const struct oggread_info *i = oggread_info(&o->og); t->audio.total = (i->total_samples) ? i->total_samples : ~0ULL; if (0 != add_decoder(o, t, t->data_out)) return PHI_ERR; + } else { + if (o->page_serial != i->serial) { + dbglog(t, "new logical stream"); + t->audio.ogg_reset = 1; + } } + o->page_serial = i->serial; + goto data; + } + + case OGGREAD_DATA: goto data; case OGGREAD_DONE: diff --git a/src/format/opus-meta.h b/src/format/opus-meta.h index 0aadcd8..61e5e64 100644 --- a/src/format/opus-meta.h +++ b/src/format/opus-meta.h @@ -4,6 +4,7 @@ #include #include +extern const phi_meta_if phi_metaif; extern int vorbistag_read(phi_track *t, ffstr vc); struct opus_hdr { @@ -52,6 +53,7 @@ static int opuscomment_read(ffstr pkt, ffstr *body) struct opusmeta { uint state; + uint reset :1; ffvec hdr; ffstr tags; }; @@ -72,43 +74,71 @@ static void opusmeta_close(void *ctx, phi_track *t) static int opusmeta_read(void *ctx, phi_track *t) { struct opusmeta *o = ctx; - switch (o->state) { - case 0: { - struct opus_info info; - if (0 != opusinfo_read(&info, t->data_in)) { - errlog(t, "opusinfo_read"); - return PHI_ERR; + for (;;) { + switch (o->state) { + case 0: { + struct opus_info info; + if (0 != opusinfo_read(&info, t->data_in)) { + errlog(t, "opusinfo_read"); + return PHI_ERR; + } + t->audio.decoder = "Opus"; + + if (o->reset) { + if (!(info.channels == t->audio.format.channels + && info.rate == t->audio.format.rate)) { + errlog(t, "changing the audio format on-the-fly is not supported"); + return PHI_ERR; + } + t->meta_changed = 1; + } + + struct phi_af f = { + .format = PHI_PCM_FLOAT32, + .channels = info.channels, + .rate = info.rate, + }; + t->audio.format = f; + + ffvec_addstr(&o->hdr, &t->data_in); + o->state = 1; + return PHI_MORE; } - t->audio.decoder = "Opus"; - struct phi_af f = { - .format = PHI_PCM_FLOAT32, - .channels = info.channels, - .rate = info.rate, - }; - t->audio.format = f; - - ffvec_addstr(&o->hdr, &t->data_in); - o->state = 1; - return PHI_MORE; - } - case 1: { - ffstr vc; - if (0 != opuscomment_read(t->data_in, &vc)) { - errlog(t, "opuscomment_read"); - return PHI_ERR; + case 1: { + ffstr vc; + if (0 != opuscomment_read(t->data_in, &vc)) { + errlog(t, "opuscomment_read"); + return PHI_ERR; + } + vorbistag_read(t, vc); + + o->tags = t->data_in; + t->data_out = *(ffstr*)&o->hdr; + o->state = 2; + return PHI_DATA; } - vorbistag_read(t, vc); - o->tags = t->data_in; - t->data_out = *(ffstr*)&o->hdr; - o->state = 2; - return PHI_DATA; - } + case 2: + t->audio.ogg_reset = 0; + ffvec_free(&o->hdr); + o->state = 3; + t->data_out = o->tags; + return (t->conf.info_only) ? PHI_LASTOUT + : (t->chain_flags & PHI_FFIRST) ? PHI_DONE + : PHI_OK; + + case 3: + if (t->audio.ogg_reset) { + phi_metaif.destroy(&t->meta); + o->reset = 1; + o->state = 0; + continue; + } + t->data_out = t->data_in; + return (t->chain_flags & PHI_FFIRST) ? PHI_DONE : PHI_OK; + } } - - t->data_out = o->tags; - return (t->conf.info_only) ? PHI_LASTOUT : PHI_DONE; } const phi_filter phi_opusmeta_read = { diff --git a/src/track.h b/src/track.h index 88209ff..ada2a57 100644 --- a/src/track.h +++ b/src/track.h @@ -165,6 +165,7 @@ struct phi_track { uint64 pos, total; // samples; -1:unset/unknown int64 seek; // >0:msec; -1:unset uint seek_req :1; // New seek request is received (UI -> fmt.read) + uint ogg_reset :1; // ogg.read -> opus.meta uint bitrate; double gain_db; // Audio gain/attenuation double maxpeak_db; diff --git a/test.sh b/test.sh index f282eb0..45540e4 100644 --- a/test.sh +++ b/test.sh @@ -208,6 +208,18 @@ test_seek() { ./phiola i -peaks -s 1 fm_wv.wv | grep '48,000 total' } +test_ogg() { + if ! test -f pl.wav ; then + ./phiola rec -rate 48000 -o pl.wav -f -u 2 + fi + + # Chained OGG(Opus) stream + ./phiola co pl.wav -o ogg1.opus -f + ./phiola co pl.wav -o ogg2.opus -f + cat ogg1.opus ogg2.opus >ogg3.opus + ./phiola pl ogg3.opus +} + test_convert_af() { O=co_wav_i24.wav ; ./phiola co co.wav -af int24 -f -o $O ; ./phiola i $O | grep 'int24' ; ./phiola pl $O O=co_wav_mono.wav ; ./phiola co co.wav -ch 1 -f -o $O ; ./phiola i $O | grep 'mono' ; ./phiola pl $O @@ -664,6 +676,7 @@ TESTS=( info until seek + ogg copy meta danorm From 16cd4d7375a2b7e0db4bf43c43e9fe0d416c597e Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 28 Sep 2024 08:16:12 +0300 Subject: [PATCH 05/59] - couldn't convert from .ogg(Vorbis) to .ogg(Opus) --- src/track.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/track.h b/src/track.h index ada2a57..dd64c3d 100644 --- a/src/track.h +++ b/src/track.h @@ -97,7 +97,7 @@ struct phi_filter { char name[16]; }; -#define MAX_FILTERS 20 +#define MAX_FILTERS 24 struct filter { void *obj; From d0648ddf78a2d4cf3bb94114552cea0cd09da89b Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 28 Sep 2024 08:16:12 +0300 Subject: [PATCH 06/59] minor --- .../src/main/java/com/github/stsaz/phiola/Explorer.java | 4 ---- src/acodec/alib3-bridge/opus-dec-if.h | 6 ++++-- src/afilter/soxr-conv.h | 4 +++- src/core/track.c | 7 ++++--- src/format/ogg-write.h | 2 +- src/jni/convert.h | 2 ++ src/net/http.c | 1 - src/track.h | 1 + 8 files changed, 15 insertions(+), 12 deletions(-) diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/Explorer.java b/android/phiola/src/main/java/com/github/stsaz/phiola/Explorer.java index 6f607e4..382c8aa 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/Explorer.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/Explorer.java @@ -11,10 +11,6 @@ import androidx.core.app.ActivityCompat; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; - class Explorer { private static final String TAG = "phiola.Explorer"; private final Core core; diff --git a/src/acodec/alib3-bridge/opus-dec-if.h b/src/acodec/alib3-bridge/opus-dec-if.h index bdb7b1b..859c941 100644 --- a/src/acodec/alib3-bridge/opus-dec-if.h +++ b/src/acodec/alib3-bridge/opus-dec-if.h @@ -155,8 +155,10 @@ int ffopus_decode(ffopus *o, ffstr *input, ffstr *output, uint64 *pos) float *pcm = (void*)o->pcmbuf.ptr; r = opus_decode_f(o->dec, input->ptr, input->len, pcm); - if (r < 0) - return ERR(o, r); + if (r < 0) { + o->err = r; + return FFOPUS_RWARN; + } input->len = 0; ffstr_set(output, pcm, r * o->info.channels * sizeof(float)); diff --git a/src/afilter/soxr-conv.h b/src/afilter/soxr-conv.h index 8847c44..09f1096 100644 --- a/src/afilter/soxr-conv.h +++ b/src/afilter/soxr-conv.h @@ -61,8 +61,10 @@ static int soxr_conv(void *ctx, phi_track *t) if (0 != (val = ffsoxr_create(&c->soxr, &inpcm, &outpcm)) || (core->conf.log_level >= PHI_LOG_DEBUG)) { log_pcmconv(val, &inpcm, &outpcm, t); - if (val != 0) + if (val != 0) { + t->error = PHI_E_ACONV; return PHI_ERR; + } } c->state = 3; diff --git a/src/core/track.c b/src/core/track.c index 4510610..1459d37 100644 --- a/src/core/track.c +++ b/src/core/track.c @@ -214,8 +214,9 @@ static int trk_filter_add(phi_track *t, const phi_filter *iface, uint pos) t->conveyor.n_active++; char buf[200]; - dbglog(t, "%s: added to chain [%s]" - , f->iface->name, conveyor_print(v, f, buf, sizeof(buf))); + dbglog(t, "%s: added to chain [%u/%u] {%s}" + , f->iface->name, t->conveyor.n_active, v->i_fpool + , conveyor_print(v, f, buf, sizeof(buf))); return pos; } @@ -358,7 +359,7 @@ static int trk_filter_handle_result(phi_track *t, struct filter *f, int r) if (chain_modified) { char buf[200]; - dbglog(t, "chain (%u): [%s]" + dbglog(t, "chain [%u] {%s}" , t->conveyor.n_active, conveyor_print(&t->conveyor, NULL, buf, sizeof(buf))); } diff --git a/src/format/ogg-write.h b/src/format/ogg-write.h index c542b1e..353370d 100644 --- a/src/format/ogg-write.h +++ b/src/format/ogg-write.h @@ -23,7 +23,7 @@ static void* ogg_w_open(phi_track *t) seed = 1; fftime t; fftime_now(&t); - ffrand_seed(fftime_sec(&t)); + ffrand_seed(fftime_sec(&t) + fftime_nsec(&t)); } return o; diff --git a/src/jni/convert.h b/src/jni/convert.h index 45ca3c7..3f88cf2 100644 --- a/src/jni/convert.h +++ b/src/jni/convert.h @@ -27,12 +27,14 @@ static const char* trk_errstr(uint e) return fferr_strptr(e & ~PHI_E_SYS); e--; + // enum PHI_E static const char errstr[][30] = { "Input file doesn't exist", // PHI_E_NOSRC "Output file already exists", // PHI_E_DSTEXIST "Unknown input file format", // PHI_E_UNKIFMT "Input audio device problem", // PHI_E_AUDIO_INPUT "Cancelled", // PHI_E_CANCELLED + "Sample conversion", // PHI_E_ACONV }; const char *s = "Unknown"; if (e < FF_COUNT(errstr)) diff --git a/src/net/http.c b/src/net/http.c index 141b459..f26e284 100644 --- a/src/net/http.c +++ b/src/net/http.c @@ -336,7 +336,6 @@ static void net_close() #ifndef PHI_HTTP_NO_SSL nml_ssl_uninit(phi_ssl_ctx); if (phi_ssl_ctx) { - ffmem_free(phi_ssl_ctx->ctx_conf->cert_file); ffmem_free(phi_ssl_ctx->ctx_conf); ffmem_free(phi_ssl_ctx); } diff --git a/src/track.h b/src/track.h index dd64c3d..0c80475 100644 --- a/src/track.h +++ b/src/track.h @@ -128,6 +128,7 @@ enum PHI_E { PHI_E_UNKIFMT, // unknown input format PHI_E_AUDIO_INPUT, PHI_E_CANCELLED, + PHI_E_ACONV, // audio conversion PHI_E_OTHER = 255, PHI_E_SYS = 0x80000000, }; From ee11422cddda60023621f2ad170decd81bd18936 Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 28 Sep 2024 08:16:12 +0300 Subject: [PATCH 07/59] 2.2.7 --- android/phiola/build.gradle | 4 ++-- src/phiola.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/phiola/build.gradle b/android/phiola/build.gradle index b299c9b..930f75f 100644 --- a/android/phiola/build.gradle +++ b/android/phiola/build.gradle @@ -8,8 +8,8 @@ android { applicationId "com.github.stsaz.phiola" minSdkVersion 16 targetSdk 33 - versionCode 20206 - versionName '2.2.6' + versionCode 20207 + versionName '2.2.7' } buildFeatures { diff --git a/src/phiola.h b/src/phiola.h index 747d273..2f4d622 100644 --- a/src/phiola.h +++ b/src/phiola.h @@ -6,7 +6,7 @@ #include #include -#define PHI_VERSION 20206 +#define PHI_VERSION 20207 /** Inter-module compatibility version. It must be updated when incompatible changes are made to this file, From 5c2bb2c55d1b97cfffab39bc1418a809ad7272ef Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 2 Nov 2024 10:19:44 +0300 Subject: [PATCH 08/59] - HTTP: query string was not passed to server --- src/exe/cmd.h | 6 ++++-- src/net/http.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/exe/cmd.h b/src/exe/cmd.h index 31d74b2..01d328d 100644 --- a/src/exe/cmd.h +++ b/src/exe/cmd.h @@ -144,8 +144,10 @@ static int cmd_input(ffvec *input, ffstr s) return cmd_input_names(input, ffstdin); #ifdef FF_WIN - if (ffstr_findany(&s, "*?", 2) >= 0 - && !ffstr_matchz(&s, "\\\\?\\")) { + if (!(ffstr_matchz(&s, "http://") + || ffstr_matchz(&s, "https://") + || ffstr_matchz(&s, "\\\\?\\")) + && ffstr_findany(&s, "*?", 2) >= 0) { if (!!wildcard_expand(input, s)) return 1; } else diff --git a/src/net/http.c b/src/net/http.c index f26e284..9b2fc7e 100644 --- a/src/net/http.c +++ b/src/net/http.c @@ -225,6 +225,8 @@ static int conf_prepare(struct httpcl *h, struct nml_http_client_conf *c, phi_tr c->host.len += p.port.len; if (!p.port.len) c->server_port = 80; + if (p.query.len) + p.path.len += p.query.len; // = "/path?query" ffvec_alloc(&h->path, p.path.len * 3, 1); h->path.len = httpurl_escape(h->path.ptr, h->path.cap, p.path); c->path = *(ffstr*)&h->path; From 980815c1d10352029b4cb8ce2bec56f444d3b013 Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 2 Nov 2024 10:19:44 +0300 Subject: [PATCH 09/59] + new remote command: `volume` --- src/exe/remote.h | 1 + src/remote-ctl.c | 10 ++++++++++ src/tui/play.h | 2 +- src/tui/tui.c | 5 ++++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/exe/remote.h b/src/exe/remote.h index dd2c165..3166b2e 100644 --- a/src/exe/remote.h +++ b/src/exe/remote.h @@ -14,6 +14,7 @@ Commands:\n\ `next` Play next track\n\ `previous` Play previous track\n\ `stop` Stop all tracks\n\ + `volume` NUMBER Set playback volume level: 0..100\n\ `quit` Exit\n\ "); x->exit_code = 0; diff --git a/src/remote-ctl.c b/src/remote-ctl.c index 8ed11b8..be37a4d 100644 --- a/src/remote-ctl.c +++ b/src/remote-ctl.c @@ -62,6 +62,15 @@ static int cmd_quit() { return 0; } +static int cmd_volume(void *o, uint64 n) { + struct phi_ui_conf uc = { + .volume_percent = n, + }; + const phi_ui_if *uif = g->core->mod("tui.if"); + uif->conf(&uc); + return 0; +} + static const struct ffarg args[] = { { "clear", '1', cmd_clear }, { "next", '1', cmd_next }, @@ -70,6 +79,7 @@ static const struct ffarg args[] = { { "quit", '1', cmd_quit }, { "start", 'S', cmd_start }, { "stop", '1', cmd_stop }, + { "volume", 'u', cmd_volume }, {} }; diff --git a/src/tui/play.h b/src/tui/play.h index c9beec7..93a59ae 100644 --- a/src/tui/play.h +++ b/src/tui/play.h @@ -151,7 +151,7 @@ static double tui_setvol(tui_track *u, uint vol) static void tuiplay_vol(tui_track *u, uint cmd) { - uint vol = 0; + uint vol = mod->vol; switch (cmd & ~FFKEY_MODMASK) { case CMD_VOLUP: diff --git a/src/tui/tui.c b/src/tui/tui.c index ab714f5..0288c49 100644 --- a/src/tui/tui.c +++ b/src/tui/tui.c @@ -423,8 +423,11 @@ static void tui_destroy() static void tui_conf(struct phi_ui_conf *c) { - if (c->volume_percent != 0xff) + if (c->volume_percent != 0xff) { mod->vol = ffmin((uint)c->volume_percent, VOL_MAX); + if (mod->curtrk) + tuiplay_vol(mod->curtrk, ~0U); + } } static struct phi_ui_if phi_tui_if = { From 0f8517a496227f12eece7c7379a1b2096c5ad19a Mon Sep 17 00:00:00 2001 From: Simon Zolin <4729655+stsaz@users.noreply.github.com> Date: Sat, 2 Nov 2024 10:19:44 +0300 Subject: [PATCH 10/59] Android: About: "Enable debug logging" --- .../main/java/com/github/stsaz/phiola/AboutActivity.java | 9 +++++++++ .../src/main/java/com/github/stsaz/phiola/Phiola.java | 1 + .../src/main/java/com/github/stsaz/phiola/Settings.java | 1 + android/phiola/src/main/res/layout/about.xml | 7 +++++++ android/phiola/src/main/res/values/strings.xml | 1 + src/jni/phiola-jni.c | 7 +++++++ 6 files changed, 26 insertions(+) diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/AboutActivity.java b/android/phiola/src/main/java/com/github/stsaz/phiola/AboutActivity.java index a9abc46..e5b428c 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/AboutActivity.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/AboutActivity.java @@ -25,6 +25,10 @@ protected void onCreate(Bundle savedInstanceState) { b.lAbout.setText(String.format("v%s\n\n%s", core.phiola.version(), "https://github.com/stsaz/phiola")); + + b.bDebugLog.setChecked(core.setts.debug_logs); + b.bDebugLog.setOnClickListener((v) -> logs_debug()); + b.bSaveLogs.setOnClickListener((v) -> logs_save_file()); } @@ -33,6 +37,11 @@ protected void onDestroy() { super.onDestroy(); } + private void logs_debug() { + core.setts.debug_logs = !core.setts.debug_logs; + core.phiola.setDebug(core.setts.debug_logs); + } + private void logs_save_file() { String fn = String.format("%s/logs.txt", core.setts.pub_data_dir); String[] args = new String[]{ diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/Phiola.java b/android/phiola/src/main/java/com/github/stsaz/phiola/Phiola.java index 9c57c8a..766bd43 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/Phiola.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/Phiola.java @@ -17,6 +17,7 @@ private static boolean lib_load(String filename) { native String version(); native void setCodepage(String codepage); + native void setDebug(boolean enable); static class Meta { int queue_pos; diff --git a/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java b/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java index 9327104..e56efd7 100644 --- a/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java +++ b/android/phiola/src/main/java/com/github/stsaz/phiola/Settings.java @@ -43,6 +43,7 @@ static int parse(String s) { class CoreSettings { private Core core; + boolean debug_logs; boolean svc_notification_disable; String trash_dir; boolean file_del; diff --git a/android/phiola/src/main/res/layout/about.xml b/android/phiola/src/main/res/layout/about.xml index 82601a0..2624e65 100644 --- a/android/phiola/src/main/res/layout/about.xml +++ b/android/phiola/src/main/res/layout/about.xml @@ -18,6 +18,13 @@ android:gravity="center_horizontal" android:textAlignment="center" /> + +