Skip to content

Commit 80593c7

Browse files
tballmsftthomasjballriknoll
authored
Audio refactor (#6114)
* new library! * built with new lib * add a function * change signature * update * add sample * turn it up * add sample * enable speaker * working! * add sim functions * update for local serve * add disable * add mbdal back * fix v1 compilation for samples, add sim support, make search only * revert targetconfig change --------- Co-authored-by: Thomas Ball <[email protected]> Co-authored-by: Thomas Ball <[email protected]> Co-authored-by: Richard Knoll <[email protected]> Co-authored-by: Richard Knoll <[email protected]>
1 parent 0805cc1 commit 80593c7

File tree

10 files changed

+231
-1
lines changed

10 files changed

+231
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"samples.disable": "Disable audio",
3+
"samples.enable": "Enable audio",
4+
"samples.playAsync": "Play a sample",
5+
"samples.setSampleRate": "Set the sample rate"
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"samples|block": "samples",
3+
"{id:category}Samples": "Samples"
4+
}

libs/audio-samples/pxt.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "audio-samples",
3+
"description": "Play audio samples. micro:bit (V2) only",
4+
"dependencies": {
5+
"core": "file:../core"
6+
},
7+
"weight": 10,
8+
"files": [
9+
"samples.ts",
10+
"samples.cpp",
11+
"shims.d.ts"
12+
],
13+
"testFiles": [
14+
"test.ts"
15+
],
16+
"public": true,
17+
"searchOnly": true
18+
}

libs/audio-samples/samples.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include "pxt.h"
2+
#include "MicroBit.h"
3+
4+
#if MICROBIT_CODAL
5+
#include "SampleSource.h"
6+
#endif
7+
8+
using namespace pxt;
9+
10+
namespace samples {
11+
12+
/**
13+
* Enable audio
14+
*/
15+
//%
16+
void enable() {
17+
#if MICROBIT_CODAL
18+
uBit.audio.enable();
19+
#else
20+
target_panic(PANIC_VARIANT_NOT_SUPPORTED);
21+
#endif
22+
}
23+
24+
25+
/**
26+
* Disable audio
27+
*/
28+
//%
29+
void disable() {
30+
#if MICROBIT_CODAL
31+
uBit.audio.disable();
32+
#else
33+
target_panic(PANIC_VARIANT_NOT_SUPPORTED);
34+
#endif
35+
}
36+
37+
/**
38+
* Set the sample rate
39+
*/
40+
//%
41+
void setSampleRate(int src, int sampleRate) {
42+
#if MICROBIT_CODAL
43+
if (0 <= src && src < 4)
44+
uBit.audio.sampleSource[src]->setSampleRate(sampleRate);
45+
#else
46+
target_panic(PANIC_VARIANT_NOT_SUPPORTED);
47+
#endif
48+
}
49+
50+
bool isValidSample(Buffer buf) {
51+
if (!buf)
52+
return false;
53+
54+
// TODO: other checks here
55+
return true;
56+
}
57+
58+
/**
59+
* Play a sample
60+
*/
61+
//%
62+
void playAsync(int src, Buffer buf) {
63+
#if MICROBIT_CODAL
64+
if (0 <= src && src < 4 && isValidSample(buf))
65+
uBit.audio.sampleSource[src]->playAsync(buf->data, buf->length);
66+
#else
67+
target_panic(PANIC_VARIANT_NOT_SUPPORTED);
68+
#endif
69+
}
70+
71+
}

libs/audio-samples/samples.ts

Whitespace-only changes.

libs/audio-samples/shims.d.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Auto-generated. Do not edit.
2+
declare namespace samples {
3+
4+
/**
5+
* Enable audio
6+
*/
7+
//% shim=samples::enable
8+
function enable(): void;
9+
10+
/**
11+
* Disable audio
12+
*/
13+
//% shim=samples::disable
14+
function disable(): void;
15+
16+
/**
17+
* Set the sample rate
18+
*/
19+
//% shim=samples::setSampleRate
20+
function setSampleRate(src: int32, sampleRate: int32): void;
21+
22+
/**
23+
* Play a sample
24+
*/
25+
//% shim=samples::playAsync
26+
function playAsync(src: int32, buf: Buffer): void;
27+
}
28+
29+
// Auto-generated. Do not edit. Really.

