Support processing of text files - #176

Draft
hagenw wants to merge 4 commits into
mainfrom
process-text
Draft

Support processing of text files#176
hagenw wants to merge 4 commits into
mainfrom
process-text

Conversation

@hagenw

@hagenwhagenw commented Jun 27, 2024

Copy link
Copy Markdown
Member

Closes#173

...

TODO: implement a read_func argument, which can be used to provide a function to read in the file from disk. Maybe we don't need the extra _call_data() and process_data() methods then?

@hagenw
hagenw marked this pull request as draft June 27, 2024 14:07
@hagenwhagenw mentioned this pull request Jun 27, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

@ChristianGeng, @maxschmitt this pull request has the first ideas for adding support for JSON and TXT files to audinterface.Process.

My general idea is:

  • Try to make the processing methods independent of the underlying file/data as possible (at the moment I have separate methods for handling signal + sampling rate + start/end values and handling data without any additional information)
  • Support per default audio/video signals (everything that can be handled by audiofile) and json and txt files (see utils.read_text())
  • Add an read_function argument to audinterface.Process to allow the user to provide a function to handle general data/files. This would then allow to support any possible data and filetypes.

I will only be able to continue working on this from 2024/08/15. If you are interested in this, feel free to create another pull request with a solution.

@maxschmitt

Copy link
Copy Markdown
Contributor

Looks great, so far, and makes all sense to me. (Won't have much time to work on it either during August.)

@ChristianGeng

ChristianGeng commented Jul 26, 2024

Copy link
Copy Markdown
Member

Afaics all test stubs are there, with all test geared towards text processing failing.
So nicely implementing a radical test-first aproach ;-)

I would be motivated to get my hands on it in the calendar week starting Aug. 5,
of course I cannot promise but let's see - there might be conceptual questions regarding the implementation or time limitations.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Yes, no worries, just take a look if you have time. It might be that the tests are not complete yet, it's all work in progress. It also looks to me, that some of the tests should also be re-written in a less imperative way, e.g. maybe using a class.

@hagenw

Copy link
Copy Markdown
MemberAuthor

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

@ChristianGeng

Copy link
Copy Markdown
Member

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

Thanks for the hint! I had often been trying to circumvent the pencil and paper bit by using software to create call graphs automaticall: but I am seing that the good old manual stuff is probably still the best: my experience with the python packages for creating call graphs is worse than mixed and has not become better over the years. neither pyan, pycallgraph, pycallgraph2 were satisfactory. So I will go for the classical one too.

@ChristianGeng

ChristianGeng commented Aug 12, 2024

Copy link
Copy Markdown
Member

I have gone down the pathway that you recommended and created call graphs of the Process class.

For example, process_index (process_file, process_files, process_folder etc. go down a similar pathway). Basically things split up in _process_file where the pathway splits:

Signals: process_index -> _process_index_wo_segments -> _process_file -> _process_signal_ -> _call

Text data: process_index -> _process_index_wo_segments -> _process_file -> _process_data -> _call_data

The crucial problem that causes the tests in test_process_text.py to fail is a mismatch between the signature of process_func and process_func_args, as the non-signal pathway does not use a sampling_rate.

The most forward approach that I could come up with was to define a second functional called data_identity that needs to function as a replacement for identity when not dealing with signals but text.

Pointing to such a functon can probably be deferred until _call_data is reached.

However, whether dealing with signals or non-signal data has to be known as early as _process_index_wo_segments.

I have created a tentative merge request for now that does

  • determine whether dealing with text or signal in _process_index_wo_segments and sets a private attribute _processing_mode depending on file extensions
  • defer re-setting the identity function to _call_data
  • fix the tests for process_index only

While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

sampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
win_dur: Timestamp=None,
hop_dur: Timestamp=None,
min_signal_dur: Timestamp=None,
max_signal_dur: Timestamp=None,

So rather than determining whether text or signal "by hand", would it not be better to design the class to have separate the two strands initially, and have separate processing modes a priori (possibly use simple inheritance?)

To me it looks like you were already unhappy, and have created a stub of a public interface called process_data that seems to be unused so far. I would find it important to discuss in which direction this should go before really implementing it. Also I am currently unsure whether the way I adapted the tests of process_index respect the interaction between preserve_index and the segment argument correctly. In the same vein: I have modified the assertions related to preserve_index and am unconvinced that these modifications are correct.

see #179

@ChristianGengChristianGeng mentioned this pull request Aug 12, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

Thanks for proposing a fix for the failing tests. It is indeed unfortunate that we need to track inside the class if we process data or signals.


While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

There are indeed several differences between a sampled signal and other "data":

  • the whole handling of segments is only needed for signals
  • Process and other inherited classes have several arguments only relevant for signals
  • several methods/functions need to have a different signature for signals than for data (as we need the sampling_rate argument)

From a developer standpoint, all those points seem to indicate that we should indeed try to separate the implementation more and not add everything to the single Process class.

On the other hand, my main motivation so far was coming from a user perspective. For a user it is very convenient if the following code works independent of the underlying data/signals:

interface=audinterface.Process()
interface.process_index(index)

In summary, I still don't know what is the best solution for the desired data processing.

@ChristianGeng

ChristianGeng commented Aug 14, 2024

Copy link
Copy Markdown
Member

There are probably several ways to get both under the same umbrella without breaking the api. One way would be to go into the metaprogramming direction and have the `Process` class delegate the objec t creation to signal and text specific classes via `__new__`.

Untested, but would something like this do it?:

classProcessData(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etc
)
classProcessSignals(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etcsampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
)
classProcess(object):
def__new__(cls, **kwargs): # only have kwargs# possibly need to pop it from kwargsprocessing_mode=kwargs.get("processing_mode")
ifprocessing_mode=="signal":
return_ProcessSignals(kwargs)
ifprocessing_mode=="text":
return_ProcessData(kwargs)
defcommon_methods(self, **args):
print("define method common methods to both text and data")

@ChristianGeng

Copy link
Copy Markdown
Member

I have assembled an example call graph using pyan usind process_index as an example.

image

If one were to separate the text and signal strands more then it probably could be made more balanced.

Then the signal portion could possibly go down a path like this:

process_index
_process_index_wo_segment
_process_file
_process(_signal)
_call

One could possibly rename _process_signal into _process and in that strand (hence the parenthesis)
And for text (skipping _process_index_wo_segment):

process_index
_process_file
_process(_data)
_call(_data)

Suffixes (again parenthesized) could be stripped in order to "balance" the tree.
Not sure whether this is too simplistic.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

@ChristianGeng

ChristianGeng commented Sep 11, 2024

Copy link
Copy Markdown
Member

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

I think it will make sense to branch off freshly from here again as #179 was experimental. I would close #179 and possibly delete the branch in that case.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for reading text files as media files

3 participants

@hagenw@maxschmitt@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Support processing of text files - #176

Draft
hagenw wants to merge 4 commits into
mainfrom
process-text
Draft

