Add Quick Test of the headphone jack

This commit is contained in:
Mathias Malmqvist
2025-09-30 00:48:05 +02:00
committed by dualshock-tools
parent 794485c265
commit e9778d44da
4 changed files with 93 additions and 27 deletions

View File

@@ -75,6 +75,14 @@ dl.row dd {
animation: glow 1.5s ease-in-out infinite !important;
}
.accordion-item:has(.accordion-collapse.show) i.fas.test-icon-lights {
animation: glow 1.2s ease-in-out infinite !important;
}
.accordion-item:has(.accordion-collapse.show) i.fas.test-icon-headphone {
animation: pulse 1s ease-in-out infinite !important;
}
/* Skip button hover behavior */
.skip-btn {
opacity: 0;

View File

@@ -382,14 +382,15 @@ class ControllerManager {
* Test speaker tone (DS5 only)
* @param {number} duration - Duration in milliseconds (optional)
* @param {Function} doneCb - Callback function called when tone ends (optional)
* @param {string} output - Audio output destination: "speaker" (default) or "headphones" (optional)
*/
async setSpeakerTone(duration = 1000, doneCb = ({success}) => {}) {
async setSpeakerTone(duration = 1000, doneCb = ({success}) => {}, output = "speaker") {
try {
if (!this.currentController.setSpeakerTone) {
throw new Error(this.l("Speaker tone not supported on this controller"));
}
await this.currentController.setSpeakerTone();
await this.currentController.setSpeakerTone(output);
// If duration is specified, automatically reset speaker after the duration
if (duration > 0) {

View File

@@ -685,25 +685,33 @@ class DS5Controller extends BaseController {
/**
* Test speaker tone by controlling speaker volume and audio settings
* This creates a brief audio feedback through the controller's speaker
* This creates a brief audio feedback through the controller's speaker or headphones
* @param {string} output - Audio output destination: "speaker" (default) or "headphones"
*/
async setSpeakerTone() {
async setSpeakerTone(output = "speaker") {
try {
const { validFlag0 } = this.currentOutputState;
const outputStruct = new DS5OutputStruct({
...this.currentOutputState,
speakerVolume: 85,
validFlag0: validFlag0 | DS5_VALID_FLAG0.SPEAKER_VOLUME | DS5_VALID_FLAG0.AUDIO_CONTROL,
headphoneVolume: 55,
validFlag0: validFlag0 | DS5_VALID_FLAG0.HEADPHONE_VOLUME | DS5_VALID_FLAG0.SPEAKER_VOLUME | DS5_VALID_FLAG0.AUDIO_CONTROL,
});
await this.sendOutputReport(outputStruct.pack(), 'play speaker tone');
outputStruct.validFlag0 &= ~(DS5_VALID_FLAG0.SPEAKER_VOLUME | DS5_VALID_FLAG0.AUDIO_CONTROL);
await this.sendOutputReport(outputStruct.pack(), output === "headphones" ? 'play headphone tone' : 'play speaker tone');
outputStruct.validFlag0 &= ~(DS5_VALID_FLAG0.HEADPHONE_VOLUME | DS5_VALID_FLAG0.SPEAKER_VOLUME | DS5_VALID_FLAG0.AUDIO_CONTROL);
// Send feature reports to enable speaker audio
// Audio configuration command
await this.sendFeatureReport(128, [6, 4, 0, 0, 8]);
// Enable speaker tone
await this.sendFeatureReport(128, [6, 2, 1, 1, 0]);
// Send feature reports to enable audio
if (output === "headphones") {
// Audio configuration command for headphones
await this.sendFeatureReport(128, [6, 4, 0, 0, 0, 0, 4, 0, 6]);
// Enable headphone tone
await this.sendFeatureReport(128, [6, 2, 1, 1, 0]);
} else {
// Audio configuration command for speakers
await this.sendFeatureReport(128, [6, 4, 0, 0, 8]);
// Enable speaker tone
await this.sendFeatureReport(128, [6, 2, 1, 1, 0]);
}
// Update current state to reflect the changes
this.updateCurrentOutputState(outputStruct);

View File

@@ -1,16 +1,6 @@
'use strict';
const ACCORDION_ELEMENTS = [
'usb-test-collapse',
'buttons-test-collapse',
'haptic-test-collapse',
'adaptive-test-collapse',
'lights-test-collapse',
'speaker-test-collapse',
'microphone-test-collapse'
];
const TEST_SEQUENCE = ['usb', 'buttons', 'haptic', 'adaptive', 'lights', 'speaker', 'microphone'];
const TEST_SEQUENCE = ['usb', 'buttons', 'haptic', 'adaptive', 'lights', 'speaker', 'headphone', 'microphone'];
const TEST_NAMES = {
'usb': 'USB Connector',
'buttons': 'Buttons',
@@ -18,7 +8,8 @@ const TEST_NAMES = {
'adaptive': 'Adaptive Trigger',
'lights': 'Lights',
'speaker': 'Speaker',
'microphone': 'Microphone'
'headphone': 'Headphone Jack',
'microphone': 'Microphone',
};
const BUTTONS = ['triangle', 'cross', 'circle', 'square', 'l1', 'r1', 'l2', 'r2', 'l3', 'r3', 'up', 'down', 'left', 'right', 'create', 'touchpad', 'options', 'ps', 'mute'];
@@ -76,6 +67,7 @@ export class QuickTestModal {
lights: null,
speaker: null,
microphone: null,
headphone: null,
microphoneStream: null,
microphoneContext: null,
microphoneMonitoring: false,
@@ -158,7 +150,8 @@ export class QuickTestModal {
'adaptive': 'fas fa-hand-pointer',
'lights': 'fas fa-lightbulb',
'speaker': 'fas fa-volume-up',
'microphone': 'fas fa-microphone'
'microphone': 'fas fa-microphone',
'headphone': 'fas fa-headphones'
};
const testContent = this._getTestContent(testType);
@@ -308,6 +301,26 @@ export class QuickTestModal {
</button>
</div>
`;
case 'headphone':
return `
<p class="ds-i18n">This test checks the headphone jack functionality.</p>
<p class="ds-i18n"><strong>Instructions:</strong></p>
<ol class="ds-i18n">
<li>Plug in headphones to the 3.5mm jack</li>
<li>Click "Test Speaker" to listen for the tone through the headphones</li>
</ol>
<div class="d-flex gap-2 mt-3">
<button type="button" class="btn btn-primary" id="headphone-test-btn" onclick="testHeadphoneAudio()">
<i class="fas fa-volume-up me-1"></i><span class="ds-i18n">Test Speaker</span>
</button>
<button type="button" class="btn btn-success" id="headphone-pass-btn" onclick="markTestResult('headphone', true)">
<i class="fas fa-check me-1"></i><span class="ds-i18n">Pass</span>
</button>
<button type="button" class="btn btn-danger" id="headphone-fail-btn" onclick="markTestResult('headphone', false)">
<i class="fas fa-times me-1"></i><span class="ds-i18n">Fail</span>
</button>
</div>
`;
default:
return '';
}
@@ -558,6 +571,9 @@ export class QuickTestModal {
case 'microphone':
this._startMicrophoneTest();
break;
case 'headphone':
// Headphone test is manual - no auto-start needed
break;
}
}, 100);
}
@@ -586,6 +602,9 @@ export class QuickTestModal {
case 'microphone':
this._stopMicrophoneTest();
break;
case 'headphone':
// Headphone test is manual - no stop needed
break;
}
// Update instructions when a test is collapsed
@@ -913,6 +932,29 @@ export class QuickTestModal {
$levelContainer.hide();
}
/**
* Test headphone audio output by playing through controller headphones
* This specifically routes audio to headphones instead of the built-in speaker
*/
async testHeadphoneAudio() {
this._startIconAnimation('headphone');
try {
// Play a test tone through the controller's headphone output
// The third parameter specifies "headphones" as the output destination
await this.controller.setSpeakerTone(500, ({success}) => {}, "headphones");
// Stop the animation after the tone completes
setTimeout(() => {
this._stopIconAnimation('headphone');
}, 700); // Slightly longer than tone duration
} catch (error) {
console.error('Error testing headphone audio:', error);
this._stopIconAnimation('headphone');
}
}
/**
* Mark test result and update UI
*/
@@ -1458,9 +1500,16 @@ function addTestBack(testType) {
}
}
function testHeadphoneAudio() {
if (currentQuickTestInstance) {
currentQuickTestInstance.testHeadphoneAudio();
}
}
// Legacy compatibility - expose functions to window for HTML onclick handlers
window.markTestResult = markTestResult;
window.resetAllTests = resetAllTests;
window.resetButtonsTest = resetButtonsTest;
window.skipTest = skipTest;
window.addTestBack = addTestBack;
window.addTestBack = addTestBack;
window.testHeadphoneAudio = testHeadphoneAudio;