libs/audio-samples/test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
let strings9 = hex`524946467406000057415645666d74201000000001000100602200006022000001000800646174615006000092a8515977939aae6a4a708998a98e47678297a29d4a5b78939bad7349738a9aa97a426b7d999fa95a587e92a0a4584f73899ba8934b6a8697a78b416277949cae7a4d788d9ca763446f809ba0aa5f5a8391a5944657748b9ca89a516c8b96a8723e6876979caf7f4f7d8ca19d4f4c6f819ca0ad675c8991a87e3f5e728d9da99d55708c99a459436978989caf8554828ca58b42546e849ca1af6f5f8c91a7643f61738f9da8a45b738e9e96464d687b969faf9158868da7713d5b6d859ca2b374638c94a04d4862768f9fa8ab62778da2813c55687f96a0b1965c878fa55c415d70869da1b67e688c9990404f61778ea0a9b0687a8ea16b3c5a667f97a0b39e6287949847495e71869da2ba856c8c9c7b3958607b8ea1aab77079929a54415d6a8196a1b5a7698698873b515d73869ea4be8c6e8e9b623a5c617c8da2abbc7978958d42495c6b8196a3b8ab6e859b7036585d76859ea7c2976f91924b415c637d8da2afc181779b7838525c6e8197a5bbb4738698553b5b5f77869faac4a06f96833c495c667c8da4b1c788769c6038565e6f8198a8bebd74888d40445a627788a0acc8a86e996a35515c697c8fa6b4cd8f7a95473f5760708297aac0c5768c7b354c5a667787a0b0cdb16f955137555d6a7d8fa8b8d3927c88364657636f8397b0c3cd7b8b6531525b677688a2b4d1b4738e3e3f56606b7d8eabb9da967f742d4e5666708498b3c7d380874c34565c697588a5b7d6ba797c2f4655636b7e8eafbede9b7f5a2c5456686f849ab6ccd4857c393c555e6a778aa6badabd7e63294d55666c7f90b2c3e0a278423156596a70859cb9cfd790652c4455626b778aaabfddc27d4a2b5156696b7f92b5c8e0ad68313a555d6a7184a0bdd4d9924c2a4a55646b788cacc5dccb7633345159686c7e95b7cde0b5532941545f6a7286a1c2d5df9334304b58646c778eadc9dcd368243c515d686e7f99bcd1e1b73b294655616a7289a3c7d5e58d1f384c5b656d7892b1cedbd8511e435060686e809cbfd2e5b7262e4757636b738ba7ccd5e97c143f4b5e656e7996b4d2dcdc3e1f4750626970839ec4d2e9ac1436475a626d738fa9cfd5ed690f454b61656f7a98b8d3ded52b244954636a7187a1c8d2ed9a0b3d485d636e7592aed3d6ea5613494c6266707d9bbdd2e3c51f2c4855636b738aa5cbd4ec860e41495e646e7794b1d2dbdf441a484f636772809ec0d4e6b11e314858636c748ca9ccd7e47415414b60656f7a98b7d1e0cb4023475263677384a3c2d5e39b24344a5a646c778eafcadcd56820404e60666f7e9abad1e1b24327475563697586a7c3dada892d334c59666c7b91b3cae0c064293e525f6772819dbad4dd9d492a485464697889acc1dfc77d3c305059686d7e95b6c9e2a7672f3d5360687583a4bbd8d089582a4b55646a7c8db0c0e1af78442f535968708399b8ccda8e71333e5560697888a7badcbb7e632a4d55656b7f91b3c2e0967f4c30555a6972869eb6d0cb7e7d354056606a7a8baabadca27c6e2a4f56666e8395b2c5d580885232565a697689a2b6d3b37488364255616c7f91acbdd78d81752d50576772869ab1c8c374905534555a6a798ea5b8d19c768f3b4257616f8197abc2c8798979314e58667489a0b1ccac6f985a35545c6c7b92a5bbc9877d924142586173849cabc5b571917c354d5968788ea2b3c892739b6037555c707f98a6beba77849547415a627788a0abc69c7095823a4d5b6b7d93a3b6bd7d7c9d6737575d73839ea4c3a1708b964f3f5b637b8ca1aec1817698863c4d5c6e7f99a1bbab6f849c6f37575e7787a0a5c188728f99543e5c667e91a1b1b26f7f968d3e4c5d70849da3bc926b8b9b7538575f7a8ca1a9b87179909d573e5c688197a2b49e65889595424b5e74889ea4b8786f8e9c7b3957637d91a2ada8628190a25e3f5d6b849ba2b585648d9598444c60768e9fa9af67768e9f803a57658096a1b0915c8890a4603f5e6e8b9ca6b070688f979d484a6479949ead9a597e8ea2853d576a8499a2b0795d8d90a7663e62718f9caaa25d6f8f99a04d4a697c989eaf8255848fa5883f586e8a9ba6a762638d91a86c3f6475959bae8b53778e9ca04f4a6c7f9aa1ac68598591a58e415a708e9aab96516c8d95a971406878989cb071527c909da4564c6f859aa4a254618593a590435e74929aae7f4b738b99aa77406a7c9aa0a95c577e919fa55b4d74889ca88f466a8498a496465f7b93997c837d827e837e817e817f`
2+
3+
samples.enable()
4+
music.setVolume(100)
5+
6+
while(1)
7+
{
8+
basic.pause(2000)
9+
10+
samples.setSampleRate(0,11000)
11+
samples.playAsync(0, strings9)
12+
samples.setSampleRate(1,11000);
13+
samples.playAsync(1, strings9)
14+
samples.setSampleRate(2,6000);
15+
samples.playAsync(2, strings9)
16+
samples.setSampleRate(3,13000);
17+
samples.playAsync(3, strings9)
18+
}

