@@ -1416,8 +1416,8 @@ function applyClipFadeSpans(
14161416}
14171417
14181418/**
1419- * Apply speed change to audio with pitch preservation using SoundTouch algorithm .
1420- * Processes all channels together through a single SoundTouch instance so that
1419+ * Apply speed change to audio with pitch preservation using the local time-stretch processor .
1420+ * Processes all channels together through a single processor instance so that
14211421 * WSOLA overlap windows are consistent across channels (prevents phase drift
14221422 * between L/R that causes a hollow sound).
14231423 *
@@ -1432,31 +1432,31 @@ async function applySpeedAndPitch(
14321432 pitchShiftSemitones : number ,
14331433 sampleRate : number ,
14341434) : Promise < Float32Array [ ] > {
1435- const requiresSoundTouch =
1435+ const requiresTimeStretch =
14361436 Math . abs ( speed - 1 ) > 0.0001 || isAudioPitchShiftActive ( pitchShiftSemitones )
1437- if ( ! requiresSoundTouch ) return channels
1437+ if ( ! requiresTimeStretch ) return channels
14381438 if ( channels . length === 0 || channels [ 0 ] ! . length === 0 ) return channels
14391439
14401440 const numChannels = channels . length
14411441 const samplesPerChannel = channels [ 0 ] ! . length
14421442
1443- log . debug ( 'Applying speed/pitch change (SoundTouch) ' , {
1443+ log . debug ( 'Applying speed/pitch change with time-stretch processor ' , {
14441444 speed,
14451445 pitchShiftSemitones,
14461446 sampleRate,
14471447 numChannels,
14481448 } )
14491449
14501450 try {
1451- const soundtouch = await import ( 'soundtouchjs ' )
1452- const st = new soundtouch . SoundTouch ( )
1451+ const timeStretch = await import ( '@/lib/audio/time-stretch ' )
1452+ const st = new timeStretch . TimeStretchProcessor ( )
14531453
14541454 st . tempo = speed
14551455 st . pitch = getAudioPitchRatioFromSemitones ( pitchShiftSemitones )
14561456 st . rate = 1.0
14571457
1458- // SoundTouch processes interleaved stereo. Interleave all channels
1459- // (for mono, duplicate to stereo so SoundTouch gets valid input).
1458+ // The processor consumes interleaved stereo. Interleave all channels
1459+ // (for mono, duplicate to stereo so it gets valid input).
14601460 const stereoInput = new Float32Array ( samplesPerChannel * 2 )
14611461 const left = channels [ 0 ] !
14621462 const right = numChannels >= 2 ? channels [ 1 ] ! : left
@@ -1479,7 +1479,7 @@ async function applySpeedAndPitch(
14791479 } ,
14801480 }
14811481
1482- const filter = new soundtouch . SimpleFilter ( source , st )
1482+ const filter = new timeStretch . TimeStretchFilter ( source , st )
14831483
14841484 const expectedOutputLength = Math . floor ( samplesPerChannel / speed )
14851485 const stereoOutput = new Float32Array ( expectedOutputLength * 2 )
@@ -1522,7 +1522,7 @@ async function applySpeedAndPitch(
15221522 outputChannels . push ( outLeft )
15231523 }
15241524
1525- log . debug ( 'SoundTouch time stretch complete' , {
1525+ log . debug ( 'Time- stretch processing complete' , {
15261526 inputLength : samplesPerChannel ,
15271527 outputLength : actualOutputLength ,
15281528 expectedLength : expectedOutputLength ,
@@ -1532,14 +1532,14 @@ async function applySpeedAndPitch(
15321532
15331533 return outputChannels
15341534 } catch ( error ) {
1535- log . warn ( 'SoundTouch failed, falling back to simple resampling' , {
1535+ log . warn ( 'Time-stretch processing failed, falling back to simple resampling' , {
15361536 error,
15371537 speed,
15381538 pitchShiftSemitones,
15391539 } )
15401540
15411541 if ( isAudioPitchShiftActive ( pitchShiftSemitones ) ) {
1542- log . warn ( 'Independent export pitch shift was skipped because SoundTouch failed to load ' )
1542+ log . warn ( 'Independent export pitch shift was skipped because time-stretch processing failed ' )
15431543 }
15441544
15451545 // Fallback: simple resampling per-channel (speed only, pitch shift omitted)
@@ -1767,7 +1767,7 @@ export async function processAudio(
17671767 // Note: decoded audio is already trimmed to the range we requested.
17681768
17691769 // Apply speed across ALL channels at once to maintain phase coherence
1770- // between L/R (SoundTouch WSOLA finds shared overlap windows).
1770+ // between L/R (the WSOLA pipeline finds shared overlap windows).
17711771 let processedChannels = decoded . samples
17721772 if (
17731773 Math . abs ( segment . speed - 1 ) > 0.0001 ||
0 commit comments