Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Aug 14 00:57
    Danesprite commented #369
  • Aug 11 20:41
    denvaar edited #369
  • Aug 11 20:26
    denvaar edited #369
  • Aug 11 20:24
    denvaar opened #369
  • Aug 11 04:47
    Danesprite commented #368
  • Aug 11 03:07
    denvaar commented #368
  • Aug 10 09:20
    Danesprite labeled #368
  • Aug 10 09:20
    Danesprite labeled #368
  • Aug 10 09:20
    Danesprite assigned #368
  • Aug 10 09:19
    Danesprite commented #368
  • Aug 05 16:34
    denvaar edited #368
  • Aug 05 16:28
    denvaar opened #368
  • Aug 05 02:36

    Danesprite on master

    Remove broken link from the doc… (compare)

  • Jul 31 08:05

    Danesprite on master

    Correct errors related to Natli… (compare)

  • Jul 23 04:02

    Danesprite on master

    Correct errors related to Natli… (compare)

  • Jul 23 01:47

    Danesprite on master

    Fix a load-related error in the… Correct errors related to Natli… (compare)

  • Jul 19 13:10

    Danesprite on master

    Fix an error in the Optional el… (compare)

  • Jul 19 12:43

    Danesprite on master

    Add a workaround for a DNS erro… (compare)

  • Jul 19 12:12

    Danesprite on master

    Fix an encoding-related error i… Change the Natlink engine to us… (compare)

  • Jul 19 07:19
    Danesprite commented #354
Dane Finlay
@Danesprite
@daanzu Yep, something like that. Maybe this functionality should be covered by the unit tests?
LexiconCode
@LexiconCode

I wonder if it would be worthwhile treating command with dictation elements and raw dictation elements special in dragonfly parsing. Raw dictation elements would be used a lot more if they were not greedy. While there's nothing we can do to stop them from being greedy per se but maybe we can change the order of when dragonfly evaluates by always evaluating them at the end rather than just in the context of the grammar. So during parsing a the dictation element in a grammar the context and the command dictation element could be extracted in the current Dictation element ignored. Placing both of those at the end to ensure that there are evaluated last. If this is technically possible it would allow for free dictation like we see in DNS for all engines.

However I think this would mean we would have to implement mechanism rather than letting the engine decide what is a command versus what is free dictation. Which can be buggy at least in DNS/DPI. After all dragonfly knows which elements are dictation/commands before loading the grammar into the engine.

Quintijn Hoogenboom
@quintijn
My experience with Natlink let me conclude that indeed the dictation part should be at the end of an utterance. So for Dragon too.
LexiconCode
@LexiconCode
So this is less to do of where a dictation element is in a spec. This has to do more of when the spec is evaluated for recognition.
Dane Finlay
@Danesprite

Dictation elements are not really greedy. If there is a command that can be spoken afterwards, then, provided the SR engine back-end supports dictation, it should be recognised and parsed just fine.

I'm not really willing to rewrite the parsing code to handle the buggy behaviour with DNS that you mention. The changes I made in PR #268 should have solved that in most cases.

You may be interested in the following dictation-related example files Christo Butcher (t4ngo) wrote at some point:

LexiconCode
@LexiconCode

Dictation elements are not really greedy. If there is a command that can be spoken afterwards, then, provided the SR engine back-end supports dictation, it should be recognised and parsed just fine.

I'm not really willing to rewrite the parsing code to handle the buggy behaviour with DNS that you mention. The changes I made in PR #268 should have solved that in most cases.

You may be interested in the following dictation-related example files Christo Butcher (t4ngo) wrote at some point:

Thanks for the info! I will look into that.

Ryan Hileman
@lunixbochs
I've found that parsing top level dictation is a harder parser problem and the dragonfly semi greedy rule at a time parser isn't especially well suited to that kind of problem
Dane Finlay
@Danesprite

@LexiconCode No worries. If you like, you can experiment with custom recognition parsing/decoding using the DgnImported class in the test_dgn_import.py as a starting point.

@lunixbochs I agree it is a harder problem and that there are better ways to do this. Personally, I think the current lesser solution works well enough.

I am open to an improved solution for differentiating between dictation and command words. This would be useful for the Natlink and text-input engine back-ends.

