@pro-gram/flux 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.txt +4 -3
- package/README.md +42 -8
- package/dependencies.json +2 -1
- package/package.json +3 -3
- package/payload/DrumBuddy-Setup.exe +0 -0
- package/scripts/install.js +4 -2
- package/source/AssetBootstrap.cs +1 -1
- package/source/Audio Decoders.txt +230 -0
- package/source/AudioDeviceSettings.cs +1 -1
- package/source/AudioEndpointWatcher.cs +25 -0
- package/source/AudioOutputs.cs +40 -60
- package/source/AudioWorkspace.cs +2 -1
- package/source/BrandArtwork.cs +2 -2
- package/source/Build Instructions.txt +9 -3
- package/source/Build.ps1 +21 -9
- package/source/EditorCompactLayout.cs +36 -0
- package/source/EditorPlaybackTests.cs +87 -0
- package/source/EditorSelectionTests.cs +73 -0
- package/source/EditorToolInteractions.cs +227 -0
- package/source/EditorTools.cs +78 -0
- package/source/EditorToolsTests.cs +93 -0
- package/source/EditorView.cs +247 -75
- package/source/Flux-Logo.png +0 -0
- package/source/InterfaceSettings.cs +10 -4
- package/source/InterfaceTests.cs +6 -0
- package/source/Main.cs +19 -13
- package/source/Main.xaml +25 -28
- package/source/PlaybackTests.cs +13 -6
- package/source/Quick Start.txt +28 -6
- package/source/SettingsWindow.cs +4 -3
- package/source/Setup.cs +9 -5
- package/source/Third Party Notices.txt +4 -3
- package/source/Timeline.cs +24 -17
- package/source/TimelineZoom.cs +21 -0
- package/source/WaveformDetail.cs +104 -0
- package/source/WindowsAudioPlayer.cs +137 -0
- package/source/WorkspaceTests.cs +7 -3
- package/source/ZoomTests.cs +95 -0
package/NOTICE.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Flux 1.
|
|
1
|
+
Flux 1.6.1 - third-party components
|
|
2
2
|
|
|
3
3
|
FFmpeg 9.0.2, release essentials build by Gyan Doshi
|
|
4
4
|
Used as a separate command-line decoding process.
|
|
@@ -20,8 +20,9 @@ Copyright (c) .NET Foundation and Contributors. MIT license.
|
|
|
20
20
|
https://github.com/dotnet/runtime/blob/main/LICENSE.TXT
|
|
21
21
|
|
|
22
22
|
Zoom H5studio and R16/R24 drivers: linked from Zoom's official support pages, not bundled.
|
|
23
|
-
R16 reference panel image: copyright Zoom Corporation. The
|
|
24
|
-
|
|
23
|
+
R16 reference panel image: copyright Zoom Corporation. The reference image
|
|
24
|
+
is included from Zoom's public product website; its source URL and SHA-256
|
|
25
|
+
are recorded in dependency-lock.json.
|
|
25
26
|
Zoom and R16 are trademarks of Zoom Corporation. This is an independent app.
|
|
26
27
|
|
|
27
28
|
The original drummer mascot was generated for Drum Buddy using OpenAI's
|
package/README.md
CHANGED
|
@@ -7,10 +7,21 @@ Flux is a compact Windows audio studio with a 16-track editor/recorder, a Media
|
|
|
7
7
|
Close the running audio application, then:
|
|
8
8
|
|
|
9
9
|
```powershell
|
|
10
|
-
npm
|
|
11
|
-
flux
|
|
10
|
+
npm install -g @pro-gram/flux@latest
|
|
11
|
+
flux --setup
|
|
12
|
+
flux
|
|
12
13
|
```
|
|
13
14
|
|
|
15
|
+
**If npm skips setup or the Desktop shortcut is missing, run `flux --setup`.** This completes the desktop installation and creates the Flux Desktop and Start menu shortcuts. When npm allows the setup script, these steps happen automatically. An `allowScripts` notice can also appear after a successful installation. The first `flux` launch completes setup if it is needed.
|
|
16
|
+
|
|
17
|
+
To allow Flux's setup script for one installation:
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
npm install -g @pro-gram/flux@latest --allow-scripts=@pro-gram/flux
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If PowerShell blocks the `npm.ps1` or `flux.ps1` launcher, use `npm.cmd` and `flux.cmd` for the same commands.
|
|
24
|
+
|
|
14
25
|
Windows 10/11 x64 is supported. macOS is planned. Basic tools work locally without an account.
|
|
15
26
|
|
|
16
27
|
### Moving from Drum Buddy / FluxTempo
|
|
@@ -25,17 +36,31 @@ flux.cmd
|
|
|
25
36
|
|
|
26
37
|
Removing that npm package leaves its separate desktop installation and audio files in place. Flux updates the existing desktop installation and uses its saved folders/settings. The launch command is now `flux.cmd`; older launchers belong to the old npm package and should be removed during migration.
|
|
27
38
|
|
|
28
|
-
If npm skipped the install script, the first launch completes setup. `flux.cmd --setup` repairs the installation. The Windows executable is unsigned.
|
|
39
|
+
If npm skipped the install script, the first launch completes setup. `flux.cmd --setup` repairs the installation. The Windows executable is unsigned. The npm and standalone installers both include FFmpeg and the R16 panel assets, with payload integrity checks. There is no separate codec download during setup. Vendor audio drivers are linked from their official support pages.
|
|
29
40
|
|
|
30
41
|
## Editor and recording
|
|
31
42
|
|
|
32
43
|
Editor opens by default. File contains New, Open session, Save, Save as and Add audio. Home and View open Converter, Media Player and the R16 workspace.
|
|
33
44
|
|
|
45
|
+
The compact header puts navigation beside the logo. Grid and zoom share one toolbar, and playback/output selection share the bottom strip. Export settings and extra range/import options expand when needed, keeping more tracks visible on smaller displays.
|
|
46
|
+
|
|
34
47
|
The editor supports 16 tracks, multiple audio regions, selected/all-track edits, mute/solo, levels, split, cut/copy/paste, undo/redo, saved `.dbedit` sessions and PCM/float mix export. Original audio stays unchanged.
|
|
35
48
|
|
|
36
49
|
Select audio, press **Ctrl+C**, press **Backspace** to delete that section, place the playhead and press **Ctrl+V**. Deleting preserves the timeline gap and the internal audio clipboard. Delete is also available as an editable alternate; Ctrl+X still cuts. Paste starts exactly at the playhead. The Cut/Copy/Paste toolbar buttons are removed.
|
|
37
50
|
|
|
38
|
-
**S** splits at the playhead;
|
|
51
|
+
**V** selects the pointer tool; **S** splits at the playhead. Click a region to select it; Shift-click adds/removes clips, and dragging selects a time range. Selected clips use a brighter purple fill and border. Drag the upper ruler for the playhead and the lower-ruler selection handles to adjust a range. Range exposes numeric selection controls.
|
|
52
|
+
|
|
53
|
+
Drag a clip's header to move it; selected groups retain their time/track offsets. Drag the left/right edges to trim within the source audio. The waveform body remains available for range selection. **Y** switches to slip editing, which shifts source contents inside a trimmed clip; **F** switches to the fade tool. Selected clips also show fade handles.
|
|
54
|
+
|
|
55
|
+
**Ctrl+D** duplicates selected clips or time; **Ctrl+L** repeats the selection with a chosen number of additional copies. **M** mutes/unmutes selected clips or isolates and mutes a time range. **X** crossfades two staggered, overlapping clips on the same track. Right-click audio for these tools, clipboard actions and Clear fades. The scissors/split toolbar buttons are removed.
|
|
56
|
+
|
|
57
|
+
Moves, trims, slip, clip mute and fades are non-destructive and undoable. Fades affect playback and exported mixes; splitting/copying inside a fade preserves the original envelope. Escape cancels a clip drag. The editor's select/slip/fade modes and all default commands have editable keybindings.
|
|
58
|
+
|
|
59
|
+
### Editor zoom
|
|
60
|
+
|
|
61
|
+
The visible **- / + buttons and slider** zoom the timeline from a full-project overview to individual samples. **Ctrl+mouse wheel** zooms around the time under the pointer; **Shift+wheel** scrolls sideways. **Fit** shows the full project, and **Selection** fills the pane with the selected time range.
|
|
62
|
+
|
|
63
|
+
**Ctrl+Plus / Ctrl+Minus** zoom (including Ctrl+= and numeric-keypad keys), **Ctrl+0** fits the project and **Ctrl+Shift+0** fits the selection. These shortcuts are editable in Settings > Keybinds. At close zoom, waveform detail is read from decoded audio in the background, preserving positive/negative peaks and float headroom. The ruler displays fractional seconds when a close-up contains no labeled beat. Zoom changes the view without changing audio, the playhead or undo history.
|
|
39
64
|
|
|
40
65
|
Record options contains microphone/input channels, sample rate and PCM/float format. Recording saves original takes and inserts them at the starting playhead on the selected track; separate input channels use consecutive tracks. Use hardware direct monitoring. Software overdub latency alignment is not included.
|
|
41
66
|
|
|
@@ -58,7 +83,9 @@ Tempo changes the grid, without stretching existing audio. Tempo, meter, divisio
|
|
|
58
83
|
|
|
59
84
|
## Audio devices
|
|
60
85
|
|
|
61
|
-
Settings > Audio selects speakers/headphones and recording input. Playback output is shared between Editor and Media Player. Windows
|
|
86
|
+
Settings > Audio selects speakers/headphones and recording input. Playback output is shared between Editor and Media Player. Windows default displays the current Windows output and resolves it again when playback starts. Named entries select active Windows endpoints; installed ASIO drivers are listed separately. Device changes refresh the selector automatically. Retired compatibility selections migrate to the corresponding real endpoint.
|
|
87
|
+
|
|
88
|
+
Windows playback uses one shared WASAPI renderer with device activation, initialization, buffering and release on its own audio thread. Audio is resampled/adapted to the endpoint's exact mix format, without an extra Windows automatic converter or WinMM playback fallback. Initialization and runtime errors retain the actual failing stage in diagnostics.
|
|
62
89
|
|
|
63
90
|
Ordinary Windows/Realtek/USB microphones capture 16-bit PCM. Saving that input in a float or 24-bit container adds no input precision or native float headroom. H5studio ASIO preserves native 32-bit float when the Zoom driver supplies it. Install Drivers links to official H5studio/R16 support. R16 ASIO recording remains available.
|
|
64
91
|
|
|
@@ -68,6 +95,8 @@ Test speakers plays a short quiet tone. Save diagnostics exports device formats
|
|
|
68
95
|
|
|
69
96
|
Media Player decodes PCM/float WAV, AIFF, FLAC, MP3 and audio from video without Windows Media Player codecs. Gain / Save provides +/-36 dB adjustment, safe level, protection and compact Save copy for PCM or float WAV. Gain affects playback and saved copies; listening volume affects playback only. Copies receive new names instead of overwriting originals or exports.
|
|
70
97
|
|
|
98
|
+
The bundled FFmpeg build contains 227 audio decoders, including integer PCM and IEEE float. Both installation paths include the decoder executable. No Windows Media Player codec pack is required. Protected, damaged or unsupported files can still be rejected; supported decoders are listed in the release's audio-decoder inventory.
|
|
99
|
+
|
|
71
100
|
Open **View > Converter**, drop audio/video, choose the target type and convert. WAV PCM, WAV float, AIFF, FLAC, MP3 and AAC/M4A are supported, including MP3 -> AIFF and AIFF -> WAV. Preserve source rate or choose 44.1/48/96 kHz; MP3 uses at most 48 kHz. Converter contains no gain amplifier or navigation tab.
|
|
72
101
|
|
|
73
102
|
The Zoom R16 conversion preset writes classic 44.1 kHz 16/24-bit PCM WAV with short compatible filenames. R16 Control also retains the hardware panel, mixer/EQ, MIDI control, MIDI recording/playback and native project-copy tools. Verify edited R16 projects on the hardware. Zoom's internal effect DSP is not emulated.
|
|
@@ -79,7 +108,10 @@ The Zoom R16 conversion preset writes classic 44.1 kHz 16/24-bit PCM WAV with sh
|
|
|
79
108
|
| Play/pause; stop active recording | Space |
|
|
80
109
|
| Stop | Shift+Space |
|
|
81
110
|
| Record | R |
|
|
82
|
-
| Split /
|
|
111
|
+
| Split / selection cursor | S / V |
|
|
112
|
+
| Slip / fade tool | Y / F |
|
|
113
|
+
| Duplicate / repeat | Ctrl+D / Ctrl+L |
|
|
114
|
+
| Clip mute / crossfade | M / X |
|
|
83
115
|
| Copy / paste / cut | Ctrl+C / Ctrl+V / Ctrl+X |
|
|
84
116
|
| Delete selected section | Backspace or Delete |
|
|
85
117
|
| Undo / redo | Ctrl+Z / Ctrl+Y |
|
|
@@ -87,6 +119,8 @@ The Zoom R16 conversion preset writes classic 44.1 kHz 16/24-bit PCM WAV with sh
|
|
|
87
119
|
| Open / new session | Ctrl+O / Ctrl+N |
|
|
88
120
|
| Import / export | Ctrl+I / Ctrl+E |
|
|
89
121
|
| Start / end | Home / End |
|
|
122
|
+
| Zoom in / out | Ctrl+= / Ctrl+- |
|
|
123
|
+
| Fit project / selection | Ctrl+0 / Ctrl+Shift+0 |
|
|
90
124
|
| Home / Editor / Converter / Player | Ctrl+1 / Ctrl+2 / Ctrl+3 / Ctrl+4 |
|
|
91
125
|
| Settings | Ctrl+, |
|
|
92
126
|
|
|
@@ -96,8 +130,8 @@ Keybind Edit immediately focuses/selects the capture field. Save commits changes
|
|
|
96
130
|
|
|
97
131
|
The editor's working timeline is 44.1 kHz float. Mix exports support 16/24-bit PCM or 32-bit float WAV, with a 4 GB limit. Source-rate conversion reads the original file. Keep original sources alongside saved sessions.
|
|
98
132
|
|
|
99
|
-
The
|
|
133
|
+
The new Flux PNG is used in the app, executable and versioned shortcut icons. A refined SVG alternative and the original black-haired Drum Buddy mascot are preserved in source; the mascot is archived in `source/brand-archive`. Brand website: https://fluxtempo.ca
|
|
100
134
|
|
|
101
135
|
Physical H5studio/R16 capture and audible playback on the affected Realtek PC require connected-device acceptance. Automated tests cover generated samples, native project fixtures, grid math, editing workflows, settings and local silent output probes.
|
|
102
136
|
|
|
103
|
-
Source, build instructions and dependency licence notices are included. Premium authentication/billing is not implemented.
|
|
137
|
+
Source, build instructions and dependency licence notices are included. Premium authentication/billing is not implemented.
|
package/dependencies.json
CHANGED
|
@@ -10,5 +10,6 @@
|
|
|
10
10
|
"sha256": "315a71e8942a445c903b2d1aedb703b64aab30f054e46199a2bac447d3d03cb2",
|
|
11
11
|
"alternateSha256": "5564fa622f101dc27ea402c399c5b9b1e1e039b8771daf40b185dab9772adf38"
|
|
12
12
|
},
|
|
13
|
-
"installerSha256": "
|
|
13
|
+
"installerSha256": "3d36d86c4bc3e83276811a59778c2fc0e3fe7368dcb6b0ecbc7658ea99220929",
|
|
14
|
+
"bundledAssets": true
|
|
14
15
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pro-gram/flux",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Flux: Windows audio
|
|
3
|
+
"version": "1.6.1",
|
|
4
|
+
"description": "Flux: a 16-track Windows audio studio with native shared playback, float/PCM recording, clip editing tools, tempo grids and bundled audio codecs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "pro-gram",
|
|
7
7
|
"os": [
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"flux": "bin/flux.js"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"postinstall": "node scripts/install.js",
|
|
20
|
+
"postinstall": "echo If npm skips setup, run flux --setup && node scripts/install.js",
|
|
21
21
|
"test": "node --test test/*.test.js"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
Binary file
|
package/scripts/install.js
CHANGED
|
@@ -44,8 +44,10 @@ async function install() {
|
|
|
44
44
|
await fsp.mkdir(cache, { recursive: true });
|
|
45
45
|
const installer = path.resolve(__dirname, '..', 'payload', 'DrumBuddy-Setup.exe');
|
|
46
46
|
if (await hash(installer) !== dependencies.installerSha256) throw new Error('The Flux installer failed its integrity check.');
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
if (!dependencies.bundledAssets) {
|
|
48
|
+
await cached(dependencies.ffmpeg, cache);
|
|
49
|
+
await cached(dependencies.panel, cache);
|
|
50
|
+
}
|
|
49
51
|
console.log('Flux: installing version ' + pkg.version + ' and the Desktop shortcut...');
|
|
50
52
|
execFileSync(installer, ['--update-app'], { windowsHide: true, stdio: 'inherit' });
|
|
51
53
|
await fsp.writeFile(path.join(root, 'npm-version.txt'), pkg.version + '\n');
|
package/source/AssetBootstrap.cs
CHANGED
|
@@ -21,7 +21,7 @@ namespace DrumBuddySetup
|
|
|
21
21
|
if(File.Exists(target) && expected.Contains(Hash(target),StringComparer.OrdinalIgnoreCase))return target;
|
|
22
22
|
string temporary=target+"."+Guid.NewGuid().ToString("N")+".partial";
|
|
23
23
|
try {
|
|
24
|
-
using(var client=new WebClient()) { client.Headers[HttpRequestHeader.UserAgent]="DrumBuddy/1.
|
|
24
|
+
using(var client=new WebClient()) { client.Headers[HttpRequestHeader.UserAgent]="DrumBuddy/1.6.1";client.DownloadFile(url,temporary); }
|
|
25
25
|
if(!expected.Contains(Hash(temporary),StringComparer.OrdinalIgnoreCase))throw new IOException("A downloaded component failed its integrity check. Please retry setup or obtain a newer Drum Buddy release.");
|
|
26
26
|
File.Copy(temporary,target,true);return target;
|
|
27
27
|
}finally { if(File.Exists(temporary))File.Delete(temporary); }
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Flux 1.6.1 bundled FFmpeg audio decoders
|
|
2
|
+
These decode imported files without Windows Media Player codecs.
|
|
3
|
+
|
|
4
|
+
A..... = Audio
|
|
5
|
+
A....D 8svx_exp 8SVX exponential
|
|
6
|
+
A....D 8svx_fib 8SVX fibonacci
|
|
7
|
+
A....D aac AAC (Advanced Audio Coding)
|
|
8
|
+
A....D aac_fixed AAC (Advanced Audio Coding) (codec aac)
|
|
9
|
+
A....D aac_latm AAC LATM (Advanced Audio Coding LATM syntax)
|
|
10
|
+
A....D ac3 ATSC A/52A (AC-3)
|
|
11
|
+
A....D ac3_fixed ATSC A/52A (AC-3) (codec ac3)
|
|
12
|
+
A....D acelp.kelvin Sipro ACELP.KELVIN
|
|
13
|
+
A....D adpcm_4xm ADPCM 4X Movie
|
|
14
|
+
A....D adpcm_adx SEGA CRI ADX ADPCM
|
|
15
|
+
A....D adpcm_afc ADPCM Nintendo Gamecube AFC
|
|
16
|
+
A....D adpcm_agm ADPCM AmuseGraphics Movie
|
|
17
|
+
A....D adpcm_aica ADPCM Yamaha AICA
|
|
18
|
+
A....D adpcm_argo ADPCM Argonaut Games
|
|
19
|
+
A....D adpcm_circus ADPCM Circus
|
|
20
|
+
A....D adpcm_ct ADPCM Creative Technology
|
|
21
|
+
A....D adpcm_dtk ADPCM Nintendo Gamecube DTK
|
|
22
|
+
A....D adpcm_ea ADPCM Electronic Arts
|
|
23
|
+
A....D adpcm_ea_maxis_xa ADPCM Electronic Arts Maxis CDROM XA
|
|
24
|
+
A....D adpcm_ea_r1 ADPCM Electronic Arts R1
|
|
25
|
+
A....D adpcm_ea_r2 ADPCM Electronic Arts R2
|
|
26
|
+
A....D adpcm_ea_r3 ADPCM Electronic Arts R3
|
|
27
|
+
A....D adpcm_ea_xas ADPCM Electronic Arts XAS
|
|
28
|
+
A....D g722 G.722 ADPCM (codec adpcm_g722)
|
|
29
|
+
A....D g726 G.726 ADPCM (codec adpcm_g726)
|
|
30
|
+
A....D g726le G.726 ADPCM little-endian (codec adpcm_g726le)
|
|
31
|
+
A....D adpcm_ima_acorn ADPCM IMA Acorn Replay
|
|
32
|
+
A....D adpcm_ima_alp ADPCM IMA High Voltage Software ALP
|
|
33
|
+
A....D adpcm_ima_amv ADPCM IMA AMV
|
|
34
|
+
A....D adpcm_ima_apc ADPCM IMA CRYO APC
|
|
35
|
+
A....D adpcm_ima_apm ADPCM IMA Ubisoft APM
|
|
36
|
+
A....D adpcm_ima_cunning ADPCM IMA Cunning Developments
|
|
37
|
+
A....D adpcm_ima_dat4 ADPCM IMA Eurocom DAT4
|
|
38
|
+
A....D adpcm_ima_dk3 ADPCM IMA Duck DK3
|
|
39
|
+
A....D adpcm_ima_dk4 ADPCM IMA Duck DK4
|
|
40
|
+
A....D adpcm_ima_ea_eacs ADPCM IMA Electronic Arts EACS
|
|
41
|
+
A....D adpcm_ima_ea_sead ADPCM IMA Electronic Arts SEAD
|
|
42
|
+
A....D adpcm_ima_escape ADPCM IMA Acorn Escape
|
|
43
|
+
A....D adpcm_ima_hvqm2 ADPCM IMA HVQM2
|
|
44
|
+
A....D adpcm_ima_hvqm4 ADPCM IMA HVQM4
|
|
45
|
+
A....D adpcm_ima_iss ADPCM IMA Funcom ISS
|
|
46
|
+
A....D adpcm_ima_magix ADPCM IMA Magix
|
|
47
|
+
A....D adpcm_ima_moflex ADPCM IMA MobiClip MOFLEX
|
|
48
|
+
A....D adpcm_ima_mtf ADPCM IMA Capcom's MT Framework
|
|
49
|
+
A....D adpcm_ima_oki ADPCM IMA Dialogic OKI
|
|
50
|
+
A....D adpcm_ima_pda ADPCM IMA PlayDate
|
|
51
|
+
A....D adpcm_ima_qt ADPCM IMA QuickTime
|
|
52
|
+
A....D adpcm_ima_rad ADPCM IMA Radical
|
|
53
|
+
A....D adpcm_ima_smjpeg ADPCM IMA Loki SDL MJPEG
|
|
54
|
+
A....D adpcm_ima_ssi ADPCM IMA Simon & Schuster Interactive
|
|
55
|
+
A....D adpcm_ima_wav ADPCM IMA WAV
|
|
56
|
+
A....D adpcm_ima_ws ADPCM IMA Westwood
|
|
57
|
+
A....D adpcm_ima_xbox ADPCM IMA Xbox
|
|
58
|
+
A....D adpcm_ms ADPCM Microsoft
|
|
59
|
+
A....D adpcm_mtaf ADPCM MTAF
|
|
60
|
+
A....D adpcm_n64 ADPCM Silicon Graphics N64
|
|
61
|
+
A....D adpcm_psx ADPCM Playstation
|
|
62
|
+
A....D adpcm_psxc ADPCM Playstation C
|
|
63
|
+
A....D adpcm_sanyo ADPCM Sanyo
|
|
64
|
+
A....D adpcm_sbpro_2 ADPCM Sound Blaster Pro 2-bit
|
|
65
|
+
A....D adpcm_sbpro_3 ADPCM Sound Blaster Pro 2.6-bit
|
|
66
|
+
A....D adpcm_sbpro_4 ADPCM Sound Blaster Pro 4-bit
|
|
67
|
+
A....D adpcm_swf ADPCM Shockwave Flash
|
|
68
|
+
A....D adpcm_thp ADPCM Nintendo THP
|
|
69
|
+
A....D adpcm_thp_le ADPCM Nintendo THP (little-endian)
|
|
70
|
+
A....D adpcm_vima LucasArts VIMA audio
|
|
71
|
+
A....D adpcm_xa ADPCM CDROM XA
|
|
72
|
+
A....D adpcm_xmd ADPCM Konami XMD
|
|
73
|
+
A....D adpcm_yamaha ADPCM Yamaha
|
|
74
|
+
A....D adpcm_zork ADPCM Zork
|
|
75
|
+
A....D ahx CRI AHX
|
|
76
|
+
AF...D alac ALAC (Apple Lossless Audio Codec)
|
|
77
|
+
A....D amrnb AMR-NB (Adaptive Multi-Rate NarrowBand) (codec amr_nb)
|
|
78
|
+
A....D libopencore_amrnb OpenCORE AMR-NB (Adaptive Multi-Rate Narrow-Band) (codec amr_nb)
|
|
79
|
+
A....D amrwb AMR-WB (Adaptive Multi-Rate WideBand) (codec amr_wb)
|
|
80
|
+
A....D libopencore_amrwb OpenCORE AMR-WB (Adaptive Multi-Rate Wide-Band) (codec amr_wb)
|
|
81
|
+
A....D anull null audio
|
|
82
|
+
A....D apac Marian's A-pac audio
|
|
83
|
+
A....D ape Monkey's Audio
|
|
84
|
+
A....D aptx aptX (Audio Processing Technology for Bluetooth)
|
|
85
|
+
A....D aptx_hd aptX HD (Audio Processing Technology for Bluetooth)
|
|
86
|
+
A....D atrac1 ATRAC1 (Adaptive TRansform Acoustic Coding)
|
|
87
|
+
A....D atrac3 ATRAC3 (Adaptive TRansform Acoustic Coding 3)
|
|
88
|
+
A....D atrac3al ATRAC3 AL (Adaptive TRansform Acoustic Coding 3 Advanced Lossless)
|
|
89
|
+
A....D atrac3plus ATRAC3+ (Adaptive TRansform Acoustic Coding 3+) (codec atrac3p)
|
|
90
|
+
A....D atrac3plusal ATRAC3+ AL (Adaptive TRansform Acoustic Coding 3+ Advanced Lossless) (codec atrac3pal)
|
|
91
|
+
A....D atrac9 ATRAC9 (Adaptive TRansform Acoustic Coding 9)
|
|
92
|
+
A....D on2avc On2 Audio for Video Codec (codec avc)
|
|
93
|
+
A....D binkaudio_dct Bink Audio (DCT)
|
|
94
|
+
A....D binkaudio_rdft Bink Audio (RDFT)
|
|
95
|
+
A....D bmv_audio Discworld II BMV audio
|
|
96
|
+
A....D bonk Bonk audio
|
|
97
|
+
A....D cbd2_dpcm DPCM Cuberoot-Delta-Exact
|
|
98
|
+
A....D comfortnoise RFC 3389 comfort noise generator
|
|
99
|
+
A....D cook Cook / Cooker / Gecko (RealAudio G2)
|
|
100
|
+
A....D derf_dpcm DPCM Xilam DERF
|
|
101
|
+
A....D dfpwm DFPWM1a audio
|
|
102
|
+
A....D dolby_e Dolby E
|
|
103
|
+
A.S..D dsd_lsbf DSD (Direct Stream Digital), least significant bit first
|
|
104
|
+
A.S..D dsd_lsbf_planar DSD (Direct Stream Digital), least significant bit first, planar
|
|
105
|
+
A.S..D dsd_msbf DSD (Direct Stream Digital), most significant bit first
|
|
106
|
+
A.S..D dsd_msbf_planar DSD (Direct Stream Digital), most significant bit first, planar
|
|
107
|
+
A....D dsicinaudio Delphine Software International CIN audio
|
|
108
|
+
A....D dss_sp Digital Speech Standard - Standard Play mode (DSS SP)
|
|
109
|
+
A....D dst DST (Digital Stream Transfer)
|
|
110
|
+
A....D dca DCA (DTS Coherent Acoustics) (codec dts)
|
|
111
|
+
A....D dvaudio Ulead DV Audio
|
|
112
|
+
A....D eac3 ATSC A/52B (AC-3, E-AC-3)
|
|
113
|
+
A....D evrc EVRC (Enhanced Variable Rate Codec)
|
|
114
|
+
A....D fastaudio MobiClip FastAudio
|
|
115
|
+
AF...D flac FLAC (Free Lossless Audio Codec)
|
|
116
|
+
A....D ftr FTR Voice
|
|
117
|
+
A....D g723_1 G.723.1
|
|
118
|
+
A....D g728 G.728)
|
|
119
|
+
A....D g729 G.729
|
|
120
|
+
A....D gremlin_dpcm DPCM Gremlin
|
|
121
|
+
A....D gsm GSM
|
|
122
|
+
A....D libgsm libgsm GSM (codec gsm)
|
|
123
|
+
A....D gsm_ms GSM Microsoft variant
|
|
124
|
+
A....D libgsm_ms libgsm GSM Microsoft variant (codec gsm_ms)
|
|
125
|
+
A....D hca CRI HCA
|
|
126
|
+
A....D hcom HCOM Audio
|
|
127
|
+
A....D iac IAC (Indeo Audio Coder)
|
|
128
|
+
A....D ilbc iLBC (Internet Low Bitrate Codec)
|
|
129
|
+
A....D imc IMC (Intel Music Coder)
|
|
130
|
+
A....D interplay_dpcm DPCM Interplay
|
|
131
|
+
A....D interplayacm Interplay ACM
|
|
132
|
+
A....D mace3 MACE (Macintosh Audio Compression/Expansion) 3:1
|
|
133
|
+
A....D mace6 MACE (Macintosh Audio Compression/Expansion) 6:1
|
|
134
|
+
A....D metasound Voxware MetaSound
|
|
135
|
+
A....D misc4 Micronas SC-4 Audio
|
|
136
|
+
A....D mlp MLP (Meridian Lossless Packing)
|
|
137
|
+
A....D mp1 MP1 (MPEG audio layer 1)
|
|
138
|
+
A....D mp1float MP1 (MPEG audio layer 1) (codec mp1)
|
|
139
|
+
A....D mp2 MP2 (MPEG audio layer 2)
|
|
140
|
+
A....D mp2float MP2 (MPEG audio layer 2) (codec mp2)
|
|
141
|
+
A....D mp3float MP3 (MPEG audio layer 3) (codec mp3)
|
|
142
|
+
A....D mp3 MP3 (MPEG audio layer 3)
|
|
143
|
+
A....D mp3adufloat ADU (Application Data Unit) MP3 (MPEG audio layer 3) (codec mp3adu)
|
|
144
|
+
A....D mp3adu ADU (Application Data Unit) MP3 (MPEG audio layer 3)
|
|
145
|
+
A....D mp3on4float MP3onMP4 (codec mp3on4)
|
|
146
|
+
A....D mp3on4 MP3onMP4
|
|
147
|
+
A....D als MPEG-4 Audio Lossless Coding (ALS) (codec mp4als)
|
|
148
|
+
A....D msnsiren MSN Siren
|
|
149
|
+
A....D mpc7 Musepack SV7 (codec musepack7)
|
|
150
|
+
A....D mpc8 Musepack SV8 (codec musepack8)
|
|
151
|
+
A....D nellymoser Nellymoser Asao
|
|
152
|
+
A....D opus Opus
|
|
153
|
+
A....D libopus libopus Opus (codec opus)
|
|
154
|
+
A....D osq OSQ (Original Sound Quality)
|
|
155
|
+
A....D paf_audio Amazing Studio Packed Animation File Audio
|
|
156
|
+
A....D pcm_alaw PCM A-law / G.711 A-law
|
|
157
|
+
A....D pcm_bluray PCM signed 16|20|24-bit big-endian for Blu-ray media
|
|
158
|
+
A....D pcm_dvd PCM signed 16|20|24-bit big-endian for DVD media
|
|
159
|
+
A....D pcm_f16le PCM 16.8 floating point little-endian
|
|
160
|
+
A....D pcm_f24le PCM 24.0 floating point little-endian
|
|
161
|
+
A....D pcm_f32be PCM 32-bit floating point big-endian
|
|
162
|
+
A....D pcm_f32le PCM 32-bit floating point little-endian
|
|
163
|
+
A....D pcm_f64be PCM 64-bit floating point big-endian
|
|
164
|
+
A....D pcm_f64le PCM 64-bit floating point little-endian
|
|
165
|
+
A....D pcm_lxf PCM signed 20-bit little-endian planar
|
|
166
|
+
A....D pcm_mulaw PCM mu-law / G.711 mu-law
|
|
167
|
+
A....D pcm_s16be PCM signed 16-bit big-endian
|
|
168
|
+
A....D pcm_s16be_planar PCM signed 16-bit big-endian planar
|
|
169
|
+
A....D pcm_s16le PCM signed 16-bit little-endian
|
|
170
|
+
A....D pcm_s16le_planar PCM signed 16-bit little-endian planar
|
|
171
|
+
A....D pcm_s24be PCM signed 24-bit big-endian
|
|
172
|
+
A....D pcm_s24daud PCM D-Cinema audio signed 24-bit
|
|
173
|
+
A....D pcm_s24le PCM signed 24-bit little-endian
|
|
174
|
+
A....D pcm_s24le_planar PCM signed 24-bit little-endian planar
|
|
175
|
+
A....D pcm_s32be PCM signed 32-bit big-endian
|
|
176
|
+
A....D pcm_s32le PCM signed 32-bit little-endian
|
|
177
|
+
A....D pcm_s32le_planar PCM signed 32-bit little-endian planar
|
|
178
|
+
A....D pcm_s64be PCM signed 64-bit big-endian
|
|
179
|
+
A....D pcm_s64le PCM signed 64-bit little-endian
|
|
180
|
+
A....D pcm_s8 PCM signed 8-bit
|
|
181
|
+
A....D pcm_s8_planar PCM signed 8-bit planar
|
|
182
|
+
A....D pcm_sga PCM SGA
|
|
183
|
+
A....D pcm_u16be PCM unsigned 16-bit big-endian
|
|
184
|
+
A....D pcm_u16le PCM unsigned 16-bit little-endian
|
|
185
|
+
A....D pcm_u24be PCM unsigned 24-bit big-endian
|
|
186
|
+
A....D pcm_u24le PCM unsigned 24-bit little-endian
|
|
187
|
+
A....D pcm_u32be PCM unsigned 32-bit big-endian
|
|
188
|
+
A....D pcm_u32le PCM unsigned 32-bit little-endian
|
|
189
|
+
A....D pcm_u8 PCM unsigned 8-bit
|
|
190
|
+
A....D pcm_vidc PCM Archimedes VIDC
|
|
191
|
+
A....D qcelp QCELP / PureVoice
|
|
192
|
+
A....D qdm2 QDesign Music Codec 2
|
|
193
|
+
A....D qdmc QDesign Music Codec 1
|
|
194
|
+
A....D qoa QOA (Quite OK Audio)
|
|
195
|
+
A....D real_144 RealAudio 1.0 (14.4K) (codec ra_144)
|
|
196
|
+
A....D real_288 RealAudio 2.0 (28.8K) (codec ra_288)
|
|
197
|
+
A....D ralf RealAudio Lossless
|
|
198
|
+
A....D rka RKA (RK Audio)
|
|
199
|
+
A....D roq_dpcm DPCM id RoQ
|
|
200
|
+
A....D s302m SMPTE 302M
|
|
201
|
+
A....D sbc SBC (low-complexity subband codec)
|
|
202
|
+
A....D sdx2_dpcm DPCM Squareroot-Delta-Exact
|
|
203
|
+
A....D shorten Shorten
|
|
204
|
+
A....D sipr RealAudio SIPR / ACELP.NET
|
|
205
|
+
A....D siren Siren
|
|
206
|
+
A....D smackaud Smacker audio (codec smackaudio)
|
|
207
|
+
A....D sol_dpcm DPCM Sol
|
|
208
|
+
A....D speex Speex
|
|
209
|
+
A....D libspeex libspeex Speex (codec speex)
|
|
210
|
+
AF...D tak TAK (Tom's lossless Audio Kompressor)
|
|
211
|
+
A....D truehd TrueHD
|
|
212
|
+
A....D truespeech DSP Group TrueSpeech
|
|
213
|
+
AF...D tta TTA (True Audio)
|
|
214
|
+
A....D twinvq VQF TwinVQ
|
|
215
|
+
A....D vmdaudio Sierra VMD audio
|
|
216
|
+
A....D vorbis Vorbis
|
|
217
|
+
A..... libvorbis libvorbis (codec vorbis)
|
|
218
|
+
A....D wady_dpcm DPCM Marble WADY
|
|
219
|
+
A....D wavarc Waveform Archiver
|
|
220
|
+
A....D wavesynth Wave synthesis pseudo-codec
|
|
221
|
+
AFS..D wavpack WavPack
|
|
222
|
+
A....D ws_snd1 Westwood Audio (SND1) (codec westwood_snd1)
|
|
223
|
+
A....D wmalossless Windows Media Audio Lossless
|
|
224
|
+
A....D wmapro Windows Media Audio 9 Professional
|
|
225
|
+
A....D wmav1 Windows Media Audio 1
|
|
226
|
+
A....D wmav2 Windows Media Audio 2
|
|
227
|
+
A....D wmavoice Windows Media Audio Voice
|
|
228
|
+
A....D xan_dpcm DPCM Xan
|
|
229
|
+
A....D xma1 Xbox Media Audio 1
|
|
230
|
+
A....D xma2 Xbox Media Audio 2
|
|
@@ -30,7 +30,7 @@ namespace DrumBuddy
|
|
|
30
30
|
Children.Add(WorkspaceUi.Text("Speakers / headphones", 18));
|
|
31
31
|
outputs = new AudioOutputPicker(delegate { StopTest(); if (stopPlayback != null) stopPlayback(); }, delegate(string text) { if (result != null) result.Text = text; });
|
|
32
32
|
outputs.Margin = new Thickness(0, 8, 0, 4); Children.Add(outputs);
|
|
33
|
-
var help = WorkspaceUi.Text("
|
|
33
|
+
var help = WorkspaceUi.Text("Windows default follows the speakers/headphones selected in Windows. Choose a named device to keep playback on that output. Only active Windows endpoints and installed ASIO drivers are listed.", 14);
|
|
34
34
|
help.Margin = new Thickness(0, 0, 0, 10); Children.Add(help);
|
|
35
35
|
Children.Add(WorkspaceUi.Button("Test speakers", Test));
|
|
36
36
|
Children.Add(WorkspaceUi.Text("Recording input", 18));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Threading;
|
|
3
|
+
using NAudio.CoreAudioApi;
|
|
4
|
+
using NAudio.CoreAudioApi.Interfaces;
|
|
5
|
+
|
|
6
|
+
namespace DrumBuddy
|
|
7
|
+
{
|
|
8
|
+
internal sealed class AudioEndpointWatcher : IMMNotificationClient, IDisposable
|
|
9
|
+
{
|
|
10
|
+
private readonly MMDeviceEnumerator enumerator;
|
|
11
|
+
private readonly Action changed;
|
|
12
|
+
private volatile bool disposed;
|
|
13
|
+
public AudioEndpointWatcher(Action changed)
|
|
14
|
+
{ this.changed = changed; enumerator = new MMDeviceEnumerator(); enumerator.RegisterEndpointNotificationCallback(this); }
|
|
15
|
+
private void Notify()
|
|
16
|
+
{ if (!disposed) ThreadPool.QueueUserWorkItem(delegate { if (!disposed) changed(); }); }
|
|
17
|
+
public void OnDeviceStateChanged(string id, DeviceState state) { Notify(); }
|
|
18
|
+
public void OnDeviceAdded(string id) { Notify(); }
|
|
19
|
+
public void OnDeviceRemoved(string id) { Notify(); }
|
|
20
|
+
public void OnDefaultDeviceChanged(DataFlow flow, Role role, string id) { if (flow == DataFlow.Render || flow == DataFlow.All) Notify(); }
|
|
21
|
+
public void OnPropertyValueChanged(string id, PropertyKey key) { Notify(); }
|
|
22
|
+
public void Dispose()
|
|
23
|
+
{ if (disposed) return; disposed = true; try { enumerator.UnregisterEndpointNotificationCallback(this); } finally { enumerator.Dispose(); } }
|
|
24
|
+
}
|
|
25
|
+
}
|
package/source/AudioOutputs.cs
CHANGED
|
@@ -164,31 +164,26 @@ namespace DrumBuddy
|
|
|
164
164
|
public static event Action OutputChanged;
|
|
165
165
|
public static string PreferredKey {
|
|
166
166
|
get { return preferredKey; }
|
|
167
|
-
set { value = value
|
|
167
|
+
set { value = NormalizeKey(value); if (value == preferredKey) return; preferredKey = value; var changed = OutputChanged; if (changed != null) changed(); }
|
|
168
168
|
}
|
|
169
|
-
public static
|
|
169
|
+
public static string NormalizeKey(string key)
|
|
170
|
+
{ return string.IsNullOrEmpty(key) || key == "compat:default" ? "default" : key.StartsWith("compat:", StringComparison.Ordinal) ? "windows:" + key.Substring(7) : key; }
|
|
171
|
+
internal static string TestErrorLog;
|
|
172
|
+
public static string ErrorLog { get { return TestErrorLog ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DrumBuddy", "audio-output-errors.txt"); } }
|
|
170
173
|
public static List<OutputChoice> Choices()
|
|
171
174
|
{
|
|
172
175
|
var choices = new List<OutputChoice> { new OutputChoice { Name = "Windows default speakers / headphones" } };
|
|
173
176
|
try {
|
|
174
177
|
using (var enumerator = new MMDeviceEnumerator()) {
|
|
175
|
-
try { using (var defaultDevice = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.
|
|
178
|
+
try { using (var defaultDevice = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console)) { choices[0].Name = "Windows default — " + defaultDevice.FriendlyName; choices[0].DefaultEndpointId = defaultDevice.ID; } } catch { }
|
|
176
179
|
foreach (var endpoint in enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active)) {
|
|
177
180
|
using (endpoint) choices.Add(new OutputChoice { Name = endpoint.FriendlyName + " (Windows)", EndpointId = endpoint.ID });
|
|
178
181
|
}
|
|
179
182
|
}
|
|
180
183
|
}
|
|
181
184
|
catch { /* The default entry keeps the picker usable if Windows Audio is stopped. */ }
|
|
182
|
-
choices.Add(new OutputChoice { Name = "Windows default - Compatibility (16-bit PCM)", Compatibility = true });
|
|
183
|
-
try {
|
|
184
|
-
for (int i = 0; i < OutputDevices.waveOutGetNumDevs(); i++) {
|
|
185
|
-
string endpoint = WindowsWaveDevices.EndpointId(i);
|
|
186
|
-
string name = choices.Where(c => !c.Compatibility && c.EndpointId == endpoint).Select(c => c.Name.Replace(" (Windows)", "")).FirstOrDefault() ?? OutputDevices.Name(i);
|
|
187
|
-
if (!string.IsNullOrEmpty(endpoint)) choices.Add(new OutputChoice { Name = name + " - Compatibility (16-bit PCM)", Compatibility = true, EndpointId = endpoint, Device = i });
|
|
188
|
-
}
|
|
189
|
-
} catch { }
|
|
190
185
|
try { foreach (string driver in AsioOut.GetDriverNames()) choices.Add(new OutputChoice { Name = driver + " (ASIO)", Driver = driver }); } catch { }
|
|
191
|
-
return choices;
|
|
186
|
+
return choices.GroupBy(c => c.Key, StringComparer.OrdinalIgnoreCase).Select(g => g.First()).ToList();
|
|
192
187
|
}
|
|
193
188
|
public static ISampleProvider Adapt(ISampleProvider source, int rate, int channels)
|
|
194
189
|
{
|
|
@@ -213,56 +208,22 @@ namespace DrumBuddy
|
|
|
213
208
|
}
|
|
214
209
|
catch (Exception ex) { route.Dispose(); throw DeviceError(choice.Name, ex); }
|
|
215
210
|
}
|
|
216
|
-
|
|
217
|
-
Exception lastError = null; string endpointId = choice.EndpointId; var failures = new List<string>();
|
|
218
|
-
// Retry polling on the same selected endpoint if its driver rejects event sync.
|
|
211
|
+
Exception lastError = null; var failures = new List<string>();
|
|
219
212
|
foreach (bool events in new[] { true, false }) {
|
|
220
213
|
var route = new PlaybackRoute();
|
|
221
|
-
string stage = "find endpoint";
|
|
222
214
|
try {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
: enumerator.GetDevice(choice.EndpointId);
|
|
227
|
-
endpointId = route.Endpoint.ID; stage = "create shared audio client";
|
|
228
|
-
var wasapi = new WasapiOut(route.Endpoint, AudioClientShareMode.Shared, events, Math.Max(40, Math.Min(250, LatencyMilliseconds))); route.Player = wasapi;
|
|
229
|
-
WaveFormat mix = wasapi.OutputWaveFormat;
|
|
230
|
-
route.Counter = new CountedPlayback(Adapt(source, mix.SampleRate, mix.Channels));
|
|
231
|
-
var wire = new EndpointWaveProvider(route.Counter, mix); route.PlaybackFormat = wire.WaveFormat;
|
|
232
|
-
stage = "initialize " + mix;
|
|
233
|
-
wasapi.Init(wire);
|
|
234
|
-
route.Description = route.Endpoint.FriendlyName + " · Windows shared audio · " + mix.SampleRate + " Hz";
|
|
215
|
+
var player = new WindowsAudioPlayer(source, choice.EndpointId, events, LatencyMilliseconds);
|
|
216
|
+
route.Player = player; route.Counter = player.Counter; route.PlaybackFormat = player.OutputFormat; route.Description = player.Description;
|
|
217
|
+
if (failures.Count > 0) WriteFailures(choice.Name, failures, "Opened native shared stream");
|
|
235
218
|
return route;
|
|
236
219
|
}
|
|
237
|
-
catch (Exception ex) { lastError = ex; failures.Add("
|
|
238
|
-
}
|
|
239
|
-
try {
|
|
240
|
-
var result = OpenCompatibility(source, new OutputChoice { Name = choice.Name, Compatibility = true, EndpointId = endpointId });
|
|
241
|
-
WriteFailures(choice.Name, failures, "Recovered using " + result.Description); return result;
|
|
220
|
+
catch (Exception ex) { lastError = ex; failures.Add("Windows shared " + (events ? "event" : "polling") + ": " + ex); route.Dispose(); }
|
|
242
221
|
}
|
|
243
|
-
catch (Exception ex) { lastError = ex; failures.Add("Compatibility: " + ex); }
|
|
244
222
|
WriteFailures(choice.Name, failures, "Playback could not start");
|
|
245
223
|
throw DeviceError(choice.Name, lastError);
|
|
246
224
|
}
|
|
247
|
-
internal static
|
|
248
|
-
{
|
|
249
|
-
int device = string.IsNullOrEmpty(choice.EndpointId) ? -1 : WindowsWaveDevices.Find(choice.EndpointId);
|
|
250
|
-
if (device == -2) throw new IOException("The selected output is no longer available. Click Refresh in Audio devices and select it again.");
|
|
251
|
-
Exception last = null;
|
|
252
|
-
foreach (int rate in new[] { 48000, 44100 }) {
|
|
253
|
-
var route = new PlaybackRoute();
|
|
254
|
-
try {
|
|
255
|
-
var player = new WaveOutEvent { DeviceNumber = device, DesiredLatency = Math.Max(100, Math.Min(250, LatencyMilliseconds)), NumberOfBuffers = 3 }; route.Player = player;
|
|
256
|
-
route.Counter = new CountedPlayback(Adapt(source, rate, 2));
|
|
257
|
-
var wire = new EndpointWaveProvider(route.Counter, new WaveFormat(rate, 16, 2)); route.PlaybackFormat = wire.WaveFormat;
|
|
258
|
-
player.Init(wire);
|
|
259
|
-
route.Description = (device == -1 ? "Windows default" : OutputDevices.Name(device)) + " - Compatibility / 16-bit PCM / " + rate + " Hz";
|
|
260
|
-
return route;
|
|
261
|
-
}
|
|
262
|
-
catch (Exception ex) { last = ex; route.Dispose(); }
|
|
263
|
-
}
|
|
264
|
-
throw new IOException("Windows PCM compatibility playback failed: " + last.Message + " (0x" + last.HResult.ToString("X8") + ")", last);
|
|
265
|
-
}
|
|
225
|
+
internal static void LogPlaybackFailure(string name, Exception error)
|
|
226
|
+
{ WriteFailures(name, new[] { error.ToString() }, "Playback stopped"); }
|
|
266
227
|
private static void WriteFailures(string name, IEnumerable<string> failures, string result)
|
|
267
228
|
{
|
|
268
229
|
try {
|
|
@@ -272,8 +233,8 @@ namespace DrumBuddy
|
|
|
272
233
|
}
|
|
273
234
|
private static IOException DeviceError(string name, Exception error)
|
|
274
235
|
{
|
|
275
|
-
string detail = error == null ? "No playback device is available." : error.Message + " (0x" + error.HResult.ToString("X8") + ")";
|
|
276
|
-
return new IOException("Could not open " + name + ". " + detail + " Open Audio devices at the top of the app, click Refresh, and select your speakers/headphones.
|
|
236
|
+
string detail = error == null ? "No playback device is available." : error.Message + (error.Message.IndexOf("0x", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : " (0x" + error.HResult.ToString("X8") + ")");
|
|
237
|
+
return new IOException("Could not open " + name + ". " + detail + " Open Audio devices at the top of the app, click Refresh, and select your speakers/headphones. Save diagnostics there for the exact Windows audio failure.", error);
|
|
277
238
|
}
|
|
278
239
|
}
|
|
279
240
|
|
|
@@ -283,6 +244,7 @@ namespace DrumBuddy
|
|
|
283
244
|
private readonly Action changed;
|
|
284
245
|
private readonly Action<string> status;
|
|
285
246
|
private bool updating;
|
|
247
|
+
private AudioEndpointWatcher watcher;
|
|
286
248
|
public OutputChoice Selected { get { return devices.SelectedItem as OutputChoice; } }
|
|
287
249
|
public AudioOutputPicker(Action changed, Action<string> status)
|
|
288
250
|
{
|
|
@@ -296,8 +258,12 @@ namespace DrumBuddy
|
|
|
296
258
|
if (updating || Selected == null) return;
|
|
297
259
|
AudioOutputs.PreferredKey = Selected.Key; if (changed != null) changed();
|
|
298
260
|
};
|
|
299
|
-
Loaded += delegate {
|
|
300
|
-
|
|
261
|
+
Loaded += delegate {
|
|
262
|
+
AudioOutputs.OutputChanged += SyncSelection; Refresh(null, false);
|
|
263
|
+
try { if (watcher == null) watcher = new AudioEndpointWatcher(delegate { Dispatcher.BeginInvoke((Action)delegate { if (IsLoaded) Refresh(null, false); }); }); }
|
|
264
|
+
catch (Exception ex) { if (status != null) status("Could not monitor audio device changes: " + ex.Message); }
|
|
265
|
+
};
|
|
266
|
+
Unloaded += delegate { AudioOutputs.OutputChanged -= SyncSelection; if (watcher != null) { try { watcher.Dispose(); } catch { } watcher = null; } };
|
|
301
267
|
Refresh();
|
|
302
268
|
}
|
|
303
269
|
private void SyncSelection()
|
|
@@ -311,10 +277,11 @@ namespace DrumBuddy
|
|
|
311
277
|
updating = true; devices.SelectedItem = selected; updating = false;
|
|
312
278
|
if (changed != null) changed();
|
|
313
279
|
}
|
|
314
|
-
private void Refresh(string desiredKey = null)
|
|
280
|
+
private void Refresh(string desiredKey = null, bool stop = true)
|
|
315
281
|
{
|
|
316
|
-
string key = desiredKey ??
|
|
317
|
-
|
|
282
|
+
string key = AudioOutputs.NormalizeKey(desiredKey ?? AudioOutputs.PreferredKey);
|
|
283
|
+
var previous = Selected;
|
|
284
|
+
if (stop && changed != null && devices.Items.Count > 0) changed();
|
|
318
285
|
updating = true;
|
|
319
286
|
try {
|
|
320
287
|
var choices = AudioOutputs.Choices(); devices.ItemsSource = choices;
|
|
@@ -322,6 +289,19 @@ namespace DrumBuddy
|
|
|
322
289
|
}
|
|
323
290
|
catch (Exception ex) { if (status != null) status("Could not list audio outputs: " + ex.Message); }
|
|
324
291
|
finally { updating = false; }
|
|
292
|
+
if (Selected != null) {
|
|
293
|
+
AudioOutputs.PreferredKey = Selected.Key;
|
|
294
|
+
if (!stop && previous != null && (previous.Key != Selected.Key || previous.DefaultEndpointId != Selected.DefaultEndpointId) && changed != null) changed();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
internal string SelectionForTest { get { return Selected == null ? null : Selected.Key; } }
|
|
298
|
+
internal void RefreshForTest() { Refresh(); }
|
|
299
|
+
internal void UseCompactLayout()
|
|
300
|
+
{
|
|
301
|
+
Width = 338; Margin = new Thickness(8, 0, 8, 4);
|
|
302
|
+
foreach (var text in Children.OfType<TextBlock>()) text.Visibility = Visibility.Collapsed;
|
|
303
|
+
devices.MaxWidth = 306; devices.MinHeight = 26; devices.FontSize = 12; devices.Padding = new Thickness(4, 2, 4, 2);
|
|
304
|
+
foreach (var button in Children.OfType<Button>()) { button.Content = "↻"; button.ToolTip = "Refresh audio devices"; button.Width = 26; button.MinHeight = 26; button.Padding = new Thickness(0); button.Margin = new Thickness(4, 0, 0, 0); }
|
|
325
305
|
}
|
|
326
306
|
}
|
|
327
307
|
}
|