Support processing of text files#176
hagenw wants to merge 4 commits into
mainfrom
process-text

Conversation

@hagenw

@hagenwhagenw commented Jun 27, 2024

Copy link
Copy Markdown
Member

Closes#173

...

TODO: implement a read_func argument, which can be used to provide a function to read in the file from disk. Maybe we don't need the extra _call_data() and process_data() methods then?

@hagenw
hagenw marked this pull request as draft June 27, 2024 14:07
@hagenwhagenw mentioned this pull request Jun 27, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

@ChristianGeng, @maxschmitt this pull request has the first ideas for adding support for JSON and TXT files to audinterface.Process.

My general idea is:

  • Try to make the processing methods independent of the underlying file/data as possible (at the moment I have separate methods for handling signal + sampling rate + start/end values and handling data without any additional information)
  • Support per default audio/video signals (everything that can be handled by audiofile) and json and txt files (see utils.read_text())
  • Add an read_function argument to audinterface.Process to allow the user to provide a function to handle general data/files. This would then allow to support any possible data and filetypes.

I will only be able to continue working on this from 2024/08/15. If you are interested in this, feel free to create another pull request with a solution.

@maxschmitt

Copy link
Copy Markdown
Contributor

Looks great, so far, and makes all sense to me. (Won't have much time to work on it either during August.)

@ChristianGeng

ChristianGeng commented Jul 26, 2024

Copy link
Copy Markdown
Member

Afaics all test stubs are there, with all test geared towards text processing failing.
So nicely implementing a radical test-first aproach ;-)

I would be motivated to get my hands on it in the calendar week starting Aug. 5,
of course I cannot promise but let's see - there might be conceptual questions regarding the implementation or time limitations.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Yes, no worries, just take a look if you have time. It might be that the tests are not complete yet, it's all work in progress. It also looks to me, that some of the tests should also be re-written in a less imperative way, e.g. maybe using a class.

@hagenw

Copy link
Copy Markdown
MemberAuthor

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

@ChristianGeng

Copy link
Copy Markdown
Member

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

Thanks for the hint! I had often been trying to circumvent the pencil and paper bit by using software to create call graphs automaticall: but I am seing that the good old manual stuff is probably still the best: my experience with the python packages for creating call graphs is worse than mixed and has not become better over the years. neither pyan, pycallgraph, pycallgraph2 were satisfactory. So I will go for the classical one too.

@ChristianGeng

ChristianGeng commented Aug 12, 2024

Copy link
Copy Markdown
Member

I have gone down the pathway that you recommended and created call graphs of the Process class.

For example, process_index (process_file, process_files, process_folder etc. go down a similar pathway). Basically things split up in _process_file where the pathway splits:

Signals: process_index -> _process_index_wo_segments -> _process_file -> _process_signal_ -> _call

Text data: process_index -> _process_index_wo_segments -> _process_file -> _process_data -> _call_data

The crucial problem that causes the tests in test_process_text.py to fail is a mismatch between the signature of process_func and process_func_args, as the non-signal pathway does not use a sampling_rate.

The most forward approach that I could come up with was to define a second functional called data_identity that needs to function as a replacement for identity when not dealing with signals but text.

Pointing to such a functon can probably be deferred until _call_data is reached.

However, whether dealing with signals or non-signal data has to be known as early as _process_index_wo_segments.

I have created a tentative merge request for now that does

  • determine whether dealing with text or signal in _process_index_wo_segments and sets a private attribute _processing_mode depending on file extensions
  • defer re-setting the identity function to _call_data
  • fix the tests for process_index only

While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

sampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
win_dur: Timestamp=None,
hop_dur: Timestamp=None,
min_signal_dur: Timestamp=None,
max_signal_dur: Timestamp=None,

So rather than determining whether text or signal "by hand", would it not be better to design the class to have separate the two strands initially, and have separate processing modes a priori (possibly use simple inheritance?)

To me it looks like you were already unhappy, and have created a stub of a public interface called process_data that seems to be unused so far. I would find it important to discuss in which direction this should go before really implementing it. Also I am currently unsure whether the way I adapted the tests of process_index respect the interaction between preserve_index and the segment argument correctly. In the same vein: I have modified the assertions related to preserve_index and am unconvinced that these modifications are correct.

see #179

@ChristianGengChristianGeng mentioned this pull request Aug 12, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

Thanks for proposing a fix for the failing tests. It is indeed unfortunate that we need to track inside the class if we process data or signals.


While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

There are indeed several differences between a sampled signal and other "data":

  • the whole handling of segments is only needed for signals
  • Process and other inherited classes have several arguments only relevant for signals
  • several methods/functions need to have a different signature for signals than for data (as we need the sampling_rate argument)

From a developer standpoint, all those points seem to indicate that we should indeed try to separate the implementation more and not add everything to the single Process class.

On the other hand, my main motivation so far was coming from a user perspective. For a user it is very convenient if the following code works independent of the underlying data/signals:

interface=audinterface.Process()
interface.process_index(index)

In summary, I still don't know what is the best solution for the desired data processing.

@ChristianGeng

ChristianGeng commented Aug 14, 2024

Copy link
Copy Markdown
Member

There are probably several ways to get both under the same umbrella without breaking the api. One way would be to go into the metaprogramming direction and have the `Process` class delegate the objec t creation to signal and text specific classes via `__new__`.

Untested, but would something like this do it?:

classProcessData(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etc
)
classProcessSignals(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etcsampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
)
classProcess(object):
def__new__(cls, **kwargs): # only have kwargs# possibly need to pop it from kwargsprocessing_mode=kwargs.get("processing_mode")
ifprocessing_mode=="signal":
return_ProcessSignals(kwargs)
ifprocessing_mode=="text":
return_ProcessData(kwargs)
defcommon_methods(self, **args):
print("define method common methods to both text and data")

@ChristianGeng

Copy link
Copy Markdown
Member

I have assembled an example call graph using pyan usind process_index as an example.

image

If one were to separate the text and signal strands more then it probably could be made more balanced.

Then the signal portion could possibly go down a path like this:

process_index
_process_index_wo_segment
_process_file
_process(_signal)
_call

One could possibly rename _process_signal into _process and in that strand (hence the parenthesis)
And for text (skipping _process_index_wo_segment):

process_index
_process_file
_process(_data)
_call(_data)

Suffixes (again parenthesized) could be stripped in order to "balance" the tree.
Not sure whether this is too simplistic.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

@ChristianGeng

ChristianGeng commented Sep 11, 2024

Copy link
Copy Markdown
Member

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

I think it will make sense to branch off freshly from here again as #179 was experimental. I would close #179 and possibly delete the branch in that case.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for reading text files as media files

3 participants

@hagenw@maxschmitt@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support processing of text files - #176

Draft
hagenw wants to merge 4 commits into
mainfrom
process-text
Draft

Support processing of text files#176
hagenw wants to merge 4 commits into
mainfrom
process-text

Conversation

