@pro-gram/flux 1.5.0 → 1.6.0

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 CHANGED
@@ -1,4 +1,4 @@
1
- Flux 1.5.0 - third-party components
1
+ Flux 1.6.0 - 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 npm installer
24
- fetches the image directly from Zoom's public product website.
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
@@ -25,17 +25,31 @@ flux.cmd
25
25
 
26
26
  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
27
 
28
- If npm skipped the install script, the first launch completes setup. `flux.cmd --setup` repairs the installation. The Windows executable is unsigned. Setup downloads pinned FFmpeg/R16 panel assets from their published sources, verifies hashes and caches them. The standalone installer includes those assets. Vendor audio drivers are linked from their official support pages.
28
+ 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
29
 
30
30
  ## Editor and recording
31
31
 
32
32
  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
33
 
34
+ 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.
35
+
34
36
  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
37
 
36
38
  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
39
 
38
- **S** splits at the playhead; **K** toggles the scissors pointer. Drag the upper ruler for the playhead and the lower-ruler selection handles to adjust a range. Range / zoom exposes numeric selection and zoom controls. Zoom extends to 2560 px/s for fine chops.
40
+ **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.
41
+
42
+ 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.
43
+
44
+ **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.
45
+
46
+ 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.
47
+
48
+ ### Editor zoom
49
+
50
+ 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.
51
+
52
+ **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
53
 
40
54
  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
55
 
@@ -58,7 +72,9 @@ Tempo changes the grid, without stretching existing audio. Tempo, meter, divisio
58
72
 
59
73
  ## Audio devices
60
74
 
61
- Settings > Audio selects speakers/headphones and recording input. Playback output is shared between Editor and Media Player. Windows speakers use shared WASAPI with native endpoint format handling and an endpoint-matched 16-bit PCM compatibility fallback. Select the same device's Compatibility output if needed.
75
+ 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.
76
+
77
+ 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
78
 
63
79
  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
80
 
@@ -68,6 +84,8 @@ Test speakers plays a short quiet tone. Save diagnostics exports device formats
68
84
 
69
85
  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
86
 
87
+ 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.
88
+
71
89
  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
90
 
73
91
  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 +97,10 @@ The Zoom R16 conversion preset writes classic 44.1 kHz 16/24-bit PCM WAV with sh
79
97
  | Play/pause; stop active recording | Space |
80
98
  | Stop | Shift+Space |
81
99
  | Record | R |
82
- | Split / scissors pointer | S / K |
100
+ | Split / selection cursor | S / V |
101
+ | Slip / fade tool | Y / F |
102
+ | Duplicate / repeat | Ctrl+D / Ctrl+L |
103
+ | Clip mute / crossfade | M / X |
83
104
  | Copy / paste / cut | Ctrl+C / Ctrl+V / Ctrl+X |
84
105
  | Delete selected section | Backspace or Delete |
85
106
  | Undo / redo | Ctrl+Z / Ctrl+Y |
@@ -87,6 +108,8 @@ The Zoom R16 conversion preset writes classic 44.1 kHz 16/24-bit PCM WAV with sh
87
108
  | Open / new session | Ctrl+O / Ctrl+N |
88
109
  | Import / export | Ctrl+I / Ctrl+E |
89
110
  | Start / end | Home / End |
111
+ | Zoom in / out | Ctrl+= / Ctrl+- |
112
+ | Fit project / selection | Ctrl+0 / Ctrl+Shift+0 |
90
113
  | Home / Editor / Converter / Player | Ctrl+1 / Ctrl+2 / Ctrl+3 / Ctrl+4 |
91
114
  | Settings | Ctrl+, |
92
115
 
@@ -96,8 +119,8 @@ Keybind Edit immediately focuses/selects the capture field. Save commits changes
96
119
 
97
120
  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
121
 
99
- The enlarged PNG is used in the header. A refined SVG alternative and the original black-haired Drum Buddy mascot are preserved in source. Brand website: https://fluxtempo.ca
122
+ 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
123
 
101
124
  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
125
 
