How to disabling DTMF for specific extensions in Asterisk

Hello together,

Is there a way to use dtmf_mode=auto in pjsip.conf and set dtmf_mode=none for extension 9000 in extensions.conf?

I tried it like this:

same => n,Set(DTMFMode=none)

But I get the output:

[2025-02-02 13:54:35] DTMF[19226][C-00000014]: channel.c:4019 __ast_read: DTMF begin '4' received on PJSIP/1002-00000013
[2025-02-02 13:54:35] DTMF[19226][C-00000014]: channel.c:4030 __ast_read: DTMF begin passthrough '4' on PJSIP/1002-00000013
[2025-02-02 13:54:35] DTMF[19228][C-00000014]: channel.c:4019 __ast_read: DTMF begin '4' received on Local/9000@extension-0000000d;2
[2025-02-02 13:54:35] DTMF[19228][C-00000014]: channel.c:4030 __ast_read: DTMF begin passthrough '4' on Local/9000@extension-0000000d;2

I am as always grateful for any tips and wish you a great weekend.

This is done with a function in chan_pjsip:

https://docs.asterisk.org/Asterisk_18_Documentation/API_Documentation/Dialplan_Functions/PJSIP_DTMF_MODE/

and with an application, in the obsolete chan_sip. In neither case is the name just DTMFMode.

Also, I’m pretty sure that you have asked about defeating DTMF before, in which case this should have been posted in the same thread, so as to provide context on why you are trying to do it.

Note that the chan_sip one, at least, doesn’t support the value “none”:

chan_pjsip does seem to support “none”:

Hi David,

Thank you for your reply. The problem is that DTMF is recognized as text during SpeechBackground(), which is why I want to disable DTMF on this extension!

[outgoing]
exten => 9000,1,Set(PJSIP_DTMF_Mode=none)
same => n,Dial(Local/9000@cfd-hotline) ← also tried Goto(cfd-hotline,9000,1)
exten => 9001,1,Answer()
same => n,ChanSpy(,g(9000),q)

[cfd-hotline]
exten = 9000,1,Answer()
same => n,Set(SPYGROUP=9000)
same => n,Set(DENOISE(rx)=on)
same => n,Set(DEVICE_STATE(Custom:9000)=INUSE)
same => n,Wait(1)
same => n(start),SpeechCreate(my-speech-to-text)
same => n,SpeechStart()
same => n,SpeechBackground()
same => n,Verbose(1,Recognized Text: ${SPEECH_TEXT(0)})
same => n,Set(SPEECH_RESULT=${SPEECH_TEXT(0)})
same => n,Set(SPEECH_RESULT=${TOLOWER(${SPEECH_RESULT})})
same => n,SpeechDestroy()

That is not the correct capitalisation. Function and variable names are case sensitive. I’d also explicitly inlucde the (), although I htink it will still be recognized as a function, with no parameters, without them.

Could you give me a full example for extensions.conf please?

exten => 9000,1,Set(PJSIP_DTMF_MODE()=none)

1 Like

Thanks David, but the problem still exists:

    -- Executing [9000@outgoing:1] Set("PJSIP/1002-00000011", "PJSIP_DTMF_MODE()=none") in new stack
    -- Executing [9000@outgoing:2] Dial("PJSIP/1002-00000011", "Local/9000@cfd-hotline") in new stack
    -- Called Local/9000@cfd-hotline
    -- Executing [9000@cfd-hotline:1] Answer("Local/9000@cfd-hotline-0000000f;2", "") in new stack
    -- Local/9000@cfd-hotline-0000000f;1 answered PJSIP/1002-00000011
       > 0x7f2bbc28c350 -- Strict RTP learning after remote address set to: 10.0.0.3:4660
    -- Channel Local/9000@cfd-hotline-0000000f;1 joined 'simple_bridge' basic-bridge <a9b9de22-7875-4c7f-9d0f-878e9639aeea>
    -- Channel PJSIP/1002-00000011 joined 'simple_bridge' basic-bridge <a9b9de22-7875-4c7f-9d0f-878e9639aeea>
       > 0x7f2bbc28c350 -- Strict RTP switching to RTP target address 10.0.0.3:4660 as source
    -- Executing [9000@cfd-hotline:2] Set("Local/9000@cfd-hotline-0000000f;2", "SPYGROUP=9000") in new stack
    -- Executing [9000@cfd-hotline:3] Set("Local/9000@cfd-hotline-0000000f;2", "DENOISE(rx)=on") in new stack
    -- Executing [9000@cfd-hotline:4] Set("Local/9000@cfd-hotline-0000000f;2", "DEVICE_STATE(Custom:9000)=INUSE") in new stack
    -- Executing [9000@cfd-hotline:5] Wait("Local/9000@cfd-hotline-0000000f;2", "1") in new stack
    -- Executing [9000@cfd-hotline:6] SpeechCreate("Local/9000@cfd-hotline-0000000f;2", "my-speech-to-text") in new stack
    -- Executing [9000@cfd-hotline:7] SpeechStart("Local/9000@cfd-hotline-0000000f;2", "") in new stack
    -- Executing [9000@cfd-hotline:8] SpeechBackground("Local/9000@cfd-hotline-0000000f;2", "") in new stack