@hagenw

@hagenwhagenw commented Jun 27, 2024

Copy link
Copy Markdown
Member

Closes#173

...

TODO: implement a read_func argument, which can be used to provide a function to read in the file from disk. Maybe we don't need the extra _call_data() and process_data() methods then?

@hagenw
hagenw marked this pull request as draft June 27, 2024 14:07
@hagenwhagenw mentioned this pull request Jun 27, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

@ChristianGeng, @maxschmitt this pull request has the first ideas for adding support for JSON and TXT files to audinterface.Process.

My general idea is:

  • Try to make the processing methods independent of the underlying file/data as possible (at the moment I have separate methods for handling signal + sampling rate + start/end values and handling data without any additional information)
  • Support per default audio/video signals (everything that can be handled by audiofile) and json and txt files (see utils.read_text())
  • Add an read_function argument to audinterface.Process to allow the user to provide a function to handle general data/files. This would then allow to support any possible data and filetypes.

I will only be able to continue working on this from 2024/08/15. If you are interested in this, feel free to create another pull request with a solution.

@maxschmitt

Copy link
Copy Markdown
Contributor

Looks great, so far, and makes all sense to me. (Won't have much time to work on it either during August.)

@ChristianGeng

ChristianGeng commented Jul 26, 2024

Copy link
Copy Markdown
Member

Afaics all test stubs are there, with all test geared towards text processing failing.
So nicely implementing a radical test-first aproach ;-)

I would be motivated to get my hands on it in the calendar week starting Aug. 5,
of course I cannot promise but let's see - there might be conceptual questions regarding the implementation or time limitations.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Yes, no worries, just take a look if you have time. It might be that the tests are not complete yet, it's all work in progress. It also looks to me, that some of the tests should also be re-written in a less imperative way, e.g. maybe using a class.

@hagenw

Copy link
Copy Markdown
MemberAuthor

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

@ChristianGeng

Copy link
Copy Markdown
Member

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

Thanks for the hint! I had often been trying to circumvent the pencil and paper bit by using software to create call graphs automaticall: but I am seing that the good old manual stuff is probably still the best: my experience with the python packages for creating call graphs is worse than mixed and has not become better over the years. neither pyan, pycallgraph, pycallgraph2 were satisfactory. So I will go for the classical one too.

@ChristianGeng

ChristianGeng commented Aug 12, 2024

Copy link
Copy Markdown
Member

I have gone down the pathway that you recommended and created call graphs of the Process class.

For example, process_index (process_file, process_files, process_folder etc. go down a similar pathway). Basically things split up in _process_file where the pathway splits:

Signals: process_index -> _process_index_wo_segments -> _process_file -> _process_signal_ -> _call

Text data: process_index -> _process_index_wo_segments -> _process_file -> _process_data -> _call_data

The crucial problem that causes the tests in test_process_text.py to fail is a mismatch between the signature of process_func and process_func_args, as the non-signal pathway does not use a sampling_rate.

The most forward approach that I could come up with was to define a second functional called data_identity that needs to function as a replacement for identity when not dealing with signals but text.

Pointing to such a functon can probably be deferred until _call_data is reached.

However, whether dealing with signals or non-signal data has to be known as early as _process_index_wo_segments.

I have created a tentative merge request for now that does

  • determine whether dealing with text or signal in _process_index_wo_segments and sets a private attribute _processing_mode depending on file extensions
  • defer re-setting the identity function to _call_data
  • fix the tests for process_index only

While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

sampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
win_dur: Timestamp=None,
hop_dur: Timestamp=None,
min_signal_dur: Timestamp=None,
max_signal_dur: Timestamp=None,

So rather than determining whether text or signal "by hand", would it not be better to design the class to have separate the two strands initially, and have separate processing modes a priori (possibly use simple inheritance?)

To me it looks like you were already unhappy, and have created a stub of a public interface called process_data that seems to be unused so far. I would find it important to discuss in which direction this should go before really implementing it. Also I am currently unsure whether the way I adapted the tests of process_index respect the interaction between preserve_index and the segment argument correctly. In the same vein: I have modified the assertions related to preserve_index and am unconvinced that these modifications are correct.

see #179

@ChristianGengChristianGeng mentioned this pull request Aug 12, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

Thanks for proposing a fix for the failing tests. It is indeed unfortunate that we need to track inside the class if we process data or signals.


While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

There are indeed several differences between a sampled signal and other "data":

  • the whole handling of segments is only needed for signals
  • Process and other inherited classes have several arguments only relevant for signals
  • several methods/functions need to have a different signature for signals than for data (as we need the sampling_rate argument)

From a developer standpoint, all those points seem to indicate that we should indeed try to separate the implementation more and not add everything to the single Process class.

On the other hand, my main motivation so far was coming from a user perspective. For a user it is very convenient if the following code works independent of the underlying data/signals:

interface=audinterface.Process()
interface.process_index(index)

In summary, I still don't know what is the best solution for the desired data processing.

@ChristianGeng

ChristianGeng commented Aug 14, 2024

Copy link
Copy Markdown
Member

There are probably several ways to get both under the same umbrella without breaking the api. One way would be to go into the metaprogramming direction and have the `Process` class delegate the objec t creation to signal and text specific classes via `__new__`.

Untested, but would something like this do it?:

classProcessData(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etc
)
classProcessSignals(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etcsampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
)
classProcess(object):
def__new__(cls, **kwargs): # only have kwargs# possibly need to pop it from kwargsprocessing_mode=kwargs.get("processing_mode")
ifprocessing_mode=="signal":
return_ProcessSignals(kwargs)
ifprocessing_mode=="text":
return_ProcessData(kwargs)
defcommon_methods(self, **args):
print("define method common methods to both text and data")

@ChristianGeng

Copy link
Copy Markdown
Member

I have assembled an example call graph using pyan usind process_index as an example.

image

If one were to separate the text and signal strands more then it probably could be made more balanced.

Then the signal portion could possibly go down a path like this:

process_index
_process_index_wo_segment
_process_file
_process(_signal)
_call

One could possibly rename _process_signal into _process and in that strand (hence the parenthesis)
And for text (skipping _process_index_wo_segment):

process_index
_process_file
_process(_data)
_call(_data)

Suffixes (again parenthesized) could be stripped in order to "balance" the tree.
Not sure whether this is too simplistic.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

@ChristianGeng

ChristianGeng commented Sep 11, 2024

Copy link
Copy Markdown
Member

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

I think it will make sense to branch off freshly from here again as #179 was experimental. I would close #179 and possibly delete the branch in that case.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for reading text files as media files

3 participants

@hagenw@maxschmitt@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support processing of text files - #176

Draft
hagenw wants to merge 4 commits into
mainfrom
process-text
Draft

Support processing of text files#176
hagenw wants to merge 4 commits into
mainfrom
process-text

Conversation

@hagenw

@hagenwhagenw commented Jun 27, 2024

Copy link
Copy Markdown
Member

