Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@types/node": "^16.11.6",
"d3": "^7.1.1",
"jacdac-ts": "^1.24.20",
"react-copy-to-clipboard": "^5.1.0",
"react-jacdac": "^1.1.3",
"rxjs": "^7.5.2",
"rxjs-spy": "^8.0.2",
Expand Down
1 change: 1 addition & 0 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ReactDOM.render(
<Routes>
<Route path="/" element={<Index />} />
<Route path="basic" element={<Demo />} />
,
<Route
path="jacdac"
element={
Expand Down
68 changes: 61 additions & 7 deletions src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { DatumOutput } from '../sonification/output/DatumOutput'
import { NoteSonify } from '../sonification/output/NoteSonify'
import { NoiseSonify } from '../sonification/output/NoiseSonify'
import { Speech } from '../sonification/output/Speech'
import { Copy } from '../sonification/output/Copy'
import { NoteHandler } from '../sonification/handler/NoteHandler'
import { OutputStateChange } from '../sonification/OutputConstants'
import { Datum } from '../sonification/Datum'
Expand All @@ -59,6 +60,7 @@ import { RunningExtremaHandler } from '../sonification/handler/RunningExtremaHan
import { SlopeParityHandler } from '../sonification/handler/SlopeParityHandler'
import { FileOutput } from '../sonification/output/FileOutput'
import { SimpleDataHandler } from '../sonification/handler/SimpleDataHandler'
import { CopyToClipboardHandler } from '../sonification/handler/CopyToClipboardHandler'

export interface JDServiceWrapper {
name: string
Expand Down Expand Up @@ -219,8 +221,8 @@ export const AVAILABLE_DATA_OUTPUT_TEMPLATES = {
sp.polite = value == 1 ? true : false
}
},
}
]
},
],
},
}

Expand Down Expand Up @@ -355,6 +357,55 @@ export const AVAILABLE_DATA_HANDLER_TEMPLATES: DataHandlerWrapper[] = [
dataOutputs: [initializeDataOutput(AVAILABLE_DATA_OUTPUT_TEMPLATES.speech)],
createHandler: (domain: [number, number]) => new SimpleDataHandler(),
},
{
name: 'Copy Handler',
id: `Copy Handler-${Math.floor(Math.random() * Date.now())}`,
description: 'Copies the data of the chosen sensor',
dataOutputs: [],
createHandler: (domain: [number, number]) =>
new CopyToClipboardHandler([
(domain[1] - domain[0]) * 0.4 + domain[0],
(domain[1] - domain[0]) * 0.6 + domain[0],
]),
parameters: [
{
name: 'Min',
type: 'number',
default: (obj?: DataHandler | DatumOutput) => {
if (obj) {
const frh = obj as CopyToClipboardHandler
return frh.domain[0]
} else {
return 0.4
}
},
handleUpdate: (value: number, obj?: DataHandler | DatumOutput) => {
if (obj) {
const frh = obj as CopyToClipboardHandler
frh.domain = [value, frh.domain[1]]
}
},
},
{
name: 'Max',
type: 'number',
default: (obj?: DataHandler | DatumOutput) => {
if (obj) {
const frh = obj as CopyToClipboardHandler
return frh.domain[1]
} else {
return 0.6
}
},
handleUpdate: (value: number, obj?: DataHandler | DatumOutput) => {
if (obj) {
const frh = obj as CopyToClipboardHandler
frh.domain = [frh.domain[0], value]
}
},
},
],
},
]