[2025-02-02 15:12:30] DTMF[6915][C-00000012]: channel.c:3905 __ast_read: DTMF end '7' received on PJSIP/1002-00000011, duration 300 ms
[2025-02-02 15:12:30] DTMF[6915][C-00000012]: channel.c:3932 __ast_read: DTMF begin emulation of '7' with duration 300 queued on PJSIP/1002-00000011
[2025-02-02 15:12:30] DTMF[6916][C-00000012]: channel.c:4019 __ast_read: DTMF begin '7' received on Local/9000@cfd-hotline-0000000f;2
[2025-02-02 15:12:30] DTMF[6916][C-00000012]: channel.c:4030 __ast_read: DTMF begin passthrough '7' on Local/9000@cfd-hotline-0000000f;2
       > 0x7f2bbc28c350 -- Strict RTP learning complete - Locking on source address 10.0.0.3:4660
[2025-02-02 15:12:30] DTMF[6915][C-00000012]: channel.c:4053 __ast_read: DTMF end emulation of '7' queued on PJSIP/1002-00000011
[2025-02-02 15:12:30] DTMF[6916][C-00000012]: channel.c:3905 __ast_read: DTMF end '7' received on Local/9000@cfd-hotline-0000000f;2, duration 300 ms
[2025-02-02 15:12:30] DTMF[6916][C-00000012]: channel.c:3956 __ast_read: DTMF end accepted with begin '7' on Local/9000@cfd-hotline-0000000f;2
[2025-02-02 15:12:30] DTMF[6916][C-00000012]: channel.c:3994 __ast_read: DTMF end passthrough '7' on Local/9000@cfd-hotline-0000000f;2
[2025-02-02 15:12:30] DTMF[6915][C-00000012]: channel.c:3905 __ast_read: DTMF end '8' received on PJSIP/1002-00000011, duration 300 ms
[2025-02-02 15:12:30] DTMF[6915][C-00000012]: channel.c:3932 __ast_read: DTMF begin emulation of '8' with duration 300 queued on PJSIP/1002-00000011
[2025-02-02 15:12:30] DTMF[6916][C-00000012]: channel.c:4019 __ast_read: DTMF begin '8' received on Local/9000@cfd-hotline-0000000f;2
[2025-02-02 15:12:30] DTMF[6916][C-00000012]: channel.c:4030 __ast_read: DTMF begin passthrough '8' on Local/9000@cfd-hotline-0000000f;2
[2025-02-02 15:12:31] DTMF[6915][C-00000012]: channel.c:4053 __ast_read: DTMF end emulation of '8' queued on PJSIP/1002-00000011
[2025-02-02 15:12:31] DTMF[6916][C-00000012]: channel.c:3905 __ast_read: DTMF end '8' received on Local/9000@cfd-hotline-0000000f;2, duration 300 ms
[2025-02-02 15:12:31] DTMF[6916][C-00000012]: channel.c:3956 __ast_read: DTMF end accepted with begin '8' on Local/9000@cfd-hotline-0000000f;2
[2025-02-02 15:12:31] DTMF[6916][C-00000012]: channel.c:3994 __ast_read: DTMF end passthrough '8' on Local/9000@cfd-hotline-0000000f;2
[2025-02-02 15:12:36] ERROR[6924]: res_aeap/aeap.c:204 raise_msg_handler: AEAP (0x7f2bb401f350): Missing request parameters
    -- Executing [9000@cfd-hotline:9] Verbose("Local/9000@cfd-hotline-0000000f;2", "1,Erkannter Text: 78") in new stack
 Erkannter Text: 78
    -- Executing [9000@cfd-hotline:10] Set("Local/9000@cfd-hotline-0000000f;2", "SPEECH_RESULT=78") in new stack
    -- Executing [9000@cfd-hotline:11] Set("Local/9000@cfd-hotline-0000000f;2", "SPEECH_RESULT=78") in new stack