Closes#173

...

TODO: implement a read_func argument, which can be used to provide a function to read in the file from disk. Maybe we don't need the extra _call_data() and process_data() methods then?

@hagenw
hagenw marked this pull request as draft June 27, 2024 14:07
@hagenwhagenw mentioned this pull request Jun 27, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

@ChristianGeng, @maxschmitt this pull request has the first ideas for adding support for JSON and TXT files to audinterface.Process.

My general idea is:

  • Try to make the processing methods independent of the underlying file/data as possible (at the moment I have separate methods for handling signal + sampling rate + start/end values and handling data without any additional information)
  • Support per default audio/video signals (everything that can be handled by audiofile) and json and txt files (see utils.read_text())
  • Add an read_function argument to audinterface.Process to allow the user to provide a function to handle general data/files. This would then allow to support any possible data and filetypes.

I will only be able to continue working on this from 2024/08/15. If you are interested in this, feel free to create another pull request with a solution.

@maxschmitt

Copy link
Copy Markdown
Contributor

Looks great, so far, and makes all sense to me. (Won't have much time to work on it either during August.)

@ChristianGeng

ChristianGeng commented Jul 26, 2024

Copy link
Copy Markdown
Member

Afaics all test stubs are there, with all test geared towards text processing failing.
So nicely implementing a radical test-first aproach ;-)

I would be motivated to get my hands on it in the calendar week starting Aug. 5,
of course I cannot promise but let's see - there might be conceptual questions regarding the implementation or time limitations.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Yes, no worries, just take a look if you have time. It might be that the tests are not complete yet, it's all work in progress. It also looks to me, that some of the tests should also be re-written in a less imperative way, e.g. maybe using a class.

@hagenw

Copy link
Copy Markdown
MemberAuthor

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

@ChristianGeng

Copy link
Copy Markdown
Member

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

Thanks for the hint! I had often been trying to circumvent the pencil and paper bit by using software to create call graphs automaticall: but I am seing that the good old manual stuff is probably still the best: my experience with the python packages for creating call graphs is worse than mixed and has not become better over the years. neither pyan, pycallgraph, pycallgraph2 were satisfactory. So I will go for the classical one too.

@ChristianGeng

ChristianGeng commented Aug 12, 2024

Copy link
Copy Markdown
Member

I have gone down the pathway that you recommended and created call graphs of the Process class.

For example, process_index (process_file, process_files, process_folder etc. go down a similar pathway). Basically things split up in _process_file where the pathway splits:

Signals: process_index -> _process_index_wo_segments -> _process_file -> _process_signal_ -> _call

Text data: process_index -> _process_index_wo_segments -> _process_file -> _process_data -> _call_data

The crucial problem that causes the tests in test_process_text.py to fail is a mismatch between the signature of process_func and process_func_args, as the non-signal pathway does not use a sampling_rate.

The most forward approach that I could come up with was to define a second functional called data_identity that needs to function as a replacement for identity when not dealing with signals but text.

Pointing to such a functon can probably be deferred until _call_data is reached.

However, whether dealing with signals or non-signal data has to be known as early as _process_index_wo_segments.

I have created a tentative merge request for now that does

  • determine whether dealing with text or signal in _process_index_wo_segments and sets a private attribute _processing_mode depending on file extensions
  • defer re-setting the identity function to _call_data
  • fix the tests for process_index only

While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

sampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
win_dur: Timestamp=None,
hop_dur: Timestamp=None,
min_signal_dur: Timestamp=None,
max_signal_dur: Timestamp=None,

So rather than determining whether text or signal "by hand", would it not be better to design the class to have separate the two strands initially, and have separate processing modes a priori (possibly use simple inheritance?)

To me it looks like you were already unhappy, and have created a stub of a public interface called process_data that seems to be unused so far. I would find it important to discuss in which direction this should go before really implementing it. Also I am currently unsure whether the way I adapted the tests of process_index respect the interaction between preserve_index and the segment argument correctly. In the same vein: I have modified the assertions related to preserve_index and am unconvinced that these modifications are correct.

see #179

@ChristianGengChristianGeng mentioned this pull request Aug 12, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

Thanks for proposing a fix for the failing tests. It is indeed unfortunate that we need to track inside the class if we process data or signals.


While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

There are indeed several differences between a sampled signal and other "data":

  • the whole handling of segments is only needed for signals
  • Process and other inherited classes have several arguments only relevant for signals
  • several methods/functions need to have a different signature for signals than for data (as we need the sampling_rate argument)

From a developer standpoint, all those points seem to indicate that we should indeed try to separate the implementation more and not add everything to the single Process class.

On the other hand, my main motivation so far was coming from a user perspective. For a user it is very convenient if the following code works independent of the underlying data/signals:

interface=audinterface.Process()
interface.process_index(index)

In summary, I still don't know what is the best solution for the desired data processing.

@ChristianGeng

ChristianGeng commented Aug 14, 2024

Copy link
Copy Markdown
Member

There are probably several ways to get both under the same umbrella without breaking the api. One way would be to go into the metaprogramming direction and have the `Process` class delegate the objec t creation to signal and text specific classes via `__new__`.

Untested, but would something like this do it?:

classProcessData(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etc
)
classProcessSignals(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etcsampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
)
classProcess(object):
def__new__(cls, **kwargs): # only have kwargs# possibly need to pop it from kwargsprocessing_mode=kwargs.get("processing_mode")
ifprocessing_mode=="signal":
return_ProcessSignals(kwargs)
ifprocessing_mode=="text":
return_ProcessData(kwargs)
defcommon_methods(self, **args):
print("define method common methods to both text and data")

@ChristianGeng

Copy link
Copy Markdown
Member

I have assembled an example call graph using pyan usind process_index as an example.

image

If one were to separate the text and signal strands more then it probably could be made more balanced.

Then the signal portion could possibly go down a path like this:

process_index
_process_index_wo_segment
_process_file
_process(_signal)
_call

One could possibly rename _process_signal into _process and in that strand (hence the parenthesis)
And for text (skipping _process_index_wo_segment):

process_index
_process_file
_process(_data)
_call(_data)

Suffixes (again parenthesized) could be stripped in order to "balance" the tree.
Not sure whether this is too simplistic.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

@ChristianGeng

ChristianGeng commented Sep 11, 2024

Copy link
Copy Markdown
Member

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

I think it will make sense to branch off freshly from here again as #179 was experimental. I would close #179 and possibly delete the branch in that case.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for reading text files as media files

3 participants

@hagenw@maxschmitt@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Support processing of text files - #176

Draft
hagenw wants to merge 4 commits into
mainfrom
process-text
Draft

Support processing of text files#176
hagenw wants to merge 4 commits into
mainfrom
process-text

Conversation

@hagenw

@hagenwhagenw commented Jun 27, 2024

Copy link
Copy Markdown
Member

Closes#173

...

TODO: implement a read_func argument, which can be used to provide a function to read in the file from disk. Maybe we don't need the extra _call_data() and process_data() methods then?

