Last active
January 14, 2025 16:26
-
-
Save danrossi/5fe77e8b36768a6371572b5735a447b2 to your computer and use it in GitHub Desktop.
Flac2Wav - Converter and preparation script for Rekordbox and Pioneer CDJ players that don't support Flac.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
# pip install sox | |
import json | |
from multiprocessing import Pool | |
import logging | |
import os | |
import sys | |
import subprocess | |
import shlex | |
import re | |
from subprocess import call | |
import sox | |
try: | |
from os import scandir, walk | |
except ImportError: | |
from scandir import scandir, walk | |
class Flac2Wav: | |
logger = '' | |
def __init__(self): | |
global logger | |
# create logger | |
logger = logging.getLogger(__name__) | |
logger.setLevel(logging.DEBUG) | |
# create a file handler | |
# handler = logging.FileHandler('converter.log') | |
handler = logging.StreamHandler() | |
handler.setLevel(logging.INFO) | |
# create a logging format | |
formatter = \ | |
logging.Formatter(''' | |
%(asctime)s - %(levelname)s - %(message)s | |
''') | |
handler.setFormatter(formatter) | |
# add the handlers to the logger | |
logger.addHandler(handler) | |
def convert(self, src_dir, out_dir): | |
global logger | |
file_queue = [] | |
ext = ['.flac', '.wav'] | |
# collect files to be converted | |
for entry in os.scandir(src_dir): | |
if entry.name.endswith(tuple(ext)) and entry.is_file(): | |
filename = entry.name | |
if entry.name.endswith('.flac'): | |
filename = filename.replace('.flac', '.wav') | |
file_queue.append({ | |
'dir': os.path.dirname(entry.path), | |
'path': entry.path, | |
'filename': filename, | |
'dest': out_dir, | |
}) | |
logger.info('Start converting: %s files', | |
str(len(file_queue))) | |
with Pool(1) as p: | |
p.map(self.process, file_queue) | |
def cleanWhiteSpace(self, value): | |
return value.replace(' ', '_').replace('/', '_').replace('|', '_').replace('"', '').replace('(', '').replace(')', '') | |
def cleanDirectoryName(self, value): | |
return value.replace('/', '_').replace('|', '_').replace('"', '') | |
def probe_file(self, filename): | |
cmnd = [ | |
'ffprobe', | |
'-loglevel', | |
'quiet', | |
'-show_format', | |
'-show_streams', | |
'-select_streams', | |
'a', | |
'-print_format', | |
'json', | |
filename, | |
] | |
p = subprocess.Popen(cmnd, stdout=subprocess.PIPE, | |
stderr=subprocess.PIPE) | |
(out, err) = p.communicate() | |
if err: | |
print(err) | |
else: | |
return (out) | |
def process(self, queue): | |
while True: | |
global logger | |
filename = queue['filename'] | |
isWav = filename.index('wav') > -1 | |
info = self.probe_file(queue['path']) | |
if info: | |
data = json.loads(info.decode('utf-8')) | |
format = data['format'] | |
stream = data['streams'][0] | |
if 'bits_per_raw_sample' in stream: | |
bits = stream['bits_per_raw_sample'] | |
else: | |
bits = stream['bits_per_sample'] | |
bits_per_sample = int(bits) | |
sample_rate = int(stream['sample_rate']) | |
if 'tags' in format and 'ARTIST' in format['tags']: | |
print(format['tags']) | |
print(filename) | |
tags = format['tags'] | |
artist = tags['ARTIST'] | |
title = tags['TITLE'] | |
album = tags.get('ALBUM', '') | |
directory = os.path.join(queue['dest'], artist, | |
self.cleanDirectoryName(album)) | |
if not os.path.exists(directory): | |
os.makedirs(directory) | |
filename = os.path.join(directory, | |
'{0}-{1}-{2}.wav'.format(self.cleanWhiteSpace(artist), | |
self.cleanWhiteSpace( | |
title), | |
self.cleanWhiteSpace(album))) | |
else: | |
filename = os.path.join(queue['dest'], | |
queue['filename']) | |
tfm = sox.Transformer() | |
if bits_per_sample != 16 or sample_rate > 48000: | |
# check for 24bit or higher sample rates than 48k but don't | |
# convert anything less than 48k | |
new_sample_rate = sample_rate | |
if sample_rate > 48000: | |
new_sample_rate = 48000 | |
logger.info('Converting From bit_depth: {0} sample_rate: {1}'.format(bits_per_sample, | |
sample_rate)) | |
tfm.convert(new_sample_rate, 2, 16) | |
logger.info('Converting {0} to {1} '.format(queue['filename' | |
], filename)) | |
tfm.build(queue['path'], filename) | |
return True | |
converter = Flac2Wav() | |
converter.convert("/mnt/d/RekordBox/queue/", "/mnt/d/RekordBox/ElectroSet/") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Flac2Wav
Converter and preparation script for Rekordbox and Pioneer CDJ players that don't support Flac.
The "standard" CDJ-2000 do not support Flac. And even though it says it supports 24/48 it has trouble reading files randomly.
This script converts to the absolute lowest common denominator 16/44 where required. It has to check for bitdepth and samplerate of the original files before conversion. So the files will hopefully load without issues.
It also renames the wav filenames from the metadata in the format artist-title-album as this has no metadata like Flac. and this is the best way to read the tracks on the display. The files are placed into individual directories of "artist/release"
The entire target directory can be imported after into Rekordbox as a playlist.
The CDJ-2000 NXS play Flac and support 24/96. The XDJ-1000 decks support Flac and 24/48.
Requirements
python3 is required for the fast scandir directory walking.
ffprobe is required for metadata and format checks. This can be installed with
Sox is required for the audio conversions. and PySox is required as the python wrapper.
This installation will work the same for Linux Ubuntu and Windows Linux Sub System.