pxtarget.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"corepkg": "core",
88
"bundleddirs": [
99
"libs/core",
10+
"libs/audio-samples",
1011
"libs/radio",
1112
"libs/devices",
1213
"libs/bluetooth",

sim/dalboard.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ namespace pxsim {
88
, RadioBoard
99
, LightBoard
1010
, MicrophoneBoard
11-
, ControlMessageBoard {
11+
, ControlMessageBoard
12+
, samples.SampleBoard {
1213
// state & update logic for component services
1314
ledMatrixState: LedMatrixState;
1415
edgeConnectorState: EdgeConnectorState;
@@ -26,6 +27,7 @@ namespace pxsim {
2627
logoTouch: Button;
2728
speakerEnabled: boolean = true;
2829
controlMessageState: ControlMessageState;
30+
samplesState: samples.SamplesState;
2931

3032
// visual
3133
viewHost: visuals.BoardHost;
@@ -119,6 +121,8 @@ namespace pxsim {
119121
this.builtinPartVisuals["buttonpair"] = (xy: visuals.Coord) => visuals.mkBtnSvg(xy);
120122
this.builtinPartVisuals["ledmatrix"] = (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8);
121123
this.builtinPartVisuals["microservo"] = (xy: visuals.Coord) => visuals.mkMicroServoPart(xy);
124+
125+
this.samplesState = new samples.SamplesState();
122126
}
123127

124128
ensureHardwareVersion(version: number) {

sim/state/audio-samples.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
namespace pxsim.samples {
2+
export interface SampleBoard extends EventBusBoard {
3+
samplesState: SamplesState;
4+
}
5+
6+
class SampleChannel {
7+
sampleRate = 11000;
8+
protected playing: AudioContextManager.PlaySampleResult;
9+
10+
constructor(public id: number) {
11+
12+
}
13+
14+
playSampleAsync(sample: RefBuffer) {
15+
if (this.playing) {
16+
this.playing.cancel();
17+
}
18+
this.playing = AudioContextManager.startSamplePlayback(sample, BufferMethods.NumberFormat.UInt8LE, 255, this.sampleRate, music.volume() / 0xff);
19+
this.playing.promise.then(() => {
20+
this.playing = undefined;
21+
});
22+
}
23+
}
24+
25+
export class SamplesState {
26+
protected channels: SampleChannel[] = [];
27+
protected enabled: boolean = false;
28+
29+
constructor() {
30+
this.channels = [
31+
new SampleChannel(0),
32+
new SampleChannel(1),
33+
new SampleChannel(2),
34+
new SampleChannel(3),
35+
];
36+
}
37+
38+
setEnabled(enabled: boolean): void {
39+
this.enabled = enabled;
40+
}
41+
42+
setSampleRate(channelId: number, sampleRate: number): void {
43+
channelId |= 0;
44+
if (channelId < 0 || channelId >= this.channels.length) {
45+
return;
46+
}
47+
48+
this.channels[channelId].sampleRate = sampleRate;
49+
}
50+
51+
playSampleAsync(channelId: number, sample: RefBuffer): void {
52+
if (!this.enabled) {
53+
return;
54+
}
55+
channelId |= 0;
56+
if (channelId < 0 || channelId >= this.channels.length) {
57+
return;
58+
}
59+
60+
this.channels[channelId].playSampleAsync(sample);
61+
}
62+
}
63+
64+
export function enable(): void {
65+
board().samplesState.setEnabled(true);
66+
}
67+
68+
export function disable(): void {
69+
board().samplesState.setEnabled(false);
70+
}
71+
72+
export function setSampleRate(src: number, sampleRate: number): void {
73+
board().samplesState.setSampleRate(src, sampleRate);
74+
}
75+
76+
export function playAsync(src: number, buf: RefBuffer): void {
77+
board().samplesState.playSampleAsync(src, buf);
78+
}
79+
}

0 commit comments

Comments
 (0)