@hagenw
hagenw marked this pull request as draft June 27, 2024 14:07
@hagenwhagenw mentioned this pull request Jun 27, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

@ChristianGeng, @maxschmitt this pull request has the first ideas for adding support for JSON and TXT files to audinterface.Process.

My general idea is:

  • Try to make the processing methods independent of the underlying file/data as possible (at the moment I have separate methods for handling signal + sampling rate + start/end values and handling data without any additional information)
  • Support per default audio/video signals (everything that can be handled by audiofile) and json and txt files (see utils.read_text())
  • Add an read_function argument to audinterface.Process to allow the user to provide a function to handle general data/files. This would then allow to support any possible data and filetypes.

I will only be able to continue working on this from 2024/08/15. If you are interested in this, feel free to create another pull request with a solution.

@maxschmitt

Copy link
Copy Markdown
Contributor

Looks great, so far, and makes all sense to me. (Won't have much time to work on it either during August.)

@ChristianGeng

ChristianGeng commented Jul 26, 2024

Copy link
Copy Markdown
Member

Afaics all test stubs are there, with all test geared towards text processing failing.
So nicely implementing a radical test-first aproach ;-)

I would be motivated to get my hands on it in the calendar week starting Aug. 5,
of course I cannot promise but let's see - there might be conceptual questions regarding the implementation or time limitations.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Yes, no worries, just take a look if you have time. It might be that the tests are not complete yet, it's all work in progress. It also looks to me, that some of the tests should also be re-written in a less imperative way, e.g. maybe using a class.

@hagenw

Copy link
Copy Markdown
MemberAuthor

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

@ChristianGeng

Copy link
Copy Markdown
Member

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

Thanks for the hint! I had often been trying to circumvent the pencil and paper bit by using software to create call graphs automaticall: but I am seing that the good old manual stuff is probably still the best: my experience with the python packages for creating call graphs is worse than mixed and has not become better over the years. neither pyan, pycallgraph, pycallgraph2 were satisfactory. So I will go for the classical one too.

@ChristianGeng

ChristianGeng commented Aug 12, 2024

Copy link
Copy Markdown
Member

I have gone down the pathway that you recommended and created call graphs of the Process class.

For example, process_index (process_file, process_files, process_folder etc. go down a similar pathway). Basically things split up in _process_file where the pathway splits:

Signals: process_index -> _process_index_wo_segments -> _process_file -> _process_signal_ -> _call

Text data: process_index -> _process_index_wo_segments -> _process_file -> _process_data -> _call_data

The crucial problem that causes the tests in test_process_text.py to fail is a mismatch between the signature of process_func and process_func_args, as the non-signal pathway does not use a sampling_rate.

The most forward approach that I could come up with was to define a second functional called data_identity that needs to function as a replacement for identity when not dealing with signals but text.

Pointing to such a functon can probably be deferred until _call_data is reached.

However, whether dealing with signals or non-signal data has to be known as early as _process_index_wo_segments.

I have created a tentative merge request for now that does

  • determine whether dealing with text or signal in _process_index_wo_segments and sets a private attribute _processing_mode depending on file extensions
  • defer re-setting the identity function to _call_data
  • fix the tests for process_index only

While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

sampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
win_dur: Timestamp=None,
hop_dur: Timestamp=None,
min_signal_dur: Timestamp=None,
max_signal_dur: Timestamp=None,

So rather than determining whether text or signal "by hand", would it not be better to design the class to have separate the two strands initially, and have separate processing modes a priori (possibly use simple inheritance?)

To me it looks like you were already unhappy, and have created a stub of a public interface called process_data that seems to be unused so far. I would find it important to discuss in which direction this should go before really implementing it. Also I am currently unsure whether the way I adapted the tests of process_index respect the interaction between preserve_index and the segment argument correctly. In the same vein: I have modified the assertions related to preserve_index and am unconvinced that these modifications are correct.

see #179

@ChristianGengChristianGeng mentioned this pull request Aug 12, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

Thanks for proposing a fix for the failing tests. It is indeed unfortunate that we need to track inside the class if we process data or signals.


While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

There are indeed several differences between a sampled signal and other "data":

  • the whole handling of segments is only needed for signals
  • Process and other inherited classes have several arguments only relevant for signals
  • several methods/functions need to have a different signature for signals than for data (as we need the sampling_rate argument)

From a developer standpoint, all those points seem to indicate that we should indeed try to separate the implementation more and not add everything to the single Process class.

On the other hand, my main motivation so far was coming from a user perspective. For a user it is very convenient if the following code works independent of the underlying data/signals:

interface=audinterface.Process()
interface.process_index(index)

In summary, I still don't know what is the best solution for the desired data processing.

@ChristianGeng

ChristianGeng commented Aug 14, 2024

Copy link
Copy Markdown
Member

There are probably several ways to get both under the same umbrella without breaking the api. One way would be to go into the metaprogramming direction and have the `Process` class delegate the objec t creation to signal and text specific classes via `__new__`.

Untested, but would something like this do it?:

classProcessData(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etc
)
classProcessSignals(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etcsampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
)
classProcess(object):
def__new__(cls, **kwargs): # only have kwargs# possibly need to pop it from kwargsprocessing_mode=kwargs.get("processing_mode")
ifprocessing_mode=="signal":
return_ProcessSignals(kwargs)
ifprocessing_mode=="text":
return_ProcessData(kwargs)
defcommon_methods(self, **args):
print("define method common methods to both text and data")

@ChristianGeng

Copy link
Copy Markdown
Member

I have assembled an example call graph using pyan usind process_index as an example.

image

If one were to separate the text and signal strands more then it probably could be made more balanced.

Then the signal portion could possibly go down a path like this:

process_index
_process_index_wo_segment
_process_file
_process(_signal)
_call

One could possibly rename _process_signal into _process and in that strand (hence the parenthesis)
And for text (skipping _process_index_wo_segment):

process_index
_process_file
_process(_data)
_call(_data)

Suffixes (again parenthesized) could be stripped in order to "balance" the tree.
Not sure whether this is too simplistic.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

@ChristianGeng

ChristianGeng commented Sep 11, 2024

Copy link
Copy Markdown
Member

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

I think it will make sense to branch off freshly from here again as #179 was experimental. I would close #179 and possibly delete the branch in that case.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for reading text files as media files

3 participants

@hagenw@maxschmitt@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support processing of text files - #176

Draft
hagenw wants to merge 4 commits into
mainfrom
process-text
Draft

Support processing of text files#176
hagenw wants to merge 4 commits into
mainfrom
process-text

Conversation

@hagenw

@hagenwhagenw commented Jun 27, 2024

Copy link
Copy Markdown
Member

Closes#173

...

TODO: implement a read_func argument, which can be used to provide a function to read in the file from disk. Maybe we don't need the extra _call_data() and process_data() methods then?

