Create a Drumless Backing Track¶
Want to practise along to a song without the original drums getting in the way?
DrumScript can create a drumless backing track from any audio file. It uses Demucs to separate the song into four stems (drums, bass, vocals, other), then re-mixes everything except the drums into a single file.
The result is a clean play-along track with bass, vocals, and instruments intact — and no drums.
Under the hood, ds.extract_stems(drumless=True) calls Demucs for source separation, then uses numpy-based element-wise summation to mix the non-drum stems back together. WAV output is ffmpeg-free; MP3 output requires ffmpeg.
(Note: We are using a copyright-free synthetic track for this demonstration.)
from IPython.display import Audio, display
import drumscript as ds
# 1. Load audio
audio_file_path = "audio/test_track_1.wav"
audio_file = ds.load_audio(audio_file_path)
print("Audio loaded!")
display(Audio(audio_file_path))
Audio loaded!
# 2. Create a drumless backing track
# This isolates all stems except the drums and mixes them together.
backing_track = ds.extract_stems(audio_path=audio_file_path, output_dir="backing_track/", output_format="wav", drumless=True)
Starting Demucs separation for: test_track_1.wav...
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
Cell In[3], line 3
1 # 2. Create a drumless backing track
2 # This isolates all stems except the drums and mixes them together.
----> 3 backing_track = ds.extract_stems(audio_path=audio_file_path, output_dir="backing_track/", output_format="wav", drumless=True)
File ~/work/DrumScript/DrumScript/drumscript/__init__.py:90, in extract_stems(audio_path, output_dir, output_format, drumless, mute, all_stems, full)
83 output_dir.mkdir(parents=True, exist_ok=True)
85 # result_path = extract_drum_stem(audio_path, output_dir=str(output_dir))
86
87 # results = separate_audio(
88 # audio_path=audio_path, output_format=output_format, drumless=drumless, mute=mute, all_stems=all_stems, output_dir=str(output_dir)
---> 90 results = separate_audio(
91 audio_path=audio_path,
92 output_format=output_format,
93 drumless=drumless,
94 mute=mute,
95 all_stems=all_stems,
96 output_dir=str(output_dir),
97 )
99 # drum_path = results.get("drums") or results.get("drums_stem")
100
101 # if full:
102 # return {"status": "success", "drum_stem_path": result_path, "original_file": audio_path,
103 # "output_directory": str(output_dir)}
105 drum_path = results.get("drums") or results.get("drums_stem")
File ~/work/DrumScript/DrumScript/drumscript/audio_processor/stem_splitter.py:194, in separate_audio(audio_path, output_format, drumless, mute, all_stems, output_dir)
192 command = ["demucs", "-o", str(temp_demucs_dir), "-n", DEMUCS_MODEL, str(input_path)]
193 try:
--> 194 subprocess.run(command, check=True, capture_output=True, text=True)
195 except subprocess.CalledProcessError as e:
196 shutil.rmtree(temp_demucs_dir, ignore_errors=True)
File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/subprocess.py:550, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
548 with Popen(*popenargs, **kwargs) as process:
549 try:
--> 550 stdout, stderr = process.communicate(input, timeout=timeout)
551 except TimeoutExpired as exc:
552 process.kill()
File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/subprocess.py:1209, in Popen.communicate(self, input, timeout)
1206 endtime = None
1208 try:
-> 1209 stdout, stderr = self._communicate(input, endtime, timeout)
1210 except KeyboardInterrupt:
1211 # https://bugs.python.org/issue25942
1212 # See the detailed comment in .wait().
1213 if timeout is not None:
File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/subprocess.py:2115, in Popen._communicate(self, input, endtime, orig_timeout)
2108 self._check_timeout(endtime, orig_timeout,
2109 stdout, stderr,
2110 skip_check_and_raise=True)
2111 raise RuntimeError( # Impossible :)
2112 '_check_timeout(..., skip_check_and_raise=True) '
2113 'failed to raise TimeoutExpired.')
-> 2115 ready = selector.select(timeout)
2116 self._check_timeout(endtime, orig_timeout, stdout, stderr)
2118 # XXX Rewrite these to use non-blocking I/O on the file
2119 # objects; they are no longer using C stdio!
File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/selectors.py:415, in _PollLikeSelector.select(self, timeout)
413 ready = []
414 try:
--> 415 fd_event_list = self._selector.poll(timeout)
416 except InterruptedError:
417 return ready
KeyboardInterrupt:
# 3. Playback!
# Workaround: extract_stems currently returns the drum path
# even with drumless=True. Manually find the backing track.
import os
backing_dir = os.path.join("backing_track", "test_track_1")
backing_files = [f for f in os.listdir(backing_dir) if "no_drums" in f]
if backing_files:
backing_path = os.path.join(backing_dir, backing_files[0])
print("Created backing track:", backing_path)
display(Audio(backing_path))
else:
print("No backing track found — check the output directory.")
Created backing track: backing_track/test_track_1/test_track_1_no_drums.wav