[outgoing]
exten => 9000,1,Set(PJSIP_DTMF_MODE()=none)
same => n,Dial(Local/9000@cfd-hotline)
exten => 9001,1,Answer()
same => n,ChanSpy(,g(9000),q)

[cfd-hotline]
exten = 9000,1,Answer()
same => n,Set(SPYGROUP=9000)
same => n,Set(DENOISE(rx)=on)
same => n,Set(DEVICE_STATE(Custom:9000)=INUSE)
same => n,Wait(1)

What was the original mode?

I don’t think that changing the mode in this way has any effect on the other end, and I’m not sure that it would make Asterisk ignore out of band DTMF that is received. I suspect it would only work for you if the original mode was inband, where the code does seem to turn off the tone decoder.

Also note that, if the other end is using INFO or telephony events, there will almost certainly be a drop out in the audio during the talk off episodes. RFC4733 replaces the audio stream, but I’d also expect it to be muted when sending INFO.

It’s not about the influence of the other end, but about influencing Asterisk so that it doesn’t accept DTMF on a local channel. It must be possible to do this somehow.

Your real problem here seems to be an undocumented feature in SpeechBackground, that has been there for at least 17 years, so presumably hasn’t been considered a problem by anyone else.

What it does is that, once it receives any DTMF, it goes into a mode in which it returns a string containing the DTMF sequence, in place of any recognized text. There are some options allowing early termination, as well, based on DTMF, in which the terminating digit might not be captured, and the transcription might go ahead (although I haven’t followed those code paths in that much detail).

There are also undocumented channel variables which affect its behaviour.

I’d suggest that the most reliable way of disabling this feature is to edit and recompile apps/app_speech_utils.c, and remove the case that handles AST_FRAME_DTMF, from, the main loop. The edit should be obvious, even if don’t know much about C.

You might want to add a github feature request to make the undocumented DTMF features optional, and a bug report, for the documentation, to document the feature, and associated channel variables.

You will still have gaps in the audio, where the talk off occurred.

Thank you, you’re great. It helped to modify the line in app_speech_utils.c as follows:

			case AST_FRAME_DTMF:
				if (dtmf_terminator != '\0' && f->subclass.integer == dtmf_terminator) {
					done = 1;
				} else {
					quieted = 1;
					if (ast_channel_stream(chan) != NULL) {
						ast_stopstream(chan);
					}
					if (!started) {
						/* Change timeout to be 5 seconds for DTMF input */
						timeout = (ast_channel_pbx(chan) && ast_channel_pbx(chan)->dtimeoutms) ? ast_channel_pbx(chan)->dtimeoutms : 5000;
						started = 1;
					}
					start = ast_tvnow();
					snprintf(tmp, sizeof(tmp), "%c", f->subclass.integer);
					strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1);
					/* If the maximum length of the DTMF has been reached, stop now */
					if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
						done = 1;
				}
				break;

to:

			case AST_FRAME_DTMF:
				break;

Then, go to /usr/src/asterisk-VERSION and run make && make install as you said.

In the Dialplan, I have now added the following:

[cfd-hotline]
exten = 9000,1,Answer()
same => n,Set(SPYGROUP=9000)
same => n,Set(DENOISE(rx)=on)
same => n,Set(DEVICE_STATE(Custom:9000)=INUSE)
same => n,Wait(1)

; Start speech processing
same => n(start),SpeechCreate(my-speech-to-text)
same => n,SpeechStart()
same => n,Set(TIMEOUT(digit)=0) <— Important to prevent delay
same => n,SpeechBackground()
same => n,Verbose(1,Recognized text: ${SPEECH_TEXT(0)})

; Save the recognized text and convert it to lowercase
same => n,Set(SPEECH_RESULT=${SPEECH_TEXT(0)})
same => n,Set(SPEECH_RESULT=${TOLOWER(${SPEECH_RESULT})})
same => n,SpeechDestroy()

; Check if the result matches a greeting
same => n,Set(MATCH_RESULT=0)

; Match greetings with a regex
same => n,ExecIf($[”${SPEECH_RESULT}” =~ “^(a|lot|of|words|that|I|want|to|recognize)$”]?Set(MATCH_RESULT=1))

; If MATCH_RESULT is 1 (successful regex match), go to success
same => n,ExecIf($[”${MATCH_RESULT}” = “1”]?Goto(success))

; If no match - continue with regular processing
same => n,Goto(start)
…

The result is that DTMF is still recognized, but it is no longer considered during SpeechBackground. When a DTMF input is received, the call flow starts again at the label (start) and SpeechBackground() is called again.