@hagenw
hagenw marked this pull request as draft June 27, 2024 14:07
@hagenwhagenw mentioned this pull request Jun 27, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

@ChristianGeng, @maxschmitt this pull request has the first ideas for adding support for JSON and TXT files to audinterface.Process.

My general idea is:

  • Try to make the processing methods independent of the underlying file/data as possible (at the moment I have separate methods for handling signal + sampling rate + start/end values and handling data without any additional information)
  • Support per default audio/video signals (everything that can be handled by audiofile) and json and txt files (see utils.read_text())
  • Add an read_function argument to audinterface.Process to allow the user to provide a function to handle general data/files. This would then allow to support any possible data and filetypes.

I will only be able to continue working on this from 2024/08/15. If you are interested in this, feel free to create another pull request with a solution.

@maxschmitt

Copy link
Copy Markdown
Contributor

Looks great, so far, and makes all sense to me. (Won't have much time to work on it either during August.)

@ChristianGeng

ChristianGeng commented Jul 26, 2024

Copy link
Copy Markdown
Member

Afaics all test stubs are there, with all test geared towards text processing failing.
So nicely implementing a radical test-first aproach ;-)

I would be motivated to get my hands on it in the calendar week starting Aug. 5,
of course I cannot promise but let's see - there might be conceptual questions regarding the implementation or time limitations.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Yes, no worries, just take a look if you have time. It might be that the tests are not complete yet, it's all work in progress. It also looks to me, that some of the tests should also be re-written in a less imperative way, e.g. maybe using a class.

@hagenw

Copy link
Copy Markdown
MemberAuthor

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

@ChristianGeng

Copy link
Copy Markdown
Member

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

Thanks for the hint! I had often been trying to circumvent the pencil and paper bit by using software to create call graphs automaticall: but I am seing that the good old manual stuff is probably still the best: my experience with the python packages for creating call graphs is worse than mixed and has not become better over the years. neither pyan, pycallgraph, pycallgraph2 were satisfactory. So I will go for the classical one too.

@ChristianGeng

ChristianGeng commented Aug 12, 2024

Copy link
Copy Markdown
Member

I have gone down the pathway that you recommended and created call graphs of the Process class.

For example, process_index (process_file, process_files, process_folder etc. go down a similar pathway). Basically things split up in _process_file where the pathway splits:

Signals: process_index -> _process_index_wo_segments -> _process_file -> _process_signal_ -> _call

Text data: process_index -> _process_index_wo_segments -> _process_file -> _process_data -> _call_data

The crucial problem that causes the tests in test_process_text.py to fail is a mismatch between the signature of process_func and process_func_args, as the non-signal pathway does not use a sampling_rate.

The most forward approach that I could come up with was to define a second functional called data_identity that needs to function as a replacement for identity when not dealing with signals but text.

Pointing to such a functon can probably be deferred until _call_data is reached.

However, whether dealing with signals or non-signal data has to be known as early as _process_index_wo_segments.

I have created a tentative merge request for now that does

  • determine whether dealing with text or signal in _process_index_wo_segments and sets a private attribute _processing_mode depending on file extensions
  • defer re-setting the identity function to _call_data
  • fix the tests for process_index only

While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

sampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
win_dur: Timestamp=None,
hop_dur: Timestamp=None,
min_signal_dur: Timestamp=None,
max_signal_dur: Timestamp=None,

So rather than determining whether text or signal "by hand", would it not be better to design the class to have separate the two strands initially, and have separate processing modes a priori (possibly use simple inheritance?)

To me it looks like you were already unhappy, and have created a stub of a public interface called process_data that seems to be unused so far. I would find it important to discuss in which direction this should go before really implementing it. Also I am currently unsure whether the way I adapted the tests of process_index respect the interaction between preserve_index and the segment argument correctly. In the same vein: I have modified the assertions related to preserve_index and am unconvinced that these modifications are correct.

see #179

@ChristianGengChristianGeng mentioned this pull request Aug 12, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

Thanks for proposing a fix for the failing tests. It is indeed unfortunate that we need to track inside the class if we process data or signals.


While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

There are indeed several differences between a sampled signal and other "data":

  • the whole handling of segments is only needed for signals
  • Process and other inherited classes have several arguments only relevant for signals
  • several methods/functions need to have a different signature for signals than for data (as we need the sampling_rate argument)

From a developer standpoint, all those points seem to indicate that we should indeed try to separate the implementation more and not add everything to the single Process class.

On the other hand, my main motivation so far was coming from a user perspective. For a user it is very convenient if the following code works independent of the underlying data/signals:

interface=audinterface.Process()
interface.process_index(index)

In summary, I still don't know what is the best solution for the desired data processing.

@ChristianGeng

ChristianGeng commented Aug 14, 2024

Copy link
Copy Markdown
Member

There are probably several ways to get both under the same umbrella without breaking the api. One way would be to go into the metaprogramming direction and have the `Process` class delegate the objec t creation to signal and text specific classes via `__new__`.

Untested, but would something like this do it?:

classProcessData(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etc
)
classProcessSignals(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etcsampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
)
classProcess(object):
def__new__(cls, **kwargs): # only have kwargs# possibly need to pop it from kwargsprocessing_mode=kwargs.get("processing_mode")
ifprocessing_mode=="signal":
return_ProcessSignals(kwargs)
ifprocessing_mode=="text":
return_ProcessData(kwargs)
defcommon_methods(self, **args):
print("define method common methods to both text and data")

@ChristianGeng

Copy link
Copy Markdown
Member

I have assembled an example call graph using pyan usind process_index as an example.

image

If one were to separate the text and signal strands more then it probably could be made more balanced.

Then the signal portion could possibly go down a path like this:

process_index
_process_index_wo_segment
_process_file
_process(_signal)
_call

One could possibly rename _process_signal into _process and in that strand (hence the parenthesis)
And for text (skipping _process_index_wo_segment):

process_index
_process_file
_process(_data)
_call(_data)

Suffixes (again parenthesized) could be stripped in order to "balance" the tree.
Not sure whether this is too simplistic.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

@ChristianGeng

ChristianGeng commented Sep 11, 2024

Copy link
Copy Markdown
Member

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

I think it will make sense to branch off freshly from here again as #179 was experimental. I would close #179 and possibly delete the branch in that case.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for reading text files as media files

3 participants

@hagenw@maxschmitt@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support processing of text files - #176

Draft
hagenw wants to merge 4 commits into
mainfrom
process-text
Draft

Support processing of text files#176
hagenw wants to merge 4 commits into
mainfrom
process-text

Conversation

@hagenw

@hagenwhagenw commented Jun 27, 2024

Copy link
Copy Markdown
Member

Closes#173

...

TODO: implement a read_func argument, which can be used to provide a function to read in the file from disk. Maybe we don't need the extra _call_data() and process_data() methods then?

@hagenw
hagenw marked this pull request as draft June 27, 2024 14:07
@hagenwhagenw mentioned this pull request Jun 27, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

