Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement DECPS using DirectSound in place of MIDI #13471

Merged
3 commits merged into from
Jul 14, 2022

Conversation

j4james
Copy link
Collaborator

@j4james j4james commented Jul 9, 2022

Summary of the Pull Request

The original DECPS implementation made use of the Windows MIDI APIs to
generate the sound, but that required a 3MB package dependency for the
GS wavetable DLS. This PR reimplements the MidiAudio class using
DirectSound, so we can avoid that dependency.

References

The original DECPS implementation was added in PR #13208, but was
hidden behind a velocity flag in #13258.

PR Checklist

Detailed Description of the Pull Request / Additional comments

The way it works is by creating a sound buffer with a single triangle
wave that is played in a loop. We generate different notes simply by
adjusting the frequency at which that buffer is played.

When we need a note to end, we just set the volume to its minimum value
rather than stopping the buffer. If we don't do that, the repeated
starting and stopping tends to produce a lot of static in the output. We
also use two buffers, which we alternate between notes, as another way
to reduce that static.

One other thing worth mentioning is the handling of the buffer position.
At the end of each note we save the current position, and then use an
offset from that position when starting the following note. This helps
produce a clearer separation between tones when repeating sequences of
the same note.

In an ideal world, we should really have something like an attack-decay-
sustain-release envelope for each note, but the above hack seems to work
reasonably well, and keeps the implementation simple.

Validation Steps Performed

I've manually tested both conhost and Terminal with the sample tunes
listed in issue #8687, as well as a couple of games that I have which
make use of DECPS sound effects.

@ghost ghost added Area-VT Virtual Terminal sequence support Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-0 Bugs that we consider release-blocking/recall-class (P0) Product-Terminal The new Windows Terminal. labels Jul 9, 2022
@j4james j4james marked this pull request as ready for review July 9, 2022 17:31
{
if (SUCCEEDED(DirectSoundCreate8(nullptr, &_directSound, nullptr)))
{
if (SUCCEEDED(_directSound->SetCooperativeLevel(windowHandle, DSSCL_NORMAL)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can ServiceLocator::LocateConsoleWindow()->GetWindowHandle() return null?
Should we add the sample code from the remarks here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can ServiceLocator::LocateConsoleWindow()->GetWindowHandle() return null?

I don't think so. Technically it could be null briefly during startup, but the ConsoleAllocateConsole call blocks until InitWindowsSubsystem is done (that's where the window gets created). And if the window couldn't be created for some reason, then the ConsoleAllocateConsole call would fail, and there simply wouldn't be a console.

In conpty mode it's different. In that case ServiceLocator::LocateConsoleWindow itself would return null, but we also would never get this far, because the DECPS sequence would be passed through to the conpty client instead.

{
midiOut.OutputMessage(MidiOut::NOTE_ON, noteNumber, velocity);
// The formula for frequency is 2^(n/12) * 440Hz, where n is zero for
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for these comments

@zadjii-msft zadjii-msft added the AutoMerge Marked for automatic merge by the bot when requirements are met label Jul 14, 2022
@ghost
Copy link

ghost commented Jul 14, 2022

Hello @zadjii-msft!

Because this pull request has the AutoMerge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit bc79867 into microsoft:main Jul 14, 2022
@@ -1342,7 +1342,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (!_midiAudio)
{
_midiAudio = std::make_unique<MidiAudio>();
const auto windowHandle = reinterpret_cast<HWND>(_owningHwnd);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for @zadjii-msft: this will need to be changed when tearout changes the owning HWND

@DHowett
Copy link
Member

DHowett commented Jul 14, 2022

Thanks for doing this! I'm looking up DirectSoundCreate8 in the Windows Composition database right now to make sure it exists on OneCore (No big deal if it doesn't; we'll just need to not call this code during OneCore startup :))

It's interesting to note that the MIDI version had somewhat of an instrumental quality to it, whereas this one is very much more "simple tones." I am not sure which is better!

@j4james
Copy link
Collaborator Author

j4james commented Jul 14, 2022

It's interesting to note that the MIDI version had somewhat of an instrumental quality to it, whereas this one is very much more "simple tones." I am not sure which is better!

I think the MIDI version handled the note separation better than my hack for this, but I actually prefer the simple sound of this one. Although we can tweak that sound fairly easily if you want to try something else - you just change the WAVE_DATA at the top of the file. When I was testing, I played with a few of the other basic wave forms (sine, square, sawtooth), but the triangle wave was my favorite.

DHowett pushed a commit that referenced this pull request Jul 19, 2022
## Summary of the Pull Request

The original `DECPS` implementation made use of the Windows MIDI APIs to
generate the sound, but that required a 3MB package dependency for the
GS wavetable DLS. This PR reimplements the `MidiAudio` class using
`DirectSound`, so we can avoid that dependency.

## References

The original `DECPS` implementation was added in PR #13208, but was
hidden behind a velocity flag in #13258.

## PR Checklist
* [x] Closes #13252
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [x] I've discussed this with core contributors already. Issue number
where discussion took place: #13252

## Detailed Description of the Pull Request / Additional comments

The way it works is by creating a sound buffer with a single triangle
wave that is played in a loop. We generate different notes simply by
adjusting the frequency at which that buffer is played.

When we need a note to end, we just set the volume to its minimum value
rather than stopping the buffer. If we don't do that, the repeated
starting and stopping tends to produce a lot of static in the output. We
also use two buffers, which we alternate between notes, as another way
to reduce that static.

One other thing worth mentioning is the handling of the buffer position.
At the end of each note we save the current position, and then use an
offset from that position when starting the following note. This helps
produce a clearer separation between tones when repeating sequences of
the same note.

In an ideal world, we should really have something like an attack-decay-
sustain-release envelope for each note, but the above hack seems to work
reasonably well, and keeps the implementation simple.

## Validation Steps Performed

I've manually tested both conhost and Terminal with the sample tunes
listed in issue #8687, as well as a couple of games that I have which
make use of `DECPS` sound effects.

(cherry picked from commit bc79867)
Service-Card-Id: 84270205
Service-Version: 1.15
@ghost
Copy link

ghost commented Aug 5, 2022

🎉Windows Terminal Preview v1.15.200 has been released which incorporates this pull request.:tada:

Handy links:

@j4james j4james deleted the decps-using-directsound branch August 31, 2022 00:24
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-VT Virtual Terminal sequence support AutoMerge Marked for automatic merge by the bot when requirements are met Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-0 Bugs that we consider release-blocking/recall-class (P0) Product-Terminal The new Windows Terminal.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

We might need another dependency for MIDI support
4 participants