(This article should be very confusing for you to read, sorry, but I can’t explain this tool I made very well)
When I was very new to BMS making I use Audacity to slice the sounds manually. It’s very tiresome!
So I made a tool to slice the wav files into parts. Written in PHP:
The Code: http://share11.appspot.com/38421
To use it, copy the code into text editor, save it as .php, use Bamcompile to compile .PHP file into .EXE.
Make a folder called “sox” in the same folder as the .EXE file and download the windows version of SoX. Put the exe in the “sox” folder.
Then, for example, I have a .wav file called “x.wav”, I need to make a text file called “x.txt” and type in some beats.
File Structure:
120bpm
16th
,..,..,. ,..,..,.
,...,... ,...,.,.
12th
,,,,,, ,,,,,,
Here are the explainations.
- 120bpm -> The BPM of the wav file is 120 (changes the tempo, affects the commands after it)
- 16th -> 1 measure is divided into 16 steps (changes the step size, will affect the commands after it)
- , -> Starts a new wave file and put 1 step into that file.
- . -> Put 1 step into the last file.
- Other texts are treated as comments.
So from the above text file, the application would slice the wav file like this:
- 0.000 sec
- 0.375 sec
- 0.750 sec
- 1.000 sec
- 1.375 sec
- 1.750 sec
- 2.000 sec
- 2.500 sec
- 3.000 sec
- 3.500 sec
- 3.750 sec
- 4.000 sec
- 4.167 sec
- 4.333 sec
- 4.500 sec
- 4.667 sec
- 4.833 sec
- 5.000 sec
- 5.167 sec
- 5.333 sec
- 5.500 sec
- 5.667 sec
- 5.833 sec (and lasts for 0.167 seconds)
The generated files will be saved in the folder called “wav”. You can them import and use in BMSE.
<?php
chdir (dirname($argv[0]));
function debug_msg($text) {
echo “$text\n”;
}
function process_file($txt, &$commands, &$dirs) {
// Init.
debug_msg (‘==================================’);
debug_msg (‘[ii] Text File: ‘ . $txt);
if (strtolower(substr($txt, -4)) !== ‘.txt’) {
debug_msg (‘[!!] Accept only .txt file!’);
return;
}
if (!file_exists($txt)) {
debug_msg (‘[!!] .txt file not found!’);
return;
}
$wav = substr($txt, 0, -3) . ‘wav’;
debug_msg (‘[ii] Wave File: ‘ . $wav);
if (!file_exists($wav)) {
debug_msg (‘[!!] .wav file not found!’);
return;
}
debug_msg (‘[..] Opening Text File: ‘ . $wav);
$d = file_get_contents($txt);
$render_mode = ‘normal’;
if (preg_match(‘~#(soft|double)~’, $d, $match)) {
$render_mode = $match[1];
}
debug_msg (‘[..] Render Mode: ‘ . $render_mode);
// Parse Tokens
if (!preg_match_all(‘~(?:[\d.]+)bpm|(?:[\d.]+)(?:st|nd|rd|th)|\.|,~i’, $d, $tokens)) {
debug_msg (‘[!!] No tokens found.’);
return;
}
$tokens = array_map(‘strtolower’, $tokens[0]);
debug_msg (‘[ii] ‘ . count($tokens) . ‘ tokens found.’);
// Default.
$bpm = 140;
$divide = 4;
$parts = array();
$samples = 0;
$rate = 44100;
// Process Tokens.
$fp = fopen(substr($txt, 0, -3) . ‘bms-clipboard.txt’, ‘w’);
fwrite ($fp, ‘BMSE ClipBoard Object Data Format’ . “\r\n”);
$timecodes = 0;
foreach ($tokens as $v) {
if ($v == ‘,’) {
if ($samples) {
$parts[] = $samples;
}
$samples = compute_samples($bpm, $divide, $rate);
fwrite ($fp, sprintf(’1010%07d1′, (int)$timecodes) . “\r\n”);
$timecodes += 192 / $divide;
continue;
}
if ($v == ‘.’) {
$samples += compute_samples($bpm, $divide, $rate);
$timecodes += 192 / $divide;
continue;
}
if (substr($v, -3) == ‘bpm’) {
$bpm = floatval(substr($v, 0, -3));
continue;
}
$divide = floatval(substr($v, 0, -2));
}
fclose ($fp);
if ($samples) {
$parts[] = $samples;
}
if ($render_mode == ‘double’) {
$parts[] = 0;
}
// Generate Batch Commands
debug_msg (‘[ii] ‘ . count($parts) . ‘ slices.’);
$start = 0;
$source = escapeshellarg($wav);
$index = 0;
$output = dirname($wav) . ‘\\wav’;
$dirs[] = $output;
$last_length = 0;
$extra_global = ”;
$extra_in = ”;
$extra_out = ”;
if ($render_mode == ‘double’) {
$index –;
}
reset ($parts);
while (list($key, $length) = each($parts)) {
$is_first = $key == 0;
$is_last = $key >= count($parts) – 1;
$index ++;
$destination = escapeshellarg(
$output . ‘\\’ . substr(basename($wav), 0, -4) .
‘-’ . sprintf(‘%03d’, $index) . ‘.wav’
);
$fade_in = 10;
$fade_out = 30;
$out_start = $start;
$out_length = $length;
if ($render_mode == ‘double’) {
$out_start -= $last_length;
$out_length += $last_length;
$extra_in = ‘-v 0.64′;
} else if ($render_mode == ‘soft’) {
if (!$is_last) {
$soft_length = min(16000, floor($parts[$key + 1] / 2));
$fade_out = $soft_length;
$out_length += $soft_length;
}
if (!$is_first) {
$soft_length = min(16000, floor($length / 2));
$fade_in = $soft_length;
}
}
$commands[] = sprintf(‘sox\sox %s %s %s %s %s trim %ds %ds fade q %ds %ds %ds’,
$extra_global, $extra_in, $source, $extra_out, $destination,
(int)($out_start), (int)($out_length),
(int)($fade_in), (int)($out_length), (int)($fade_out)
);
$last_length = $length;
$start += $length;
}
}
function compute_samples($bpm, $divide, $rate) {
return ((240 / $bpm) / $divide) * $rate;
}
$args = $argv;
array_shift ($args);
$commands = array();
$dirs = array();
foreach ($args as $v) {
process_file ($v, $commands, $dirs);
}
$count = count($commands);
debug_msg (‘==================================’);
debug_msg (‘[ii] Number of files to be generated: ‘ . $count);
debug_msg (‘[..] Creating directories.’);
$made = 0;
foreach (array_unique($dirs) as $v) {
if (!file_exists($v)) {
debug_msg (‘[..] Making output directory: ‘ . $v);
mkdir ($v);
$made ++;
} else {
debug_msg (‘[ii] Output directory already exists: ‘ . $v);
}
}
debug_msg (‘[ii] Number of new directories created: ‘ . $made . ”);
debug_msg (‘==================================’);
debug_msg (‘[ii] Generating wav files..’);
$index = 0;
$digits = strlen(” . $count);
foreach ($commands as $v) {
$index ++;
debug_msg (‘( ‘ . sprintf(‘%0′ . $digits . ‘d’, $index) . ‘ / ‘ . $count . ‘ ): ‘ . $v);
exec ($v);
}
?>
I’m confused too, but I’ll try to understand..