@ChristianGeng, @maxschmitt this pull request has the first ideas for adding support for JSON and TXT files to audinterface.Process.

My general idea is:

  • Try to make the processing methods independent of the underlying file/data as possible (at the moment I have separate methods for handling signal + sampling rate + start/end values and handling data without any additional information)
  • Support per default audio/video signals (everything that can be handled by audiofile) and json and txt files (see utils.read_text())
  • Add an read_function argument to audinterface.Process to allow the user to provide a function to handle general data/files. This would then allow to support any possible data and filetypes.

I will only be able to continue working on this from 2024/08/15. If you are interested in this, feel free to create another pull request with a solution.

@maxschmitt

Copy link
Copy Markdown
Contributor

Looks great, so far, and makes all sense to me. (Won't have much time to work on it either during August.)

@ChristianGeng

ChristianGeng commented Jul 26, 2024

Copy link
Copy Markdown
Member

Afaics all test stubs are there, with all test geared towards text processing failing.
So nicely implementing a radical test-first aproach ;-)

I would be motivated to get my hands on it in the calendar week starting Aug. 5,
of course I cannot promise but let's see - there might be conceptual questions regarding the implementation or time limitations.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Yes, no worries, just take a look if you have time. It might be that the tests are not complete yet, it's all work in progress. It also looks to me, that some of the tests should also be re-written in a less imperative way, e.g. maybe using a class.

@hagenw

Copy link
Copy Markdown
MemberAuthor

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

@ChristianGeng

Copy link
Copy Markdown
Member

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

Thanks for the hint! I had often been trying to circumvent the pencil and paper bit by using software to create call graphs automaticall: but I am seing that the good old manual stuff is probably still the best: my experience with the python packages for creating call graphs is worse than mixed and has not become better over the years. neither pyan, pycallgraph, pycallgraph2 were satisfactory. So I will go for the classical one too.

@ChristianGeng

ChristianGeng commented Aug 12, 2024

Copy link
Copy Markdown
Member

I have gone down the pathway that you recommended and created call graphs of the Process class.

For example, process_index (process_file, process_files, process_folder etc. go down a similar pathway). Basically things split up in _process_file where the pathway splits:

Signals: process_index -> _process_index_wo_segments -> _process_file -> _process_signal_ -> _call

Text data: process_index -> _process_index_wo_segments -> _process_file -> _process_data -> _call_data

The crucial problem that causes the tests in test_process_text.py to fail is a mismatch between the signature of process_func and process_func_args, as the non-signal pathway does not use a sampling_rate.

The most forward approach that I could come up with was to define a second functional called data_identity that needs to function as a replacement for identity when not dealing with signals but text.

Pointing to such a functon can probably be deferred until _call_data is reached.

However, whether dealing with signals or non-signal data has to be known as early as _process_index_wo_segments.

I have created a tentative merge request for now that does

  • determine whether dealing with text or signal in _process_index_wo_segments and sets a private attribute _processing_mode depending on file extensions
  • defer re-setting the identity function to _call_data
  • fix the tests for process_index only

While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

sampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
win_dur: Timestamp=None,
hop_dur: Timestamp=None,
min_signal_dur: Timestamp=None,
max_signal_dur: Timestamp=None,

So rather than determining whether text or signal "by hand", would it not be better to design the class to have separate the two strands initially, and have separate processing modes a priori (possibly use simple inheritance?)

To me it looks like you were already unhappy, and have created a stub of a public interface called process_data that seems to be unused so far. I would find it important to discuss in which direction this should go before really implementing it. Also I am currently unsure whether the way I adapted the tests of process_index respect the interaction between preserve_index and the segment argument correctly. In the same vein: I have modified the assertions related to preserve_index and am unconvinced that these modifications are correct.

see #179

@ChristianGengChristianGeng mentioned this pull request Aug 12, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

Thanks for proposing a fix for the failing tests. It is indeed unfortunate that we need to track inside the class if we process data or signals.


While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

There are indeed several differences between a sampled signal and other "data":

  • the whole handling of segments is only needed for signals
  • Process and other inherited classes have several arguments only relevant for signals
  • several methods/functions need to have a different signature for signals than for data (as we need the sampling_rate argument)

From a developer standpoint, all those points seem to indicate that we should indeed try to separate the implementation more and not add everything to the single Process class.

On the other hand, my main motivation so far was coming from a user perspective. For a user it is very convenient if the following code works independent of the underlying data/signals:

interface=audinterface.Process()
interface.process_index(index)

In summary, I still don't know what is the best solution for the desired data processing.

@ChristianGeng

ChristianGeng commented Aug 14, 2024

Copy link
Copy Markdown
Member

There are probably several ways to get both under the same umbrella without breaking the api. One way would be to go into the metaprogramming direction and have the `Process` class delegate the objec t creation to signal and text specific classes via `__new__`.

Untested, but would something like this do it?:

classProcessData(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etc
)
classProcessSignals(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etcsampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
)
classProcess(object):
def__new__(cls, **kwargs): # only have kwargs# possibly need to pop it from kwargsprocessing_mode=kwargs.get("processing_mode")
ifprocessing_mode=="signal":
return_ProcessSignals(kwargs)
ifprocessing_mode=="text":
return_ProcessData(kwargs)
defcommon_methods(self, **args):
print("define method common methods to both text and data")

@ChristianGeng

Copy link
Copy Markdown
Member

I have assembled an example call graph using pyan usind process_index as an example.

image

If one were to separate the text and signal strands more then it probably could be made more balanced.

Then the signal portion could possibly go down a path like this:

process_index
_process_index_wo_segment
_process_file
_process(_signal)
_call

One could possibly rename _process_signal into _process and in that strand (hence the parenthesis)
And for text (skipping _process_index_wo_segment):

process_index
_process_file
_process(_data)
_call(_data)

Suffixes (again parenthesized) could be stripped in order to "balance" the tree.
Not sure whether this is too simplistic.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

@ChristianGeng

ChristianGeng commented Sep 11, 2024

Copy link
Copy Markdown
Member

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

I think it will make sense to branch off freshly from here again as #179 was experimental. I would close #179 and possibly delete the branch in that case.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for reading text files as media files

3 participants

@hagenw@maxschmitt@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Support processing of text files - #176

Draft
hagenw wants to merge 4 commits into
mainfrom
process-text
Draft

Support processing of text files#176
hagenw wants to merge 4 commits into
mainfrom
process-text

Conversation

@hagenw

@hagenwhagenw commented Jun 27, 2024

Copy link
Copy Markdown
Member

Closes#173

...

TODO: implement a read_func argument, which can be used to provide a function to read in the file from disk. Maybe we don't need the extra _call_data() and process_data() methods then?

@hagenw
hagenw marked this pull request as draft June 27, 2024 14:07
@hagenwhagenw mentioned this pull request Jun 27, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

@ChristianGeng, @maxschmitt this pull request has the first ideas for adding support for JSON and TXT files to audinterface.Process.