David Zurow
@daanzu
In dragonfly, doesn't the engine already determine what part of the utterance is dictation and what isn't?
@Danesprite BTW, the new (to me) Modifier element is delightful!
Dane Finlay
@Danesprite

@daanzu Yes, that is how it is supposed to work and how it does work with SAPI5 and Kaldi. DNS, however, occasionally doesn't report nicely differentiated words, leaving that task to us. The other two engine back-ends ("sphinx" and "text") have limited support for dictation.

The Modifier element was added by mrob95. It is delightful, isn't it? ^^

David Zurow
@daanzu
@Danesprite ah, thanks for the explanation
Dane Finlay
@Danesprite
:+1:
Ryan Hileman
@lunixbochs

DNS, however, occasionally doesn't report nicely differentiated words, leaving that task to us.

I've found the precise behavior to vary per Dragon version as well, which means you can't especially rely on it

If dragonfly at some point added raw wav2letter support, my wav2letter decoder only sometimes differentiates between dictation and commands - that was a low priority for me as the Talon parser ignores that information anyway. It won't be an issue for dragonfly hosted in Talon, however, as you'll be downstream from my parser
Dane Finlay
@Danesprite

Thanks, that's good to know. If an engine back-end is to be added that uses wav2letter directly, then I'll revisit this and see what I can do. I'll give some thought to improving the solution added in #268.

There is an open issue for wav2letter engine support: #245. Although, to my knowledge, there hasn't been much activity on that recently.

Ryan Hileman
@lunixbochs
I did just push a wav2letter engine backend to my dragonfly fork in the form of Talon support (Dragonfly is generally working in Talon now, as far as I can tell with simple scripts, including auto-CCR and file autoreload on save)
Ryan Hileman
@lunixbochs
I still need a more advanced Dragonfly user to test it but the Talon side of this can ship as experimental in the next public release in a few days
Quintijn Hoogenboom
@quintijn
@Danesprite thanks for your explanation. I will stay outside this discussion :-)
Dane Finlay
@Danesprite
@lunixbochs Okay then, sounds good. I'll have a look at the code and I'm happy to do some testing for you.

@quintijn No worries :-)

It is a pity this a DNS problem, rather than a Natlink one we could fix.

Ryan Hileman
@lunixbochs
I know I'm pushing it but if you run under DNS in Talon, it reparses the phrases for you and fixes that (because the dragonfly integration runs after the talon parser)
Dane Finlay
@Danesprite
Yep, that is another plus for integration with Talon :-)
Ryan Hileman
@lunixbochs
are folks open to prefixing the logger names? e.g. logging.getLogger("engine") -> logging.getLogger("dragonfly.engine")
Ryan Hileman
@lunixbochs

is there a specific reason the decorator module is used in dragonfly.rpc? the usage seems equivalent to:

import functools
def _rpc_method_wrapper(method, *args, **kwargs):
    ...

