-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(game/game-loader): load keysounds
- Loading branch information
Showing
14 changed files
with
282 additions
and
18 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
|
||
import AudioLoader from 'bemuse/game/audio-loader' | ||
|
||
describe('AudioLoader', function() { | ||
|
||
describe('load', function() { | ||
|
||
var assets | ||
var keysounds | ||
var master | ||
var loader | ||
|
||
beforeEach(function() { | ||
assets = { file: sinon.stub() } | ||
keysounds = { files: sinon.stub() } | ||
master = { sample: sinon.stub() } | ||
loader = new AudioLoader(assets, master) | ||
}) | ||
|
||
it('should load file when matches', function() { | ||
keysounds.files.returns(['a.wav']) | ||
assets.file.withArgs('a.wav').returns(Promise.resolve({ | ||
read: () => Promise.resolve('ok1') | ||
})) | ||
master.sample.withArgs('ok1').returns(Promise.resolve('ok2')) | ||
return expect(loader.loadFrom(keysounds)).to.eventually.deep.eq({ | ||
'a.wav': 'ok2' | ||
}) | ||
}) | ||
|
||
it('should try mp3', function() { | ||
keysounds.files.returns(['a.wav']) | ||
assets.file.withArgs('a.wav').returns(Promise.reject()) | ||
assets.file.withArgs('a.mp3').returns(Promise.resolve({ | ||
read: () => Promise.resolve('ok1') | ||
})) | ||
master.sample.withArgs('ok1').returns(Promise.resolve('ok2')) | ||
return expect(loader.loadFrom(keysounds)).to.eventually.deep.eq({ | ||
'a.wav': 'ok2' | ||
}) | ||
}) | ||
|
||
it('should not include failed matches', function() { | ||
keysounds.files.returns(['a.wav']) | ||
assets.file.withArgs('a.wav').returns(Promise.reject()) | ||
assets.file.withArgs('a.mp3').returns(Promise.reject()) | ||
return expect(loader.loadFrom(keysounds)).to.eventually.deep.eq({ }) | ||
}) | ||
|
||
}) | ||
|
||
}) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"files":[{"name":"do.mp3","ref":[0,0,30093]},{"name":"mi.mp3","ref":[0,30093,60186]},{"name":"sol.mp3","ref":[0,60186,90279]}],"refs":[{"path":"mp3.0.ea6087c3.bemuse","hash":"ea6087c3379bce007e50c3100b99b83c"}]} |
Binary file not shown.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
|
||
import BemusePackageResources from 'bemuse/resources/bemuse-package' | ||
|
||
describe('BemusePackageResources', function() { | ||
|
||
describe('#file', function() { | ||
|
||
var resources | ||
|
||
beforeEach(function() { | ||
resources = new BemusePackageResources('/spec/resources/fixtures/a/') | ||
}) | ||
|
||
it('returns a file', function() { | ||
return expect(resources.file('do.mp3')).to.be.fulfilled | ||
}) | ||
|
||
it('rejects if file not found', function() { | ||
return expect(resources.file('wow.mp3')).to.be.rejected | ||
}) | ||
|
||
it('can be read', function() { | ||
return expect(resources.file('mi.mp3') | ||
.then(file => file.read()) | ||
.then(buffer => buffer.byteLength)).to.eventually.eq(30093) | ||
}) | ||
|
||
}) | ||
|
||
}) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
|
||
import R from 'ramda' | ||
import SamplingMaster from 'bemuse/sampling-master' | ||
import throat from 'throat' | ||
|
||
export class AudioLoader { | ||
constructor(assets, master) { | ||
this._assets = assets | ||
this._master = master || new SamplingMaster() | ||
} | ||
loadFrom(keysounds) { | ||
let files = keysounds.files() | ||
let notifier = new ProgressNotifier(files.length, | ||
this.audioTask, this.decodeTask) | ||
let load = name => this._loadSample(name, notifier) | ||
return Promise.map(files, load).then(R.fromPairs) | ||
} | ||
_loadSample(name, notifier) { | ||
let decode = throat(8, buffer => this._master.sample(buffer)) | ||
return this._getFile(name) | ||
.then( | ||
file => file.read() | ||
.tap(() => notifier.loaded(name)) | ||
.then(decode) | ||
.tap(() => notifier.decoded(name)) | ||
.then(sample => [name, sample]) | ||
.catch(e => { | ||
console.error('Unable to decode: ' + name, e) | ||
return null | ||
}), | ||
() => null | ||
) | ||
} | ||
_getFile(name) { | ||
return this._assets.file(name) | ||
.catch(() => this._assets.file(name.replace(/\.\w+$/, '.mp3'))) | ||
} | ||
} | ||
|
||
export default AudioLoader | ||
|
||
function ProgressNotifier(total, load, decode) { | ||
let a = 0 | ||
let b = 0 | ||
return { | ||
loaded: function(name) { | ||
a += 1 | ||
load.update({ current: a, total, progress: a / total }) | ||
void name | ||
}, | ||
decoded: function(name) { | ||
b += 1 | ||
decode.update({ text: 'Decoded ' + name, progress: b / total }) | ||
}, | ||
} | ||
} |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
|
||
import { resolve } from 'url' | ||
import addLazyProperty from 'lazy-property' | ||
import R from 'ramda' | ||
import download from 'bemuse/download' | ||
import readBlob from 'bemuse/read-blob' | ||
import throat from 'throat' | ||
|
||
export class BemusePackageResources { | ||
constructor(url) { | ||
let lazy = addLazyProperty.bind(null, this) | ||
this._url = url | ||
lazy('metadata', () => | ||
download(resolve(this._url, 'metadata.json')).as('text') | ||
.then(str => JSON.parse(str))) | ||
lazy('refs', () => | ||
this.metadata.then( | ||
metadata => metadata.refs.map(spec => | ||
new Ref(this, spec)))) | ||
this.getBlob = throat(2, this.getBlob) | ||
} | ||
get url() { | ||
return this._url | ||
} | ||
file(name) { | ||
return this.metadata.then(metadata => { | ||
let file = R.find(file => file.name === name, metadata.files) | ||
if (!file) throw new Error('Unable to find: ' + name) | ||
return new BemusePackageFileResource(this, file.ref) | ||
}) | ||
} | ||
getBlob(ref) { | ||
let [index, start, end] = ref | ||
return this.refs | ||
.then(refs => refs[index]) | ||
.then(ref => ref.load()) | ||
.then(payload => payload.slice(start, end)) | ||
} | ||
} | ||
|
||
class BemusePackageFileResource { | ||
constructor(resources, ref) { | ||
this._resources = resources | ||
this._ref = ref | ||
} | ||
read() { | ||
return this._resources.getBlob(this._ref) | ||
.then(blob => readBlob(blob).as('arraybuffer')) | ||
} | ||
} | ||
|
||
class Ref { | ||
constructor(resources, spec) { | ||
this._load = () => | ||
this._promise || (this._promise = | ||
download(resolve(resources.url, spec.path)).as('blob') | ||
.then(getPayload)) | ||
} | ||
load() { | ||
return this._load() | ||
} | ||
} | ||
|
||
export default BemusePackageResources | ||
|
||
function getPayload(blob) { | ||
return readBlob(blob.slice(0, 10)).as('text') | ||
.then(magic => { | ||
if (magic !== 'BEMUSEPACK') { | ||
throw new Error('Invalid magic number') | ||
} | ||
return readBlob(blob.slice(10, 14)).as('arraybuffer') | ||
}) | ||
.then(buffer => { | ||
let array = new Uint8Array(buffer) | ||
let length = array[0] + | ||
(array[1] << 8) + | ||
(array[2] << 16) + | ||
(array[3] << 24) | ||
return length | ||
}) | ||
.then(metadataLength => blob.slice(14 + metadataLength)) | ||
} | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
import download from 'bemuse/download' | ||
import { basename } from 'path' | ||
|
||
export class URLResource { | ||
constructor(url) { | ||
this._url = url | ||
} | ||
read(task) { | ||
return download(this._url).as('arraybuffer', task) | ||
} | ||
get name() { | ||
return basename(this._url) | ||
} | ||
} | ||
|
||
export default URLResource |
Oops, something went wrong.