Skip to content

Commit

Permalink
Update channels (#5)
Browse files Browse the repository at this point in the history
* IMPROVED: show spectrum for each mod channel
IMPROVED: clicking on a visualisation will mute the channel

* ADDED: workaround for browsers that do not support audioWorkletNode

* FIXED: emptybuffer didn't empty all channels in fallback mode
FIXED: display left/right channels in fallback mode
IMPROVED: now show "mute" for muted channels
IMPROVED: call render once at startup/when mute change

* REMOVED: audio files from github
ADDED: more nice funky module files
UPDATED: readme with ModPlug copyright
UPDATED: Xbox ES5 version of mod-processor to latest version
  • Loading branch information
warpdesign authored Sep 17, 2018
1 parent 4492767 commit 547fe36
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 217 deletions.
33 changes: 16 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ Modplayer-js will fall back to the deprecated `ScriptProcessorNode` API in these
- Amiga 4 channel Sountracker/Noisetracker mod files with 4 channels and 15-31 instruments
- Stereo playback (channels 0 & 3 goes to the left chan, 1 & 2 to the right, just like on a real Amiga)
- LowPass filter (not sure it sounds right)
- Left/Right Spectrum vizualizers
- Ability to mute any of the 4 module channels
- Includes 8 Module files from the Amiga-era
- Individual Spectrum vizualizers for each channel (AudioWorklet mode only)
- Ability to mute any of the 4 module channels (AudioWorklet only)
- 26 great modules of various styles and times are downloaded from [The Mod Archive](https://modarchive.org)

Most note effects should be supported, including extended ones. Only effect not implemented is `Vibrato (0x4 & 0x6)`.
Most note effects should be supported, including extended ones.

# Dependencies

Expand All @@ -42,19 +42,6 @@ ModPlayer JS makes use of the following piece of software:

I also heavily used [MilkyTracker](https://milkytracker.titandemo.org/) and [webaudio-mod-player](https://mod.haxor.fi/) - which plays lot of module formats with high fidelity - to track down some timing bugs.

# Module files copyright

ModPlayer-js includes the following module files:

- agony.mod: music from Amiga game [Agony](https://www.youtube.com/watch?v=iRzIpghJeec) by [Tim Wright](https://twitter.com/CoLDSToRAGE)
- all_that_she_wants.mod: Ace of Base remix from 1993 by Crossair
- bigtime.mod: Björk Big Time Sensuality remix from 1994 by ISO from Axis group, appeared in the [Big Time Sensuality demo](https://www.youtube.com/watch?v=Y-2xUXk5F2w)
- cannonfodder.mod: music from [Cannon Fodder](https://www.youtube.com/watch?v=PiYuq6Ac3a0) Amiga game by [John Hare](https://twitter.com/johnhare) / [Richard Joseph](https://en.wikipedia.org/wiki/Richard_Joseph)
- desert_strike.mod: music from [Desert Strike](https://www.youtube.com/watch?v=hcJaph0D7UM) Amiga game by Jason Whitley
- LotusII.mod: music from [Lotus II](https://www.youtube.com/watch?v=vETonlaTZ4c) game by [Barry Leitch](https://en.wikipedia.org/wiki/Barry_Leitch)
- projectx.mod: music from [Project-X](https://www.youtube.com/watch?v=gjq-ONi3dZE) Amiga game by [Allister Brimble](https://twitter.com/allisterbrimble)
- silkworm.mod: music from [Silkworm](https://www.youtube.com/watch?v=4wNidIucUuc) Amiga game by [Barry Leitch](https://en.wikipedia.org/wiki/Barry_Leitch)

# Module background

Modules are like MIDI files but with custom sound samples instead of builtin synth files.
Expand Down Expand Up @@ -107,3 +94,15 @@ Then comes the patterns data: first the list of positions and then each patterns
Last but not least, sample data is stored, uncompressed in LPCM 8bit format.

More information can be found in the [original specs](https://github.com/cmatsuoka/tracker-history/blob/master/reference/amiga/soundtracker/Soundtracker_v1-v9/Soundtracker_v2.doc) file (which was written in 1988: ouch!).

# Module files copyright

No file is hosted in GitHub, all modules are downloaded from [The Mod Archive](https://modarchive.org) and are licensed under the [Mod Archive Distribution License](https://modarchive.org/index.php?terms-upload)

- [agony.mod](https://api.modarchive.org/downloads.php?moduleid=124303#agony_intro.mod): music from Amiga game [Agony](https://www.youtube.com/watch?v=iRzIpghJeec) by [Tim Wright](https://twitter.com/CoLDSToRAGE)
- [bigtime.mod](https://api.modarchive.org/downloads.php?moduleid=98051#big_time_sensuality.mod): Björk Big Time Sensuality remix from 1994 by ISO from Axis group, appeared in the [Big Time Sensuality demo](https://www.youtube.com/watch?v=Y-2xUXk5F2w)
- [cannonfodder.mod](https://api.modarchive.org/downloads.php?moduleid=34568#CANNONFO.MOD): music from [Cannon Fodder](https://www.youtube.com/watch?v=PiYuq6Ac3a0) Amiga game by [John Hare](https://twitter.com/johnhare) / [Richard Joseph](https://en.wikipedia.org/wiki/Richard_Joseph)
- [desert_strike.mod](https://api.modarchive.org/downloads.php?moduleid=68835#desert_strike.mod): music from [Desert Strike](https://www.youtube.com/watch?v=hcJaph0D7UM) Amiga game by Jason Whitley
- [LotusII.mod](https://api.modarchive.org/downloads.php?moduleid=87180#lotus2-title.mod): music from [Lotus II](https://www.youtube.com/watch?v=vETonlaTZ4c) game by [Barry Leitch](https://en.wikipedia.org/wiki/Barry_Leitch)
- [projectx.mod](https://api.modarchive.org/downloads.php?moduleid=56660#projectx.mod): music from [Project-X](https://www.youtube.com/watch?v=gjq-ONi3dZE) Amiga game by [Allister Brimble](https://twitter.com/allisterbrimble)
- [silkworm.mod](https://api.modarchive.org/downloads.php?moduleid=83115#silkwormtitle.mod): music from [Silkworm](https://www.youtube.com/watch?v=4wNidIucUuc) Amiga game by [Barry Leitch](https://en.wikipedia.org/wiki/Barry_Leitch)
Binary file removed audio/LotusII.mod
Binary file not shown.
Binary file removed audio/agony.mod
Binary file not shown.
Binary file removed audio/all_that_she_wants.mod
Binary file not shown.
Binary file removed audio/bigtime.mod
Binary file not shown.
Binary file removed audio/cannonfodder.mod
Binary file not shown.
Binary file removed audio/desert_strike.mod
Binary file not shown.
Binary file removed audio/projectx.mod
Binary file not shown.
Binary file removed audio/silkworm.mod
Binary file not shown.
27 changes: 3 additions & 24 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
}

.mdl-mini-footer--link-list li, .mdl-mini-footer__link-list li{
margin-right: 32px;
margin: 0 16px;
}

.author{
Expand All @@ -61,6 +61,8 @@

canvas{
width:100%;
height:100px;
cursor:pointer;
}
</style>
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
Expand Down Expand Up @@ -125,35 +127,12 @@
.mdl-card__title{
position:relative;
}

.channel_control{
position:absolute;
top:10px;
}
</style>

<div class="demo-card-wide mdl-card mdl-shadow--2dp">
<div class="mdl-card__title">
<canvas width="512" height="128" id="visualizer"></canvas>
<h2 class="mdl-card__title-text" title="Click here to load another module"><div><i class="material-icons">audiotrack</i></div><div><span class="title"></span><br />by&nbsp;<span class="author"></span></div></h2>
<div class="channel_control">
<label class="mdl-icon-toggle mdl-js-icon-toggle mdl-js-ripple-effect" for="channel-toggle-1" title="Click to mute/unmute channel 1">
<input type="checkbox" id="channel-toggle-1" class="mdl-icon-toggle__input" checked>
<i class="mdl-icon-toggle__label material-icons">volume_up</i>
</label>
<label class="mdl-icon-toggle mdl-js-icon-toggle mdl-js-ripple-effect" for="channel-toggle-2" title="Click to mute/unmute channel 2">
<input type="checkbox" id="channel-toggle-2" class="mdl-icon-toggle__input" checked>
<i class="mdl-icon-toggle__label material-icons">volume_up</i>
</label>
<label class="mdl-icon-toggle mdl-js-icon-toggle mdl-js-ripple-effect" for="channel-toggle-3" title="Click to mute/unmute channel 3">
<input type="checkbox" id="channel-toggle-3" class="mdl-icon-toggle__input" checked>
<i class="mdl-icon-toggle__label material-icons">volume_up</i>
</label>
<label class="mdl-icon-toggle mdl-js-icon-toggle mdl-js-ripple-effect" for="channel-toggle-4" title="Click to mute/unmute channel 4">
<input type="checkbox" id="channel-toggle-4" class="mdl-icon-toggle__input" checked>
<i class="mdl-icon-toggle__label material-icons">volume_up</i>
</label>
</div>
</div>
<div class="mdl-card__supporting-text">
<div class="mdl-tabs mdl-js-tabs mdl-js-ripple-effect">
Expand Down
178 changes: 80 additions & 98 deletions js/main.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,46 @@
var moduleList = [
{ file: 'agony.mod', author: 'Tim Wright' },
{ file: 'all_that_she_wants.mod', author: 'Crossair' },
{ file: 'bigtime.mod', author: 'ISO/Axis Group' },
{ file: 'cannonfodder.mod', author: 'John Hare' },
{ file: 'desert_strike.mod', author: 'Jason Whitley' },
{ file: 'LotusII.mod', author: 'Barry Leitch' },
{ file: 'projectx.mod', author: 'Allister Brimble' },
{ file: 'silkworm.mod', author: 'Barry Leitch' }
{ file: 'https://api.modarchive.org/downloads.php?moduleid=91286#faggots_universe.mod', author: 'Deelite' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=168739#neurodancer_-_quasar.mod', author: 'Neurodancer' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=183672#tax_haven_dry_hump.mod', author: 'Curt Cool' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=182057#h0ffman_-_drop_the_panic.mod', author: 'h0ffman' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=168122#prodigy_-_downtown.mod', author: 'prodigy of oops' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=172266#hoffman_-_the_hunter.mod', author: 'h0ffman' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=65280#variations.mod', author: 'jogeir-liljedahl' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=166686#wiklund_-_bonfire.mod', author: 'Wiklund' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=55058#pinball_illusions.mod', author: 'Olof Gustafsson' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=167668#vinnie_-_sweet_dreams.mod', author: 'vinnie/spaceballs' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=172271#subi-king_of_boggle.mod', author: 'Subi/DESiRE' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=171416#bass-1107.mod', author: 'Noiseless (cm/ao)' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=168110#punnik_-_drum_bass.mod', author: 'punnik' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=171616#dan_-_childs_philozophy.mod', author: 'dan / picco' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=119303#boesendorfer_p_s_s.mod', author: 'romeoknight' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=170637#ghost_in_the_cli.mod', author: 'h0ffman' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=158057#alf_-_no-mercy.mod', author: 'alf/vtl' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=105709#trans_atlantic.mod', author: 'Lizardking'},
{ file: 'https://api.modarchive.org/downloads.php?moduleid=124303#agony_intro.mod', author: 'Tim Wright' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=98051#big_time_sensuality.mod', author: 'ISO/Axis Group' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=34568#CANNONFO.MOD', author: 'John Hare' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=68835#desert_strike.mod', author: 'Jason Whitley' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=87180#lotus2-title.mod', author: 'Barry Leitch' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=56660#projectx.mod', author: 'Allister Brimble' },
{ file: 'https://api.modarchive.org/downloads.php?moduleid=83115#silkwormtitle.mod', author: 'Barry Leitch' }
],
selectedMod = 0,
prefix = 'audio/',
toast;

window.onload = function () {
var canvas = document.getElementById('visualizer'),
effect = 1,
effects = [],
ctx = canvas.getContext('2d'),
canvasWidth = canvas.width,
canvasHeight = canvas.height,
channelsPlaying = [true, true, true, true];
channelsPlaying = [true, true, true, true],
audioWorkletSupport = !!AudioWorkletNode.toString().match(/native code/);

toast = new Toast('info-snackbar');

document.addEventListener('moduleLoaded', (event) => {
toast.show(`Module loaded: ${moduleList[selectedMod].file}`);
const split = moduleList[selectedMod].file.split('#'),
name = split.length > 1 && split[1] || split[0];

toast.show(`Module loaded: ${name}`);

const samples = event.data.samples;
let str = '';
Expand All @@ -37,7 +53,7 @@ window.onload = function () {
document.querySelector('.sample-list').innerHTML = str;

document.querySelector('.song-title').innerText = event.data.title;
document.querySelector('.title').innerText = moduleList[selectedMod].file;
document.querySelector('.title').innerText = name;
document.querySelector('.author').innerText = moduleList[selectedMod].author;
document.querySelector('.song-length').innerText = event.data.length;
document.querySelector('.song-samples').innerText = event.data.samples.length;
Expand Down Expand Up @@ -65,7 +81,9 @@ window.onload = function () {
if (i === selectedMod) {
options += ' selected';
}
options += `">${module.file}</a>`;
const split = module.file.split('#'),
name = split.length > 1 && split[1] || split[0];
options += `">${name}</a>`;
});

modNav.innerHTML = options;
Expand All @@ -78,97 +96,61 @@ window.onload = function () {
document.querySelector('.mdl-layout__obfuscator').click();
});

document.addEventListener('analyzer_ready', (event) => {
requestAnimationFrame(() => {
effects[effect](event.data);
});
});

document.querySelector('.channel_control').addEventListener('click', (event) => {
if (event.target.id && event.target.id.match(/channel-toggle/)) {
var channel = event.target.id.substr(-1, 1),
checked = event.target.hasAttribute('checked');

channelsPlaying[channel - 1] = !channelsPlaying[channel - 1];

ModPlayer.setPlayingChannels(channelsPlaying);
}
});

canvas.onclick = () => {
effect++;
if (effect >= effects.length) {
effect = 0;
}
};

function drawBars(amplitudeArray) {
var bufferLength = amplitudeArray.length;
ctx.fillStyle = 'rgb(0, 0, 0)';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);

var barWidth = (canvasWidth / bufferLength) * 2.5 - 1;
barWidth *= 2;
var barHeight;
var x = 0;

for (var i = 0; i < bufferLength; i++) {
barHeight = amplitudeArray[i];

ctx.fillStyle = 'rgb(' + (barHeight + 100) + ',50,50)';
ctx.fillRect(x, canvasHeight - barHeight / 2, barWidth, barHeight / 2);

x += barWidth;
}
}

function drawOscillo(amplitudeArray) {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);

for (var i = 0; i < amplitudeArray.length; i++) {
var value = amplitudeArray[i] / 256;
var y = canvasHeight - (canvasHeight * value) - 1;
ctx.fillStyle = '#000000';
ctx.fillRect(i, y, 1, 1);
canvas.addEventListener('click', (event) => {
const width = canvas.width / 4,
channel = Math.floor(event.offsetX / width);

// audioworklet mode shows the four channels
// scriptprocessor fallback groups 0-3 and 1-2 channels visually
if (audioWorkletSupport) {
channelsPlaying[channel] = !channelsPlaying[channel];
} else {
if (!channel) {
channelsPlaying[0] = !channelsPlaying[0];
channelsPlaying[3] = !channelsPlaying[3];
} else if (channel === 3) {
channelsPlaying[1] = !channelsPlaying[1];
channelsPlaying[2] = !channelsPlaying[2];
}
}
}

function drawOscillo2(amplitudeArray) {
var bufferLength = amplitudeArray.length;

ctx.fillStyle = "rgb(200, 200, 200)";
ctx.fillRect(0, 0, canvas.width, canvas.height);

ctx.lineWidth = 2;
ctx.strokeStyle = "rgb(0, 0, 0)";
ModPlayer.setPlayingChannels(channelsPlaying);
});

ctx.beginPath();
// function drawBars(amplitudeArray) {
// var bufferLength = amplitudeArray.length;
// ctx.fillStyle = 'rgb(0, 0, 0)';
// ctx.fillRect(0, 0, canvasWidth, canvasHeight);

var sliceWidth = canvas.width * 1.0 / bufferLength;
var x = 0;
// var barWidth = (canvasWidth / bufferLength) * 2.5 - 1;
// barWidth *= 2;
// var barHeight;
// var x = 0;

for (var i = 0; i < bufferLength; i++) {
// for (var i = 0; i < bufferLength; i++) {
// barHeight = amplitudeArray[i];

var v = amplitudeArray[i] / 128.0;
var y = v * canvas.height / 2;
// ctx.fillStyle = 'rgb(' + (barHeight + 100) + ',50,50)';
// ctx.fillRect(x, canvasHeight - barHeight / 2, barWidth, barHeight / 2);

if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
// x += barWidth;
// }
// }

x += sliceWidth;
}

ctx.lineTo(canvas.width, canvas.height / 2);
ctx.stroke();
}
// function drawOscillo(amplitudeArray) {
// ctx.clearRect(0, 0, canvasWidth, canvasHeight);

effects.push(drawBars, drawOscillo);
// for (var i = 0; i < amplitudeArray.length; i++) {
// var value = amplitudeArray[i] / 256;
// var y = canvasHeight - (canvasHeight * value) - 1;
// ctx.fillStyle = '#000000';
// ctx.fillRect(i, y, 1, 1);
// }
// }

ModPlayer.init({
canvas: canvas
canvas: canvas,
audioWorkletSupport: audioWorkletSupport
}).then(() => {
loadModule(selectedMod, false);
}).catch((err) => {
Expand Down Expand Up @@ -211,7 +193,7 @@ function loadModule(moduleIndex, hideDrawer = true) {
document.querySelector('a.mdl-navigation__link.selected').classList.toggle('selected');
document.querySelector(`a.mdl-navigation__link.mod_${moduleIndex}`).classList.add('selected');

ModPlayer.loadModule(prefix + moduleName)
ModPlayer.loadModule(moduleName.match(/^http/) ? moduleName : prefix + moduleName)
.catch(err => {
toast.show(`Error loading module: ${err}`);
});
Expand Down
Loading

0 comments on commit 547fe36

Please sign in to comment.