def _rpc_method(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        return _rpc_method_wrapper(method, *args, **kwargs)
    return wrapper

and switching to the above approach would make integration easier, as I ran into an issue with the decorator module

Dane Finlay
@Danesprite

@lunixbochs
I think prefixing the logger names should be fine. You use some of the same names in Talon?

Yeah, if that is equivalent, then the decorator module isn't really needed.

Ryan Hileman
@lunixbochs
yeah just trying to avoid ambiguity as most of the logger names are concepts in both dragonfly and talon
it seems like some of your users are probably using the old logger names in their own code right now fwiw, as the example runners set a bunch of the log levels
Ryan Hileman
@lunixbochs
I'm open to pushback, code review, or different opinions on the logger change
Dane Finlay
@Danesprite

Okay then, make sense. Given that some users set logging levels using the current names, maybe this should be held off until version 1.0.0. I intend to make a few other changes like this in that release, e.g. resolving issue #238 (Modularize dependencies).

Your changes removing the decorator dependency look good to me.
Incidentally, the requirements for dragonfly.rpc will be made optional extras in version 1.0.0. I will probably remove the relevant import statements from dragonfly/__init__.py.

Ryan Hileman
@lunixbochs
ok, going to submit a separate PR for the logging changes
I'll submit a PR for the talon integration now. the only thing really missing I think is it still depends on pyperclip instead of using talon's own clipboard backend
(as otherwise it wouldn't have any dependencies, I monkeypatch regex -> re)
Ryan Hileman
@lunixbochs
just opened #326
Dane Finlay
@Danesprite
Okay then, sounds good. Thanks :+1:
Dane Finlay
@Danesprite
I've just about completed a refactor of Dragonfly's clipboard code (PR #325), so that's probably just as well. That should make a Talon back-end easier for you to add at a later date if you want. I'm sure that would be better for any macOS users (at least).
Ryan Hileman
@lunixbochs
ah perfect the structure was the main reason I didn't mess with it
Dane Finlay
@Danesprite
Yeah fair, the clipboard code is a bit of a mess.
LexiconCode
@lexicon-code:matrix.org
[m]

When working with extras it would be nice in some circumstances to remember the last extra recognized for that particular spec. Let's consider the following command select word [<left_or_right>] [<n>] The user says select word left and then again says select word left extra would be remembered.

Now we could set a directional default in extras. However in more complex scenarios it's not that simple. Now wrapping this up in the function would work but it really does pollute grammar sets making them harder to read. Any thoughts on implementation or should I stick to using a function?

Ryan Hileman
@lunixbochs
can you make a kind of sticky RuleRef
David Zurow
@daanzu
@LexiconCode I can't think of a clean way to do this other than as you said a function
Quintijn Hoogenboom
@quintijn
In voicecode, "long time ago", there was the "again" command, which could repeat the last performed move or select action. Even again three times etc.
Dane Finlay
@Danesprite

If you do go with a function, then you can get at the underlying rule by including a _rule argument:

def function(_rule):
    print(_rule)
    print(_rule._defaults["left_or_right"])

An alternative is a separate command for explicitly changing your default value for left_or_right. This would change it for all mappings using the extra.

I think Quintijn is right here though, repeating the last action is probably easier.

Quintijn Hoogenboom
@quintijn
In voicecode quite a lot of navigation and selection commands were coupled to "again", and this seemed to work quite well!
Shervin Emami
@shervinemami
Nice, so is there an easy way to get Dragonfly to playback a previous command? I did set up an elaborate way so I can play back my recent commands by saying things like "play 3" as well as to record macros and play them back too such as "play inbox". But it took noticeable effort to set it all up, with code in something like 8 files! So I haven't put in the effort to document it and make it public. But if there was an easy method to playback or read the last command using just 1 or 2 lines of code then I can imagine many people would be interested!
David Zurow
@daanzu
@shervinemami I think I recall an example using a recognition observer in some of the old original dragonfly examples
Dane Finlay
@Danesprite
I think Caster has this functionality. I remember Christo Butcher's _cmdmemory.py module being quite useful for that.
LexiconCode
@LexiconCode
Caster again do command is similar to what Quintijn described. This looks much more complex than it is due to utilizing Caster asynchronous actions. It could be converted utilizing to pure dragonfly. This allows to repeat not only the last command but the last utterance. Useful for CCR chains or commands with dragonfly repetition elements.
# migrated this function from elsewhere for completeness of the post.
def get_and_register_history(utterances=10):
    history = RecognitionHistory(utterances)
    history.register()
    return history

_history = get_and_register_history(10)

class Again(MappingRule):

    mapping = {
        "again (<n> [(times|time)] | do)":
            R(Function(lambda n: Again._create_asynchronous(n)), show=False), 
    }
    extras = [ShortIntegerRef("n", 1, 50)]
    defaults = {"n": 1}

    @staticmethod
    def _repeat(utterance):
        Playback([(utterance, 0.0)]).execute()
        return False 

    @staticmethod
    def _create_asynchronous(n):
        last_utterance_index = 2
        if len(_history) == 0:
            return

        # ContextStack adds the word to history before executing it for WSR 
        if get_current_engine().name in ["sapi5shared", "sapi5", "sapi5inproc"]:  
            if len(_history) == 1: return

        # Calculatees last utterance from recognition history and creates list of str for Dragonfly Playback
        utterance = list(map(str, _history[len(_history) - last_utterance_index]))

        if utterance[0] == "again": return
        # Create AsynchronousAction
        forward = [L(S(["cancel"], lambda: Again._repeat(utterance)))]
        AsynchronousAction(
            forward,
            rdescript="Repeat Last Action",
            time_in_seconds=0.2,
            repetitions=int(n),
            blocking=False).execute()