function saveText(name: string, data: string, mimeType?: string) {
Expand Down Expand Up @@ -450,18 +501,21 @@ export function DashboardView() {
name: `${jds.specification.name} ${jds.device.name}`,
id: serviceId,
values: serviceInfo.values.map((v, i) => {
const sink = OutputEngine.getInstance().addSink(
`JacDac Service = ${jds.specification.name}; Index = ${i}`,
)
const description = `JacDac Service = ${jds.specification.name}; Index = ${i}`

const sink = OutputEngine.getInstance().addSink(description)
const sinkId = sink.id

const rawSubject = new Subject<Datum>()
let sinkName = `${jds.specification.name} ${jds.device.name} ${v}`

OutputEngine.getInstance().setStream(sinkId, rawSubject)

const jdUnsubscribe = jds.readingRegister.subscribe(REPORT_UPDATE, () => {
// console.log(jds.specification.name, v, jds.readingRegister.unpackedValue[i])
rawSubject.next(new Datum(sinkId, jds.readingRegister.unpackedValue[i]))
let datum: Datum = new Datum(sinkId, jds.readingRegister.unpackedValue[i])
datum.setSinkName(sinkName)
rawSubject.next(datum)
})

const unsubscribe = () => {
Expand Down Expand Up @@ -546,6 +600,7 @@ export function DashboardView() {
template: DataHandlerWrapper,
) => {
console.log(add, serviceId, valueId, template)

const servicesCopy = services.map((service) => {
if (serviceId === service.id) {
const values = service.values.map((value) => {
Expand Down Expand Up @@ -730,7 +785,6 @@ export function DashboardView() {
type: 'number',
}}
/> */}

</Box>
</Box>
<Box role="region" aria-labelledby="header-configure-add" sx={{ mb: 2, mt: 4 }}>
Expand Down
14 changes: 9 additions & 5 deletions src/pages/Demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ import Table from 'arquero/dist/types/table/table'
const DEMO_VIEW_MAP = {
simple: { value: 'simple', label: 'Simple sonification', component: DemoSimple },
highlightRegion: { value: 'highlightRegion', label: 'Highlight points for region', component: DemoHighlightRegion },
speechHighlight: { value: 'speechHighlight', label: 'Speak points in range', component: DemoSpeakRange},
fileOutput: {value: 'fileOutput', label: 'Point of interest notification', component: DemoFileOutput},
slopeParityV1: {value: 'slopeParityV1', label: 'Slope direction change notification', component: DemoSlopeParityV1},
slopeParityV2: {value: 'slopeParityV2', label: 'Slope parity notification', component: DemoSlopeParityV2},
runningExtrema: {value: 'runningExtrema', label: 'Running extrema notification', component: DemoRunningExtrema}
speechHighlight: { value: 'speechHighlight', label: 'Speak points in range', component: DemoSpeakRange },
fileOutput: { value: 'fileOutput', label: 'Point of interest notification', component: DemoFileOutput },
slopeParityV1: {
value: 'slopeParityV1',
label: 'Slope direction change notification',
component: DemoSlopeParityV1,
},
slopeParityV2: { value: 'slopeParityV2', label: 'Slope parity notification', component: DemoSlopeParityV2 },
runningExtrema: { value: 'runningExtrema', label: 'Running extrema notification', component: DemoRunningExtrema },
}

let demoViewRef: React.RefObject<DemoSimple<DemoProps, DemoState> | DemoHighlightRegion> = React.createRef()
Expand Down
49 changes: 26 additions & 23 deletions src/pages/MicrobitController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function filterNullish<T>(): UnaryFunction<Observable<T | null | undefined>, Obs
return pipe(filter((x) => x != null) as OperatorFunction<T | null | undefined, T>)
}

function MicroBitButton(props: {outputEngine: OutputEngine, service: JDService}) {
function MicroBitButton(props: { outputEngine: OutputEngine; service: JDService }) {
const { service, outputEngine } = props
const downEvent = useEvent(service, ButtonEvent.Down)
const upEvent = useEvent(service, ButtonEvent.Up)
Expand All @@ -54,34 +54,37 @@ function MicroBitButton(props: {outputEngine: OutputEngine, service: JDService})
const handleButton = (id: string, down: boolean) => {
// only act on an up event

if (down)
setState("down")
else
setState("up")
if (down) setState('down')
else setState('up')

if (down) return

if (id === "A+B") {
if (outputEngine.value === OutputStateChange.Play)
outputEngine.next(OutputStateChange.Stop)
else
outputEngine.next(OutputStateChange.Play)
if (id === 'A+B') {
if (outputEngine.value === OutputStateChange.Play) outputEngine.next(OutputStateChange.Stop)
else outputEngine.next(OutputStateChange.Play)
}
}
useEffect(() => {
if (instanceName === "") return
if (instanceName === '') return
downEvent.subscribe(EVENT, () => handleButton(instanceName, true))
}, [downEvent, instanceName])
useEffect(() =>{
if (instanceName === "") return
useEffect(() => {
if (instanceName === '') return
upEvent.subscribe(EVENT, () => handleButton(instanceName, false))
}, [upEvent, instanceName])

useEffect(() => {
const resolveIName = async () => setInstanceName(await service.resolveInstanceName())
resolveIName()
}, [])
return (<div key={service.friendlyName}><br/><>{instanceName} {state}</></div>)
return (
<div key={service.friendlyName}>
<br />
<>
{instanceName} {state}
</>
</div>
)
}

function ConnectButton() {
Expand Down Expand Up @@ -123,13 +126,11 @@ function ConnectButton() {
return () => unsubs?.()
}, [services])

useEffect(()=>{
OutputEngine.getInstance().subscribe((e: OutputStateChange)=>{
console.log("Chaning streaming state ", e)
if (e === OutputStateChange.Play)
setStreaming(true);
else
setStreaming(false);
useEffect(() => {
OutputEngine.getInstance().subscribe((e: OutputStateChange) => {
console.log('Chaning streaming state ', e)
if (e === OutputStateChange.Play) setStreaming(true)
else setStreaming(false)
})
return () => OutputEngine.getInstance().unsubscribe()
}, [])
Expand Down Expand Up @@ -234,7 +235,7 @@ function ConnectButton() {
}
}

console.log("STREAMING STATE: ", streaming)
console.log('STREAMING STATE: ', streaming)

return (
<>
Expand All @@ -245,7 +246,9 @@ function ConnectButton() {
</Button>
)}
{buttons &&
buttons.map((button,i) => <MicroBitButton key={i} service={button} outputEngine={OutputEngine.getInstance()}/>)}
buttons.map((button, i) => (
<MicroBitButton key={i} service={button} outputEngine={OutputEngine.getInstance()} />
))}
</>
)
}
Expand Down
49 changes: 49 additions & 0 deletions src/sonification/CopyEngine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Datum } from './Datum'
import { getSonificationLoggingLevel, OutputStateChange, SonificationLoggingLevel } from './OutputConstants'
import { DataSink } from './DataSink'
import { BehaviorSubject, distinctUntilChanged, map, merge, Observable, shareReplay, tap } from 'rxjs'
import assert from 'assert'
import { create } from 'rxjs-spy'

const DEBUG = false

export class CopyEngine {
/**
* The Output Engine. Enforce that there is only ever one.
* @todo ask group if there is a better way to enforce this.
*/
private static copyEngineInstance: CopyEngine
private copiedDataMap: Map<String, Datum[]> = new Map()

public setCopiedData(key: String | undefined, data: Datum[]) {
if (key !== undefined) {
if (this.copiedDataMap.has(key)) {
const existingArray = this.copiedDataMap.get(key)
if (existingArray) {
existingArray.push(...data)
}
} else {
this.copiedDataMap.set(key, data)
}
} else {
// Handle the case where SinkName is undefined
console.error('getSinkName() returned undefined')
}
}

public printCopiedData(): void {
console.log(this.copiedDataMap)
}

/**
* Create a new CopyEngine. Enforces that there is only ever one
* @returns The CopyEngine's instance..
*/
public static getInstance(): CopyEngine {
if (!CopyEngine.copyEngineInstance) {
CopyEngine.copyEngineInstance = new CopyEngine()
}

return CopyEngine.copyEngineInstance
}
}
11 changes: 9 additions & 2 deletions src/sonification/Datum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import * as d3 from 'd3'
*
* All data points have certain properties and abilities, however
* @field value The raw data value associated with this point
* @field adjustedValue An adjusted value that may be assigned to a point for output.
* @field previous The previous point in the sequence for this sink
* @method toString() Returns a string describing this data point
* @field sink The data sink this point is associated with [not sure if we need this pointer, but for completeness...]
*/
Expand All @@ -16,6 +14,7 @@ export class Datum {
value: number
sinkId: number
time: number
sinkName?: String

constructor(sinkId: number, value: number, time?: number) {
this.value = value
Expand All @@ -24,6 +23,14 @@ export class Datum {
else this.time = d3.now()
}

public setSinkName(sinkName: String) {
this.sinkName = sinkName
}

public getSinkName() {
return this.sinkName
}

public toString(): string {
return `(raw: ${this.value}, ${this.time})`
}
Expand Down
Loading