103
- Source, build instructions and dependency licence notices are included. Premium authentication/billing is not implemented.
126
+ 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": "cf5047db59588e7cbbd1ceae31f14f5e22689474b658379c4e45098bef38570e"
13
+ "installerSha256": "0a57d2459fdb2c5c0c2917df12ca907b47c5a966006d2680e9d160d16bdfed9c",
14
+ "bundledAssets": true
14
15
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pro-gram/flux",
3
- "version": "1.5.0",
4
- "description": "Flux: Windows audio editor/recorder with tempo grids, independent snapping, 16 tracks, PCM/float playback and file conversion.",
3
+ "version": "1.6.0",
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": [
Binary file
@@ -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
- await cached(dependencies.ffmpeg, cache);
48
- await cached(dependencies.panel, cache);
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');
@@ -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.5.0";client.DownloadFile(url,temporary); }
24
+ using(var client=new WebClient()) { client.Headers[HttpRequestHeader.UserAgent]="DrumBuddy/1.6.0";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.0 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("For a Realtek or Windows audio error, choose that device's Compatibility (16-bit PCM) entry. Automatic Windows output also tries this path if shared audio cannot open.", 14);
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
+ }
@@ -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 ?? "default"; if (value == preferredKey) return; preferredKey = value; var changed = OutputChanged; if (changed != null) changed(); }
167
+ set { value = NormalizeKey(value); if (value == preferredKey) return; preferredKey = value; var changed = OutputChanged; if (changed != null) changed(); }
168
168
  }
169
- public static readonly string ErrorLog = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DrumBuddy", "audio-output-errors.txt");
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.Multimedia)) choices[0].Name = "Windows default — " + defaultDevice.FriendlyName; } catch { }
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
- if (choice.Compatibility) return OpenCompatibility(source, choice);
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
- using (var enumerator = new MMDeviceEnumerator())
224
- route.Endpoint = string.IsNullOrEmpty(choice.EndpointId)
225
- ? enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia)
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("WASAPI " + (events ? "event" : "polling") + " / " + stage + ": " + ex); route.Dispose(); }
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 PlaybackRoute OpenCompatibility(ISampleProvider source, OutputChoice choice)
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. Try the Compatibility (16-bit PCM) entry. Save diagnostics there if it still fails.", error);
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 { AudioOutputs.OutputChanged += SyncSelection; SyncSelection(); };
300
- Unloaded += delegate { AudioOutputs.OutputChanged -= SyncSelection; };
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 ?? (Selected == null ? AudioOutputs.PreferredKey : Selected.Key);
317
- if (changed != null && devices.Items.Count > 0) changed();
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
  }
@@ -51,12 +51,13 @@ namespace DrumBuddy
51
51
  current.PlaybackStopped += delegate(object sender, StoppedEventArgs e) {
52
52
  dispatcher.BeginInvoke((Action)delegate {
53
53
  if (device != current) return;
54
+ if (e.Exception != null) AudioOutputs.LogPlaybackFailure(route.Description, e.Exception);
54
55
  Stop(); if (ended != null) ended(e.Exception);
55
56
  });
56
57
  };
57
58
  current.Play();
58
59
  }
59
- catch { Stop(); throw; }
60
+ catch (Exception ex) { if (route != null) AudioOutputs.LogPlaybackFailure(route.Description, ex); Stop(); throw; }
60
61
  }
61
62
  public void Pause() { if (device != null) device.Pause(); }
62
63
  public void Resume() { if (device != null) device.Play(); }
@@ -17,10 +17,10 @@ namespace DrumBuddy
17
17
  public static ImageSource Raster()
18
18
  {
19
19
  if (raster != null) return raster;
20
- var source = new BitmapImage(); var embedded = Assembly.GetExecutingAssembly().GetManifestResourceStream("logo.png");
20
+ var source = new BitmapImage(); var embedded = Assembly.GetExecutingAssembly().GetManifestResourceStream("Flux-Logo.png");
21
21
  try {
22
22
  source.BeginInit(); source.CacheOption = BitmapCacheOption.OnLoad;
23
- if (embedded != null) source.StreamSource = embedded; else source.UriSource = new Uri(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logo.png"));
23
+ if (embedded != null) source.StreamSource = embedded; else source.UriSource = new Uri(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Flux-Logo.png"));
24
24
  source.EndInit(); source.Freeze();
25
25
  }
26
26
  finally { if (embedded != null) embedded.Dispose(); }