My general idea is:

  • Try to make the processing methods independent of the underlying file/data as possible (at the moment I have separate methods for handling signal + sampling rate + start/end values and handling data without any additional information)
  • Support per default audio/video signals (everything that can be handled by audiofile) and json and txt files (see utils.read_text())
  • Add an read_function argument to audinterface.Process to allow the user to provide a function to handle general data/files. This would then allow to support any possible data and filetypes.

I will only be able to continue working on this from 2024/08/15. If you are interested in this, feel free to create another pull request with a solution.

@maxschmitt

Copy link
Copy Markdown
Contributor

Looks great, so far, and makes all sense to me. (Won't have much time to work on it either during August.)

@ChristianGeng

ChristianGeng commented Jul 26, 2024

Copy link
Copy Markdown
Member

Afaics all test stubs are there, with all test geared towards text processing failing.
So nicely implementing a radical test-first aproach ;-)

I would be motivated to get my hands on it in the calendar week starting Aug. 5,
of course I cannot promise but let's see - there might be conceptual questions regarding the implementation or time limitations.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Yes, no worries, just take a look if you have time. It might be that the tests are not complete yet, it's all work in progress. It also looks to me, that some of the tests should also be re-written in a less imperative way, e.g. maybe using a class.

@hagenw

Copy link
Copy Markdown
MemberAuthor

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

@ChristianGeng

Copy link
Copy Markdown
Member

The structure of the code itself inside audinterface/core/process.py is a little bit complex. For me it helped to create first an overview which method is calling which method on a sheet of paper to get an overview.

Thanks for the hint! I had often been trying to circumvent the pencil and paper bit by using software to create call graphs automaticall: but I am seing that the good old manual stuff is probably still the best: my experience with the python packages for creating call graphs is worse than mixed and has not become better over the years. neither pyan, pycallgraph, pycallgraph2 were satisfactory. So I will go for the classical one too.

@ChristianGeng

ChristianGeng commented Aug 12, 2024

Copy link
Copy Markdown
Member

I have gone down the pathway that you recommended and created call graphs of the Process class.

For example, process_index (process_file, process_files, process_folder etc. go down a similar pathway). Basically things split up in _process_file where the pathway splits:

Signals: process_index -> _process_index_wo_segments -> _process_file -> _process_signal_ -> _call

Text data: process_index -> _process_index_wo_segments -> _process_file -> _process_data -> _call_data

The crucial problem that causes the tests in test_process_text.py to fail is a mismatch between the signature of process_func and process_func_args, as the non-signal pathway does not use a sampling_rate.

The most forward approach that I could come up with was to define a second functional called data_identity that needs to function as a replacement for identity when not dealing with signals but text.

Pointing to such a functon can probably be deferred until _call_data is reached.

However, whether dealing with signals or non-signal data has to be known as early as _process_index_wo_segments.

I have created a tentative merge request for now that does

  • determine whether dealing with text or signal in _process_index_wo_segments and sets a private attribute _processing_mode depending on file extensions
  • defer re-setting the identity function to _call_data
  • fix the tests for process_index only

While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

sampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
win_dur: Timestamp=None,
hop_dur: Timestamp=None,
min_signal_dur: Timestamp=None,
max_signal_dur: Timestamp=None,

So rather than determining whether text or signal "by hand", would it not be better to design the class to have separate the two strands initially, and have separate processing modes a priori (possibly use simple inheritance?)

To me it looks like you were already unhappy, and have created a stub of a public interface called process_data that seems to be unused so far. I would find it important to discuss in which direction this should go before really implementing it. Also I am currently unsure whether the way I adapted the tests of process_index respect the interaction between preserve_index and the segment argument correctly. In the same vein: I have modified the assertions related to preserve_index and am unconvinced that these modifications are correct.

see #179

@ChristianGengChristianGeng mentioned this pull request Aug 12, 2024
@hagenw

Copy link
Copy Markdown
MemberAuthor

Thanks for proposing a fix for the failing tests. It is indeed unfortunate that we need to track inside the class if we process data or signals.


While it should be able to implennt this similarly for e.g. process_folder, I am not sure whether this is good design.

class Process has following kwargs in its constructor that are specific for signals in the first place:

There are indeed several differences between a sampled signal and other "data":

  • the whole handling of segments is only needed for signals
  • Process and other inherited classes have several arguments only relevant for signals
  • several methods/functions need to have a different signature for signals than for data (as we need the sampling_rate argument)

From a developer standpoint, all those points seem to indicate that we should indeed try to separate the implementation more and not add everything to the single Process class.

On the other hand, my main motivation so far was coming from a user perspective. For a user it is very convenient if the following code works independent of the underlying data/signals:

interface=audinterface.Process()
interface.process_index(index)

In summary, I still don't know what is the best solution for the desired data processing.

@ChristianGeng

ChristianGeng commented Aug 14, 2024

Copy link
Copy Markdown
Member

There are probably several ways to get both under the same umbrella without breaking the api. One way would be to go into the metaprogramming direction and have the `Process` class delegate the objec t creation to signal and text specific classes via `__new__`.

Untested, but would something like this do it?:

classProcessData(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etc
)
classProcessSignals(Process):
def__init__(
self,
*,
process_func: typing.Callable[..., typing.Any] =None,
process_func_args: typing.Dict[str, typing.Any] =None, # etcsampling_rate: int=None,
resample: bool=False,
channels: typing.Union[int, typing.Sequence[int]] =None,
mixdown: bool=False,
)
classProcess(object):
def__new__(cls, **kwargs): # only have kwargs# possibly need to pop it from kwargsprocessing_mode=kwargs.get("processing_mode")
ifprocessing_mode=="signal":
return_ProcessSignals(kwargs)
ifprocessing_mode=="text":
return_ProcessData(kwargs)
defcommon_methods(self, **args):
print("define method common methods to both text and data")

@ChristianGeng

Copy link
Copy Markdown
Member

I have assembled an example call graph using pyan usind process_index as an example.

image

If one were to separate the text and signal strands more then it probably could be made more balanced.

Then the signal portion could possibly go down a path like this:

process_index
_process_index_wo_segment
_process_file
_process(_signal)
_call

One could possibly rename _process_signal into _process and in that strand (hence the parenthesis)
And for text (skipping _process_index_wo_segment):

process_index
_process_file
_process(_data)
_call(_data)

Suffixes (again parenthesized) could be stripped in order to "balance" the tree.
Not sure whether this is too simplistic.

@hagenw

Copy link
Copy Markdown
MemberAuthor

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

@ChristianGeng

ChristianGeng commented Sep 11, 2024

Copy link
Copy Markdown
Member

Sounds good to me. Feel free to branch off from here and implement your changes (or continue from #179 if this makes more sense).

I think it will make sense to branch off freshly from here again as #179 was experimental. I would close #179 and possibly delete the branch in that case.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for reading text files as media files

3 participants

@hagenw@maxschmitt@ChristianGeng