From 3bbbe4ffb3131bd38cb015195d4f86e5fd5a9f98 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 13 May 2024 12:42:12 +0700 Subject: [PATCH 01/76] feat: add support for re-scanning with updated files detection --- packages/core/src/collections/base.ts | 203 ++++++++++++++---- packages/core/src/collections/watch.ts | 67 ++++-- packages/core/src/library/music_db.ts | 20 +- packages/core/src/library/music_library.ts | 79 +++++-- packages/core/src/metadata/helper.ts | 25 ++- packages/core/src/playout/boombox.ts | 18 +- packages/core/src/station/station.ts | 4 + packages/core/src/track.ts | 5 +- packages/radio/src/db/musicdb/mongo/worker.js | 14 +- .../src/discord/command/commands/rescan.ts | 36 +--- packages/radio/src/helper.ts | 4 +- 11 files changed, 345 insertions(+), 130 deletions(-) diff --git a/packages/core/src/collections/base.ts b/packages/core/src/collections/base.ts index 77dd9401..0a529343 100644 --- a/packages/core/src/collections/base.ts +++ b/packages/core/src/collections/base.ts @@ -1,11 +1,12 @@ import os from 'node:os'; +import { access, stat } from 'node:fs/promises'; import { createHash } from 'crypto'; import { TypedEmitter } from "tiny-typed-emitter"; -import { castArray, chain, chunk, clamp, findLastIndex, omit, partition, random, sample, shuffle, sortBy, zip } from "lodash"; +import { castArray, chain, chunk, clamp, findLastIndex, omit, partition, random, sample, shuffle, sortBy, stubFalse, stubTrue, zip } from "lodash"; import normalizePath from 'normalize-path'; import { Logger, createLogger } from '@seamless-medley/logging'; -import { Track } from "../track"; import { moveArrayElements, moveArrayIndexes, waitFor } from '@seamless-medley/utils'; +import { Track, TrackExtra, TrackExtraOf } from "../track"; export type TrackAddingMode = 'prepend' | 'append' | 'spread'; @@ -48,6 +49,12 @@ export type TrackPeek> = TrackIndex & { localIndex: number; } +export type TracksUpdateEvent> = { + tracks: Array; + updatedTracks: Array; + promises: Array>; +} + export type TrackCollectionEvents> = { ready: () => void; refresh: () => void; @@ -55,7 +62,7 @@ export type TrackCollectionEvents> = { trackPush: (track: T) => void; tracksAdd: (tracks: T[], chunkIndex: number, totalChunks: number) => void; tracksRemove: (tracks: T[]) => void; - tracksUpdate: (tracks: T[]) => void; + tracksUpdate: (event: TracksUpdateEvent) => void; } export const supportedExts = ['mp3', 'flac', 'wav', 'ogg', 'aiff']; @@ -65,7 +72,8 @@ export const knownExtRegExp = new RegExp(`\\.(${supportedExts.join('|')})$`, 'i' export type ChunkHandler = (chunk: T[], chunkIndex: number, totalChunks: number) => Promise; export class TrackCollection< - T extends Track, + T extends Track, + TE extends TrackExtra = TrackExtraOf, Extra = any, Options extends TrackCollectionOptions = TrackCollectionOptions > extends TypedEmitter> @@ -174,7 +182,12 @@ export class TrackCollection< return knownExtRegExp.test(filename); } - async #transform(paths: string[], onChunkCreated: ChunkHandler) { + async #transform(options: { + paths: string[]; + chunkSize?: number; + onChunkCreated: ChunkHandler + }) { + const { paths, onChunkCreated, chunkSize = 25 * os.cpus().length } = options; const validPaths = chain(paths) .castArray() .map(p => normalizePath(p)) @@ -193,7 +206,7 @@ export class TrackCollection< const immediateTracks: T[] = []; const newTracks: T[] = []; - const buckets = chunk(validPaths, 25 * os.cpus().length); + const buckets = chunk(validPaths, Math.max(os.cpus().length, chunkSize)); for (const [index, bucket] of buckets.entries()) { const created = new Array(bucket.length); @@ -216,67 +229,147 @@ export class TrackCollection< }; } - async add(paths: string[], mode?: TrackAddingMode, onChunkAdded?: ChunkHandler) { - return this.#transform(paths, async (chunk, chunkIndex, totalChunks) => { - const added = await this.#addTracks(chunk, chunkIndex, totalChunks, mode); - await onChunkAdded?.(chunk, chunkIndex, totalChunks); + async add(options: { + paths: string[]; + mode?: TrackAddingMode; + updateExisting?: boolean; + chunkSize?: number; + onChunkAdded?: ChunkHandler; + }) { + const { paths, mode, updateExisting, chunkSize, onChunkAdded } = options; + + let totalUpdatedCount = 0; + + const transformed = await this.#transform({ + paths, + chunkSize, + onChunkCreated: async (tracks, chunkIndex, totalChunks) => { + const { added, updatedCount } = await this.#addTracks({ + tracks, + chunkIndex, + totalChunks, + mode, + updateExisting + }); + + await onChunkAdded?.(tracks, chunkIndex, totalChunks); + + totalUpdatedCount += updatedCount; - return added; + return added; + } }); + + return { + ...transformed, + updatedCount: totalUpdatedCount + } } - async #addTracks(tracks: T[], chunkIndex: number, totalChunks: number, mode?: TrackAddingMode) { + async #addTracks(options: { + tracks: T[]; + chunkIndex: number; + totalChunks: number; + mode?: TrackAddingMode; + updateExisting?: boolean; + }) { + const { tracks, chunkIndex, totalChunks, mode, updateExisting } = options; const { tracksMapper } = this.options; const newTracks = tracks.filter(it => !this.trackIdMap.has(it.id)); - const mapped = await tracksMapper?.(newTracks) ?? newTracks; - - if (!mapped.length) { - return []; + let updatedCount = 0; + + if (updateExisting) { + const tracksToUpdate = (await Promise.all(tracks + .filter(it => this.trackIdMap.has(it.id)) + .map(async (track) => { + const s = await stat(track.path); + return (s.mtimeMs > (track.extra?.timestamp ?? 0)) + ? track + : undefined + }) + )).filter((t): t is Awaited => t !== undefined) + + const updated = tracksToUpdate.length > 0 + ? await this.#internalUpdate(tracksToUpdate) + : undefined; + + if (updated !== undefined) { + updatedCount = updated.length; + } } - switch (mode ?? this.options.newTracksAddingMode ?? 'spread') { - case 'append': - this.tracks.push(...mapped); - break; + const mapped = await tracksMapper?.(newTracks) ?? newTracks; - case 'prepend': - this.tracks.unshift(...mapped); - break; + if (mapped.length) { + switch (mode ?? this.options.newTracksAddingMode ?? 'spread') { + case 'append': + this.tracks.push(...mapped); + break; + + case 'prepend': + this.tracks.unshift(...mapped); + break; + + case 'spread': + { + let index = 0; + + for (const track of mapped) { + const width = Math.ceil(this.tracks.length / mapped.length) + 1; + index = clamp(random(index, index + width), 0, this.tracks.length - 1); + this.tracks.splice(index, 0, track); + } + } + break; + } - case 'spread': - { - let index = 0; + for (const track of mapped) { + this.trackIdMap.set(track.id, track); + } - for (const track of mapped) { - const width = Math.ceil(this.tracks.length / mapped.length) + 1; - index = clamp(random(index, index + width), 0, this.tracks.length - 1); - this.tracks.splice(index, 0, track); - } - } - break; + this.logger.info(`${mapped.length} track(s) added`); + this.emit('tracksAdd', mapped, chunkIndex, totalChunks); } - for (const track of mapped) { - this.trackIdMap.set(track.id, track); + return { + added: mapped, + updatedCount + }; + } + + async #internalUpdate(tracks: T[]) { + if (tracks.length <= 0) { + return; } - this.logger.info(`${mapped.length} track(s) added`); - this.emit('tracksAdd', mapped, chunkIndex, totalChunks); + const e: TracksUpdateEvent = { + tracks, + updatedTracks: [], + promises: [], + }; + + this.emit('tracksUpdate', e); + + await Promise.all(e.promises); - return mapped; + if (e.updatedTracks.length > 0) { + this.logger.info(`${e.updatedTracks.length} track(s) updated`); + } + + return e.updatedTracks; } async update(paths: string[]) { - return this.#transform(paths, async updated => { - const existing = updated.filter(it => this.trackIdMap.has(it.id)); + return this.#transform({ + paths, + onChunkCreated: async updated => { + const existing = updated.filter(it => this.trackIdMap.has(it.id)); - if (existing.length > 0) { - this.logger.info(`${existing.length} track(s) updated`); - this.emit('tracksUpdate', existing); - } + this.#internalUpdate(existing); - return existing; + return existing; + } }); } @@ -310,6 +403,26 @@ export class TrackCollection< return this.remove(toRemove); } + async removeNonExistent() { + const existences = await Promise.all(this.tracks.map(async track => ({ + track, + exists: await access(track.path).then(stubTrue).catch(stubFalse) + }))); + + const [existing, removed] = partition(existences, e => e.exists); + + this.tracks = existing.map(t => t.track); + const removedTracks = removed.map(t => t.track); + + for (const track of removedTracks) { + this.trackIdMap.delete(track.id); + } + + this.emit('tracksRemove', removedTracks); + + return removedTracks; + } + move(newPosition: number, indexes: number[]) { moveArrayIndexes(this.tracks, newPosition, ...indexes); } diff --git a/packages/core/src/collections/watch.ts b/packages/core/src/collections/watch.ts index 67ff226f..3e899ce2 100644 --- a/packages/core/src/collections/watch.ts +++ b/packages/core/src/collections/watch.ts @@ -9,7 +9,7 @@ import normalizePath from "normalize-path"; import watcher, { AsyncSubscription, SubscribeCallback, BackendType } from '@parcel/watcher'; import { ChunkHandler, TrackCollection, TrackCollectionOptions } from "./base"; -import { Track } from "../track"; +import { Track, TrackExtra, TrackExtraOf } from "../track"; import { breath } from '@seamless-medley/utils'; export type WatchOptions = { @@ -31,7 +31,20 @@ export type WatchTrackCollectionOptions> = TrackCollectionO scanner?: (dir: string) => Promise; } -export class WatchTrackCollection, Extra = any> extends TrackCollection> { +export type RescanStats = { + scanned: number; + added: number; + removed: number; + updated: number; +} + +type ScanOptions = { + dir: string; + updateExisting?: boolean; + chunkSize?: number; + onFirstChunkAdded?: () => any; +} +export class WatchTrackCollection, TE extends TrackExtra = TrackExtraOf, Extra = any> extends TrackCollection> { constructor(id: string, extra: Extra, options: WatchTrackCollectionOptions = {}) { super(id, extra, { tracksMapper: async (tracks) => shuffle(tracks), @@ -61,7 +74,7 @@ export class WatchTrackCollection, Extra = any> extends Tra return result; } - #storeNewFiles = debounce(() => this.add(this.#fetchNewPaths()), 2000); + #storeNewFiles = debounce(() => this.add({ paths: this.#fetchNewPaths() }), 2000); #fetchUpdatePaths(): string[] { const result = uniq(this.#updatePaths); @@ -149,7 +162,7 @@ export class WatchTrackCollection, Extra = any> extends Tra if (stats.isDirectory()) { // A sub folder rename results in a single create event, explicitly scan the path now - this.#scan(path); + this.#scan({ dir: path }); continue; } @@ -188,7 +201,7 @@ export class WatchTrackCollection, Extra = any> extends Tra if (info.subscription) { this.logger.info(`Resume subscription for ${dir}`); - await this.#scan(dir); + await this.#scan({ dir }); } } @@ -207,10 +220,13 @@ export class WatchTrackCollection, Extra = any> extends Tra return; } - await this.#scan(normalized, async () => { - this.logger.info(`Watching ${normalized}`); - await this.#subscribeToPath(normalized, options); - this.becomeReady(); + await this.#scan({ + dir: normalized, + onFirstChunkAdded: async () => { + this.logger.info(`Watching ${normalized}`); + await this.#subscribeToPath(normalized, options); + this.becomeReady(); + } }); } @@ -249,30 +265,38 @@ export class WatchTrackCollection, Extra = any> extends Tra #scanning = 0; - async #scan(dir: string, onFirstChunkAdded?: () => any) { + async #scan({ dir, onFirstChunkAdded, chunkSize, updateExisting }: ScanOptions) { if (this.#scanning === 0) { this.emit('scan' as any); } this.#scanning++; - const notifyOnce = once(onFirstChunkAdded ?? noop) as ChunkHandler; + const onChunkAdded = once(onFirstChunkAdded ?? noop) as ChunkHandler; const scanners = [this.#extScanner, this.#globScanner]; const counter = { scanned: 0, - added: 0 + added: 0, + updatedCount: 0 } for (const scanner of scanners) { const files = await scanner.call(this, dir); if (files !== false) { - const { scanned, added } = await this.add(shuffle(files), undefined, notifyOnce); + const { scanned, added, updatedCount } = await this.add({ + paths: shuffle(files), + updateExisting, + chunkSize, + onChunkAdded + }); + await breath(); counter.scanned += scanned.length; counter.added += added.length; + counter.updatedCount += updatedCount; break; } @@ -287,20 +311,25 @@ export class WatchTrackCollection, Extra = any> extends Tra return counter; } - async rescan(full?: boolean) { + async rescan(full?: boolean): Promise { if (this.#scanning) { return; } - if (full) { - this.clear(); - } + const removed = !full + ? await this.removeNonExistent() + : [] - const results = await Promise.all([...this.#watchInfos.keys()].map(dir => this.#scan(dir))) + const results = await Promise.all([...this.#watchInfos.keys()].map(dir => this.#scan({ + dir, + updateExisting: full + }))); return { scanned: sumBy(results, c => c.scanned), - added: sumBy(results, c => c.added) + added: sumBy(results, c => c.added), + updated: sumBy(results, c => c.updatedCount), + removed: removed.length } } diff --git a/packages/core/src/library/music_db.ts b/packages/core/src/library/music_db.ts index 3455c84c..8f277848 100644 --- a/packages/core/src/library/music_db.ts +++ b/packages/core/src/library/music_db.ts @@ -2,6 +2,7 @@ import type { Metadata } from "@seamless-medley/medley"; import normalizePath from "normalize-path"; import type { TrackRecord } from "../playout"; import type { SearchQuery, SearchQueryKey } from "./search"; +import { isEqual } from "lodash"; export type RecentSearchRecord = [term: string, count: number, timestamp: Date]; @@ -31,6 +32,10 @@ export interface TrackHistory { getAll(scope: string): Promise; } +export type UpdateInfo = { + modified: number; +} + export interface MusicDb { dispose(): void; @@ -42,7 +47,7 @@ export interface MusicDb { findByComment(field: string, value: string, limit: number): Promise; - update(trackId: string, update: Omit): Promise; + update(trackId: string, update: Omit): Promise; delete(trackId: string): Promise; @@ -54,6 +59,7 @@ export interface MusicDb { export interface MusicDbTrack extends Metadata { trackId: string; path: string; + timestamp?: number; } /** @@ -79,11 +85,17 @@ export class InMemoryMusicDb implements MusicDb { } async update(trackId: string, update: Omit) { - const existing = this.#tracks.get(trackId) ?? {}; - this.#tracks.set(trackId, { + const existing = this.#tracks.get(trackId); + + const track: MusicDbTrack = { + trackId, ...existing, ...update - } as MusicDbTrack) + }; + + this.#tracks.set(trackId, track); + + return track; } async delete(trackId: string){ diff --git a/packages/core/src/library/music_library.ts b/packages/core/src/library/music_library.ts index 22ec4ea8..6ab80233 100644 --- a/packages/core/src/library/music_library.ts +++ b/packages/core/src/library/music_library.ts @@ -1,13 +1,12 @@ -import { castArray, chain, isString, noop, partition } from 'lodash'; -import normalizePath from 'normalize-path'; -import { TrackCreator, WatchTrackCollection, TrackCollectionBasicOptions, TrackCollectionEvents, WatchPathWithOption } from '../collections'; +import { castArray, chain, isString, partition, stubFalse } from 'lodash'; +import { TrackCreator, WatchTrackCollection, TrackCollectionBasicOptions, TrackCollectionEvents, WatchPathWithOption, RescanStats, TracksUpdateEvent } from '../collections'; import { Logger, createLogger } from '@seamless-medley/logging'; import { BoomBoxTrack, TrackKind } from '../playout'; import { BaseLibrary } from './library'; import { SearchEngine, Query, TrackDocumentFields } from './search'; import { MetadataHelper } from '../metadata'; import { MusicDb } from './music_db'; -import { TrackWithCollectionExtra } from '../track'; +import { TrackExtraOf, TrackWithCollectionExtra } from '../track'; import { scanDir } from './scanner'; export type MusicCollectionWatch = WatchPathWithOption | string; @@ -23,9 +22,9 @@ export type MusicLibraryExtra = { owner: O; } -export type MusicTrack = TrackWithCollectionExtra>; +export type MusicTrack = TrackWithCollectionExtra, MusicLibraryExtra>; -export type MusicTrackCollection = WatchTrackCollection, MusicLibraryExtra>; +export type MusicTrackCollection = WatchTrackCollection, TrackExtraOf>, MusicLibraryExtra>; export type MusicTrackCollectionEvents = TrackCollectionEvents>; @@ -37,6 +36,11 @@ type IndexInfo = { type Stats = Record<'discovered' | 'indexing' | 'indexed', number>; +export type LibraryRescanStats = RescanStats & { + elapsedTime: number; + collection: MusicTrackCollection; +} + export interface MusicLibraryEvents { stats(stats: Stats): void; } @@ -82,7 +86,7 @@ export class MusicLibrary extends BaseLibrary, MusicL return; } - const { trackId: id, ...tags } = fromDb; + const { trackId: id, timestamp, ...tags } = fromDb; return { id, @@ -90,12 +94,13 @@ export class MusicLibrary extends BaseLibrary, MusicL musicId: fromDb.isrc, extra: { kind: TrackKind.Normal, + timestamp, tags } } } - #handleTrackAddition = (collection: WatchTrackCollection, MusicLibraryExtra>) => (tracks: Array>, chunkIndex: number, totalChunks: number) => { + #handleTrackAddition = (collection: WatchTrackCollection, TrackExtraOf>, MusicLibraryExtra>) => (tracks: Array>, chunkIndex: number, totalChunks: number) => { this.stats = { discovered: this.#stats.discovered + tracks.length } @@ -145,12 +150,20 @@ export class MusicLibrary extends BaseLibrary, MusicL } } - #handleTrackUpdates = async (tracks: Array>) => { - await this.#searchEngine.removeAll(tracks).catch(e => this.#logger.error(e)); + #handleTrackUpdates = async (event: TracksUpdateEvent>) => { + event.promises.push(new Promise(async (resolve) => { + await this.#searchEngine.removeAll(event.tracks).catch(e => this.#logger.error(e)); - for (const track of tracks) { - await this.#indexTrack({ track }, true).catch(noop); - } + for (const track of event.tracks) { + const modified = await this.#indexTrack({ track }, true).then(r => r.modified).catch(stubFalse); + + if (modified) { + event.updatedTracks.push(track); + } + } + + resolve(); + })) } remove(...collections: Array>) { @@ -195,23 +208,26 @@ export class MusicLibrary extends BaseLibrary, MusicL } async #indexTrack({ track, retried }: IndexInfo, force: boolean = false) { + let modified = false; + if (force || !track.extra?.tags) { - const { metadata } = await MetadataHelper.fetchMetadata(track, this.musicDb, force); + const { metadata, timestamp, modified: metadataUpdated } = await MetadataHelper.fetchMetadata(track, this.musicDb, force); track.musicId = metadata.isrc, track.extra = { ...track.extra, tags: metadata, + timestamp, kind: TrackKind.Normal }; - } - try { - await this.#searchEngine.add(track); + modified = metadataUpdated === true; } - catch (e) { + + this.#searchEngine.add(track).catch((e) => { this.#logger.error(e); - throw e; - } + }); + + return { modified }; } async addCollection(descriptor: MusicCollectionDescriptor, onceReady?: () => void): Promise | undefined> { @@ -227,7 +243,7 @@ export class MusicLibrary extends BaseLibrary, MusicL owner: this.owner } - const newCollection = new WatchTrackCollection, MusicLibraryExtra>( + const newCollection = new WatchTrackCollection, TrackExtraOf>, MusicLibraryExtra>( id, extra, { ...options, @@ -396,4 +412,25 @@ export class MusicLibrary extends BaseLibrary, MusicL return result.map(s => s.suggestion); } + + async rescan(full?: boolean, scanningCb?: (collection: MusicTrackCollection) => any): Promise[]> { + const stats: LibraryRescanStats[] = []; + + for (const collection of this) { + scanningCb?.(collection); + + const started = performance.now(); + const result = await collection.rescan(full); + + if (result) { + stats.push({ + ...result, + collection, + elapsedTime: (performance.now() - started) / 1000 + }); + } + } + + return stats; + } } diff --git a/packages/core/src/metadata/helper.ts b/packages/core/src/metadata/helper.ts index 6d1e4a2b..99f35654 100644 --- a/packages/core/src/metadata/helper.ts +++ b/packages/core/src/metadata/helper.ts @@ -5,7 +5,7 @@ import { WorkerPoolAdapter } from '../worker_pool_adapter'; import { MusicDb } from '../library/music_db'; import { omitBy, negate } from 'lodash/fp'; import { BoomBoxCoverAnyLyrics } from '../playout'; -import { stubFalse } from 'lodash'; +import { isEqual, omit, stubFalse } from 'lodash'; import { LyricProviderName, LyricsSearchResult } from './lyrics/types'; let instance: MetadataHelper; @@ -22,6 +22,8 @@ type WorkerCoverAndLyrics = Omit & { export type FetchResult = { hit: boolean; metadata: Metadata; + timestamp?: number; + modified?: boolean; } interface Methods { @@ -122,19 +124,30 @@ export class MetadataHelper extends WorkerPoolAdapter { } async fetchMetadata(track: Track, musicDb: MusicDb | undefined, refresh = false): Promise { + const cached = await musicDb?.findById(track.id); + const metadata = omit(cached, 'trackId', 'timestamp'); + if (!refresh) { - const cached = await musicDb?.findById(track.id); if (cached) { - return { hit: true, metadata: cached } + return { + hit: true, + timestamp: cached.timestamp, + metadata + } } } const fresh = await this.metadata(track.path); - fresh.comments = fresh.comments?.filter(([key]) => /^[^:]+:(?!\/\/)/i.test(key)) ?? []; // + fresh.comments = fresh.comments?.filter(([key]) => /^[^:]+:(?!\/\/)/i.test(key)) ?? []; - musicDb?.update(track.id, { ...fresh, path: track.path }); + const updated = await musicDb?.update(track.id, { ...fresh, path: track.path }); - return { hit: false, metadata: fresh }; + return { + hit: false, + timestamp: updated?.timestamp, + modified: refresh ? !isEqual(metadata, fresh) : false, + metadata: fresh + }; } async isTrackLoadable(path: string, timeout = 500) { diff --git a/packages/core/src/playout/boombox.ts b/packages/core/src/playout/boombox.ts index e9be9bfd..e73e7c59 100644 --- a/packages/core/src/playout/boombox.ts +++ b/packages/core/src/playout/boombox.ts @@ -40,6 +40,7 @@ export type BoomBoxTrackExtra = TrackExtra & { maybeAudioProperties?: Promise; maybeCoverAndLyrics?: Promise; kind: TrackKind; + timestamp?: number; } export type BoomBoxTrack = Track; @@ -269,18 +270,29 @@ export class BoomBox> = new WatchTrackCollection('$_requests', undefined); + #requests = new WatchTrackCollection>('$_requests', undefined); #isTrackLoadable: TrackValidator = async (path) => trackHelper.isTrackLoadable(path, 1000); #verifyTrack: TrackVerifier = async (track): Promise> => { try { - const metadata = track.extra?.tags ?? (await helper.fetchMetadata(track, this.musicDb, true)).metadata; + let timestamp: number | undefined; + let tags: Metadata; + + if (track.extra?.tags) { + timestamp = track.extra.timestamp; + tags = track.extra?.tags; + } else { + const fetched = await helper.fetchMetadata(track, this.musicDb, true); + timestamp = fetched.timestamp; + tags = fetched.metadata; + } const boomboxExtra: BoomBoxTrackExtra = { kind: TrackKind.Normal, ...track.extra, - tags: metadata + tags, + timestamp } const playedArtists = flatten(this.artistHistory).map(toLower); diff --git a/packages/core/src/station/station.ts b/packages/core/src/station/station.ts index 14fbed6b..4b4b8993 100644 --- a/packages/core/src/station/station.ts +++ b/packages/core/src/station/station.ts @@ -865,6 +865,10 @@ export class Station extends TypedEmitter { get allLatches(): ReadonlyArray> { return this.#boombox.allLatches; } + + async rescan(full?: boolean, scanningCb?: (collection: MusicTrackCollection) => any) { + return this.#library.rescan(full, scanningCb); + } } export class StationRegistry extends Library { diff --git a/packages/core/src/track.ts b/packages/core/src/track.ts index d6578ed9..7d6e5501 100644 --- a/packages/core/src/track.ts +++ b/packages/core/src/track.ts @@ -15,6 +15,7 @@ export interface Track extends TrackInfo { export type TrackExtra = { source?: string; + timestamp?: number; } export type TrackExtraOf> = T extends Track ? E : never; @@ -43,6 +44,6 @@ export type SequencedTrack> = Omit & { sequencing: TrackSequencing; } -export type TrackWithCollectionExtra, Extra> = Omit & { - readonly collection: TrackCollection; +export type TrackWithCollectionExtra, TE extends TrackExtra, Extra> = Omit & { + readonly collection: TrackCollection; } diff --git a/packages/radio/src/db/musicdb/mongo/worker.js b/packages/radio/src/db/musicdb/mongo/worker.js index 1078d479..3f8b3516 100644 --- a/packages/radio/src/db/musicdb/mongo/worker.js +++ b/packages/radio/src/db/musicdb/mongo/worker.js @@ -227,19 +227,29 @@ async function findByComment(field, value, limit = 1) { * @type {MusicDb['update']} */ const update = async (trackId, fields) => { + const track = { + ...fields, + timestamp: fields.timestamp ?? Date.now(), + } + await musics.updateOne({ trackId }, { $set: { - ...fields, + ...track, expires: Date.now() + random(...ttls) * 1000 } }, { upsert: true }) .catch((e) => { logger.error(e, 'Error in update'); }); + + return { + trackId, + ...track + } } /** - * @type {MusicDb['update']} + * @type {MusicDb['delete']} */ const _delete = async (trackId) => { await musics.deleteOne({ trackId }) diff --git a/packages/radio/src/discord/command/commands/rescan.ts b/packages/radio/src/discord/command/commands/rescan.ts index 28b78630..1cc7872a 100644 --- a/packages/radio/src/discord/command/commands/rescan.ts +++ b/packages/radio/src/discord/command/commands/rescan.ts @@ -1,7 +1,7 @@ import { CommandInteraction, blockQuote, inlineCode, unorderedList } from "discord.js"; import { CommandDescriptor, InteractionHandlerFactory, OptionType, SubCommandLikeOption } from "../type"; import { deny, guildStationGuard, joinStrings, reply } from "../utils"; -import { MusicTrackCollection, Station } from "@seamless-medley/core"; +import { LibraryRescanStats, MusicTrackCollection, Station } from "@seamless-medley/core"; const declaration: SubCommandLikeOption = { type: OptionType.SubCommand, @@ -17,42 +17,24 @@ const createCommandHandler: InteractionHandlerFactory = (aut return; } - const { station } = guildStationGuard(automaton, interaction); - await interaction.deferReply({ ephemeral: true }); - const { collections } = station; - - type Stat = { - scanned: number; - added: number; - elapsedTime: number; - } - - const stats: Array }> = []; + const { station } = guildStationGuard(automaton, interaction); - for (const col of collections) { - await reply(interaction, { + const stats = await station.rescan(true, (col) => { + reply(interaction, { ephemeral: true, content: `Re-scanning: ${col.extra.description}` }); + }) - const started = performance.now(); - const result = await col.rescan(); - if (result) { - stats.push({ - ...result, - collection: col, - elapsedTime: (performance.now() - started) / 1000 - }); - } - } + type Stat = Omit, 'collection'>; const formatter = new Intl.NumberFormat(); const formatNumber = (n: number) => inlineCode(formatter.format(n)); - const formatStat = (s: Stat) => `${formatNumber(s.scanned)} track(s) scanned in ${inlineCode(s.elapsedTime.toFixed(2))} seconds, ${formatNumber(s.added)} track(s) added`; + const formatStat = (s: Stat) => `${formatNumber(s.scanned)} track(s) scanned in ${inlineCode(s.elapsedTime.toFixed(2))} seconds, ${formatNumber(s.added)} added, ${formatNumber(s.removed)} removed, ${formatNumber(s.updated)} updated`; const lines = stats.map((s) => { return `${s.collection.extra.description}: ${formatStat(s)}`; @@ -61,9 +43,11 @@ const createCommandHandler: InteractionHandlerFactory = (aut const summary = stats.reduce((a, s) => { a.scanned += s.scanned; a.added += s.added; + a.removed += s.removed; + a.updated += s.updated; a.elapsedTime += s.elapsedTime; return a; - }, { scanned: 0, added: 0, elapsedTime: 0 } as Stat) + }, { scanned: 0, added: 0, removed: 0, updated: 0, elapsedTime: 0 } as Stat) await reply(interaction, { ephemeral: true, diff --git a/packages/radio/src/helper.ts b/packages/radio/src/helper.ts index 644ab646..b3057312 100644 --- a/packages/radio/src/helper.ts +++ b/packages/radio/src/helper.ts @@ -1,4 +1,4 @@ -import { Crate, MusicCollectionWatch, MusicDb, Station, StationProfile, StationRegistry, StationTrack, WatchPathWithOption, WatchTrackCollection, crateLimitFromSequenceLimit, createChanceable, scanDir } from "@seamless-medley/core"; +import { BoomBoxTrack, Crate, MusicCollectionWatch, MusicDb, Station, StationProfile, StationRegistry, StationTrack, WatchPathWithOption, WatchTrackCollection, crateLimitFromSequenceLimit, createChanceable, scanDir } from "@seamless-medley/core"; import { readFile } from 'node:fs/promises'; import { @@ -27,7 +27,7 @@ function createCrateFromSequence(id: string, station: Station, sequence: Sequenc } function createTrackCollection(id: string, paths: MusicCollectionWatch[] = [], logPrefix: string) { - const collection = new WatchTrackCollection(id, undefined, { logPrefix, scanner: scanDir }); + const collection = new WatchTrackCollection(id, undefined, { logPrefix, scanner: scanDir }); for (const path of paths) { collection.watch(isString(path) ? { dir: path } : path); From da14bb84f7017f4e56b7e1fc85ddecc241f6467e Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 13 May 2024 13:56:06 +0700 Subject: [PATCH 02/76] fix(core): where the playback did not start during the rescuing phase --- packages/core/src/playout/boombox.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/core/src/playout/boombox.ts b/packages/core/src/playout/boombox.ts index e73e7c59..c7ec76ef 100644 --- a/packages/core/src/playout/boombox.ts +++ b/packages/core/src/playout/boombox.ts @@ -214,6 +214,11 @@ export class BoomBox Date: Mon, 13 May 2024 18:56:14 +0700 Subject: [PATCH 03/76] refactor: tweak search params --- packages/core/src/station/station.ts | 13 +++++++++++-- .../radio/src/discord/automaton/guild-state.ts | 15 +++++++++++---- .../src/discord/command/commands/request/main.ts | 8 ++++---- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/core/src/station/station.ts b/packages/core/src/station/station.ts index 4b4b8993..3dffdf26 100644 --- a/packages/core/src/station/station.ts +++ b/packages/core/src/station/station.ts @@ -138,6 +138,13 @@ export type StationEvents = { type BoomBoxEventsForStation = BoomBoxEvents; +export type StationSearchOptions = { + q: SearchQuery; + limit?: number; + exactMatch?: boolean; + noHistory?: boolean; +} + export class Station extends TypedEmitter { readonly id: string; @@ -662,10 +669,12 @@ export class Station extends TypedEmitter { return this.#library.findTracksByComment(key, value, limit); } - async search(q: SearchQuery, limit?: number, exactMatch?: boolean) { + async search({ q, limit, exactMatch, noHistory }: StationSearchOptions) { const result = await this.#library.search(q, limit, exactMatch); - this.#musicDb.searchHistory.add(this.id, { ...q, resultCount: result.length }); + if (!noHistory) { + this.#musicDb.searchHistory.add(this.id, { ...q, resultCount: result.length }); + } return result as StationTrack[]; } diff --git a/packages/radio/src/discord/automaton/guild-state.ts b/packages/radio/src/discord/automaton/guild-state.ts index cf894e9a..9d828e80 100644 --- a/packages/radio/src/discord/automaton/guild-state.ts +++ b/packages/radio/src/discord/automaton/guild-state.ts @@ -551,9 +551,13 @@ export class GuildState { if (tracks.length < 1) { const searchResult = await station.search({ - title: info.title, - artist: info.artist - }, 1); + q: { + title: info.title, + artist: info.artist + }, + limit: 1, + noHistory: true + }); tracks.push(...searchResult); } @@ -610,7 +614,10 @@ export class GuildState { } const exactMatches = await station.findTracksByComment(searchKey, id); - const searchResult = await station.search({ artist: info.artist }, undefined, true); + const searchResult = await station.search({ + q: { artist: info.artist }, + exactMatch: true + }); const tracks = uniqBy( [...exactMatches, ...searchResult], diff --git a/packages/radio/src/discord/command/commands/request/main.ts b/packages/radio/src/discord/command/commands/request/main.ts index f076e309..a44d2a90 100644 --- a/packages/radio/src/discord/command/commands/request/main.ts +++ b/packages/radio/src/discord/command/commands/request/main.ts @@ -172,15 +172,15 @@ export const handleRequestCommand = async ({ automaton, interaction, artist, tit await interaction.deferReply(); - const results = await station.search( - { + const results = await station.search({ + q: { artist, title, query }, // 10 pages - maxSelectMenuOptions * 10 - ); + limit: maxSelectMenuOptions * 10 + }); if (results.length < 1) { const highlight = (n: string, v: string) => ansi`({{yellow}}${n} {{cyan}}~ {{pink}}{{bgDarkBlue|b}}${v}{{reset}})` From e08ba2129708aaf3b978563f610f9bd23ac05436 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 13 May 2024 19:00:56 +0700 Subject: [PATCH 04/76] feat(core): add the new trackSkipped event --- packages/core/src/station/station.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/core/src/station/station.ts b/packages/core/src/station/station.ts index 3dffdf26..b0748448 100644 --- a/packages/core/src/station/station.ts +++ b/packages/core/src/station/station.ts @@ -120,6 +120,7 @@ export type StationEvents = { trackStarted: (deck: DeckIndex, trackPlay: StationTrackPlay, lastTrackPlay?: StationTrackPlay) => void; trackActive: (deck: DeckIndex, trackPlay: StationTrackPlay) => void; trackFinished: (deck: DeckIndex, trackPlay: StationTrackPlay) => void; + trackSkipped: (trackPlay: StationTrackPlay) => void; collectionChange: (oldCollection: StationTrackCollection | undefined, newCollection: StationTrackCollection, transitingFromRequestTrack: boolean) => void; crateChange: (oldCrate: StationCrate | undefined, newCrate: StationCrate) => void; sequenceProfileChange: (oldProfile: StationProfile | undefined, newProfile: StationProfile) => void; @@ -487,6 +488,10 @@ export class Station extends TypedEmitter { if (ok) { this.playState = PlayState.Playing; this.#logger.info('Skipping track: %s', this.trackPlay?.track?.path); + + if (this.trackPlay) { + this.emit('trackSkipped', this.trackPlay); + } } return ok; From 2f8483f2821a85b07c316af6de7f54fc4c474e1e Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 13 May 2024 19:01:46 +0700 Subject: [PATCH 05/76] fix(radio): properly show the skipped track --- .../radio/src/discord/automaton/automaton.ts | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/radio/src/discord/automaton/automaton.ts b/packages/radio/src/discord/automaton/automaton.ts index 58229ae0..3f305aa1 100644 --- a/packages/radio/src/discord/automaton/automaton.ts +++ b/packages/radio/src/discord/automaton/automaton.ts @@ -206,6 +206,7 @@ export class MedleyAutomaton extends TypedEmitter { station.on('trackStarted', this.#handleTrackStarted(station)); station.on('trackActive', this.#handleTrackActive(station)); station.on('trackFinished', this.#handleTrackFinished(station)); + station.on('trackSkipped', this.#handleTrackSkipped(station)) station.on('collectionChange', this.#handleCollectionChange(station)); } @@ -571,6 +572,26 @@ export class MedleyAutomaton extends TypedEmitter { }); } + #handleTrackSkipped = (station: Station): StationEvents['trackSkipped'] => (trackPlay) => { + this.updateTrackMessage(async (msg) => { + if (msg.station !== station) { + return; + } + + if (trackPlay.uuid !== msg.trackPlay.uuid) { + return; + } + + return { + title: 'Skipped', + status: TrackMessageStatus.Skipped, + showSkip: false, + showMore: false, + showLyrics: false + } + }); + } + #handleCollectionChange = (station: Station): StationEvents['collectionChange'] => (oldCollection, newCollection) => { // Hide "more like this" button for this currently playing track this.updateTrackMessage(async (msg) => { @@ -844,25 +865,6 @@ export class MedleyAutomaton extends TypedEmitter { return false; } - this.updateTrackMessage(async (msg) => { - if (msg.station !== station) { - return; - } - - if (trackPlay.uuid !== msg.trackPlay.uuid) { - return; - } - - // FIXME: This only works when skip by the automaton itself, perhaps using event might help - return { - title: 'Skipped', - status: TrackMessageStatus.Skipped, - showSkip: false, - showMore: false, - showLyrics: false - } - }); - return true; } From 0d200c6f5fe8a47fd4eb9faf8235cb99259e5475 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 15 May 2024 01:02:52 +0700 Subject: [PATCH 06/76] fix(core): where station rescanning did not scan sweeper/jingle collections --- packages/core/src/playout/boombox.ts | 4 ++-- packages/core/src/station/station.ts | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/core/src/playout/boombox.ts b/packages/core/src/playout/boombox.ts index c7ec76ef..1352a74b 100644 --- a/packages/core/src/playout/boombox.ts +++ b/packages/core/src/playout/boombox.ts @@ -46,7 +46,7 @@ export type BoomBoxTrackExtra = TrackExtra & { export type BoomBoxTrack = Track; export type BoomBoxTrackPlay = TrackPlay; export type BoomBoxCrate = Crate; -export type BoomBoxTrackCollection = TrackCollection; +export type BoomBoxTrackCollection = TrackCollection; export type Requester = any; @@ -169,7 +169,7 @@ export class BoomBox; - readonly #profileBook = new CrateProfileBook(); + readonly #profileBook = new CrateProfileBook

(); artistHistory: Array = []; diff --git a/packages/core/src/station/station.ts b/packages/core/src/station/station.ts index b0748448..1cc4e24f 100644 --- a/packages/core/src/station/station.ts +++ b/packages/core/src/station/station.ts @@ -15,7 +15,7 @@ import { import { createLogger, Logger } from "@seamless-medley/logging"; -import { TrackCollectionBasicOptions, TrackIndex } from "../collections"; +import { TrackCollectionBasicOptions, TrackIndex, WatchTrackCollection } from "../collections"; import { Crate, LatchOptions, LatchSession } from "../crate"; import { Library, MusicCollectionDescriptor, MusicDb, MusicLibrary, MusicTrack, MusicTrackCollection } from "../library"; import { @@ -880,7 +880,19 @@ export class Station extends TypedEmitter { return this.#boombox.allLatches; } - async rescan(full?: boolean, scanningCb?: (collection: MusicTrackCollection) => any) { + async rescan(full?: boolean, scanningCb?: (collection: BoomBoxTrackCollection) => any) { + const jingleCollections = this.profiles + .flatMap(profile => ([ + profile.intros, + profile.requestSweepers, + ...profile.sweeperRules.map(r => r.collection), + ])) + .filter((c): c is WatchTrackCollection => c instanceof WatchTrackCollection) + + for (const col of jingleCollections) { + col.rescan(full); + } + return this.#library.rescan(full, scanningCb); } } From 559e10b53a7703a13dc5dae00d783e02f038085c Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 15 May 2024 02:09:49 +0700 Subject: [PATCH 07/76] fix(radio): track suggestion now suggestion track from non-auxiliary collection first --- packages/radio/src/discord/automaton/guild-state.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/radio/src/discord/automaton/guild-state.ts b/packages/radio/src/discord/automaton/guild-state.ts index 9d828e80..216f32b7 100644 --- a/packages/radio/src/discord/automaton/guild-state.ts +++ b/packages/radio/src/discord/automaton/guild-state.ts @@ -1,4 +1,4 @@ -import { chain, noop, uniqBy } from "lodash"; +import { noop, sortBy, uniqBy } from "lodash"; import { AudienceGroupId, @@ -547,7 +547,7 @@ export class GuildState { embed.setThumbnail(info.image); } - const tracks = await station.findTracksByComment(searchKey, id, 1); + const tracks = await station.findTracksByComment(searchKey, id); if (tracks.length < 1) { const searchResult = await station.search({ @@ -562,7 +562,7 @@ export class GuildState { tracks.push(...searchResult); } - const [track] = tracks; + const [track] = sortBy(tracks, t => t.collection.options.auxiliary ? 1 : 0); if (track) { const { id: trackId, extra } = track; From e340885aff73f5ebc7a2184a3a2959d3268e5ca6 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 15 May 2024 02:11:34 +0700 Subject: [PATCH 08/76] perf(radio): reduce message updates while re-scanning --- packages/radio/src/discord/command/commands/rescan.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/radio/src/discord/command/commands/rescan.ts b/packages/radio/src/discord/command/commands/rescan.ts index 1cc7872a..7efdd6b8 100644 --- a/packages/radio/src/discord/command/commands/rescan.ts +++ b/packages/radio/src/discord/command/commands/rescan.ts @@ -2,6 +2,7 @@ import { CommandInteraction, blockQuote, inlineCode, unorderedList } from "disco import { CommandDescriptor, InteractionHandlerFactory, OptionType, SubCommandLikeOption } from "../type"; import { deny, guildStationGuard, joinStrings, reply } from "../utils"; import { LibraryRescanStats, MusicTrackCollection, Station } from "@seamless-medley/core"; +import { once } from "lodash"; const declaration: SubCommandLikeOption = { type: OptionType.SubCommand, @@ -23,12 +24,12 @@ const createCommandHandler: InteractionHandlerFactory = (aut const { station } = guildStationGuard(automaton, interaction); - const stats = await station.rescan(true, (col) => { + const stats = await station.rescan(true, once(() => { reply(interaction, { ephemeral: true, - content: `Re-scanning: ${col.extra.description}` + content: `Re-scanning...` }); - }) + })); type Stat = Omit, 'collection'>; From b34323461ca81511fb8913e06de06988e77db6ae Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 15 May 2024 02:13:11 +0700 Subject: [PATCH 09/76] fix(radio): where station audiences were mistakenly reset --- packages/core/src/station/station.ts | 2 +- .../src/discord/automaton/guild-state.ts | 78 +++++++++++++------ 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/packages/core/src/station/station.ts b/packages/core/src/station/station.ts index 1cc4e24f..07a95119 100644 --- a/packages/core/src/station/station.ts +++ b/packages/core/src/station/station.ts @@ -78,7 +78,7 @@ export enum AudienceType { export type AudienceOfGroup = `${A}$${Join}`; -export type DiscordAudienceGroupId = AudienceOfGroup; +export type DiscordAudienceGroupId = AudienceOfGroup; export type AudienceGroupId = DiscordAudienceGroupId | AudienceOfGroup, [string]>; diff --git a/packages/radio/src/discord/automaton/guild-state.ts b/packages/radio/src/discord/automaton/guild-state.ts index 216f32b7..d98cc6fe 100644 --- a/packages/radio/src/discord/automaton/guild-state.ts +++ b/packages/radio/src/discord/automaton/guild-state.ts @@ -39,7 +39,7 @@ import { GuildSpecificConfig, MedleyAutomaton } from "./automaton"; import { TrackMessageCreator } from "../trackmessage/creator/base"; import { makeCreator } from "../trackmessage/creator"; import { createCoverImageAttachment } from "../helpers/message"; -import { extractSpotifyMetadata, extractSpotifyUrl, fetchSpotifyInfo, formatSpotifyField, spotifyURI } from "../helpers/spotify"; +import { extractSpotifyUrl, fetchSpotifyInfo, formatSpotifyField } from "../helpers/spotify"; export type GuildStateAdapter = { getAutomaton(): MedleyAutomaton; @@ -132,7 +132,13 @@ export class GuildState { this.#voiceConnector = undefined; } - #updateChannelAudiences(channel: VoiceBasedChannel | null | undefined, muted: boolean) { + /** + * Just joined a voice channel, audiences for this guild should be reset to this channel's members + */ + #joinedVoiceChannel(channel: VoiceBasedChannel | null | undefined, muted: boolean) { + this.#voiceChannelId = channel?.id; + this.#serverMuted = muted; + const station = this.tunedStation; if (!station) { @@ -143,16 +149,15 @@ export class GuildState { if (muted || !channel) { station.removeAudiencesForGroup(audienceGroup); - } else { - updateStationAudiences(station, audienceGroup, channel); + return; } - } - #joinedVoiceChannel(channel: VoiceBasedChannel | null | undefined, muted: boolean) { - this.#voiceChannelId = channel?.id; - this.#serverMuted = muted; - - this.#updateChannelAudiences(channel, muted); + station.updateAudiences( + audienceGroup, + channel.members + .filter(member => !member.user.bot && !member.voice.deaf) + .map(member => member.id) + ); } #leftVoiceChannel() { @@ -352,10 +357,11 @@ export class GuildState { const channel = this.adapter.getChannel(this.voiceChannelId); if (channel?.type === ChannelType.GuildVoice) { - updateStationAudiences( - this.preferredStation, + this.preferredStation.updateAudiences( this.adapter.makeAudienceGroup(this.guildId), - channel + channel.members + .filter(member => !member.user.bot && !member.voice.deaf) + .map(member => member.id) ); } } @@ -409,7 +415,42 @@ export class GuildState { // Stationary if (oldState.serverMute !== newState.serverMute) { this.#serverMuted = newState.serverMute === true; - this.#updateChannelAudiences(newState.channel, this.#serverMuted); + this.#serverMuteStateChanged(newState.channel); + } + } + + #serverMuteStateChanged(channel: VoiceBasedChannel | null) { + const station = this.tunedStation; + + if (!station) { + return; + } + + const audienceGroup = this.adapter.makeAudienceGroup(this.guildId); + + if (this.#serverMuted || !channel) { + station.removeAudiencesForGroup(audienceGroup); + return; + } + + const updateAudience = !this.#serverMuted + ? ((member: GuildMember) => { + if (!member.user.system && !member.user.bot && !member.voice.deaf) { + station.addAudience(audienceGroup, member.id); + } + }) + : (mmeber: GuildMember) => station.removeAudience(audienceGroup, mmeber.id) + + channel.members.forEach(updateAudience); + + // Remove invalid audiences, any audience that does not belong to this channel + const audiences = station.getAudiences(audienceGroup); + if (audiences) { + for (const memberId of audiences) { + if (!channel.members.has(memberId)) { + station.removeAudience(audienceGroup, memberId); + } + } } } @@ -675,15 +716,6 @@ export type StationLink = { exciter: DiscordAudioPlayer; } -export function updateStationAudiences(station: Station, groupId: AudienceGroupId, channel: VoiceBasedChannel) { - station.updateAudiences( - groupId, - channel.members - .filter(member => !member.user.bot && !member.voice.deaf) - .map(member => member.id) - ); -} - interface VoiceStateWithMember extends VoiceState { get member(): GuildMember; } From 612ec346203b28ad2f7117aebaefc135a109c7e7 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 20 May 2024 21:22:13 +0700 Subject: [PATCH 10/76] chore(node-medley): update MACOSX_DEPLOYMENT_TARGET to 12.0 --- packages/node-medley/binding.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-medley/binding.gyp b/packages/node-medley/binding.gyp index cc4ee637..ebb3008e 100644 --- a/packages/node-medley/binding.gyp +++ b/packages/node-medley/binding.gyp @@ -175,7 +175,7 @@ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'GCC_ENABLE_CPP_RTTI': 'YES', 'CLANG_CXX_LANGUAGE_STANDARD': 'c++17', - 'MACOSX_DEPLOYMENT_TARGET': '10.9' + 'MACOSX_DEPLOYMENT_TARGET': '12.0' }, 'configurations': { 'Debug': { From bb2cdb67f1817084b5e44f0f2c17693ab268fd81 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 20 May 2024 21:22:47 +0700 Subject: [PATCH 11/76] chore(node-medley): target NodeJS 18 --- packages/node-medley/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-medley/package.json b/packages/node-medley/package.json index a1b52196..a152b807 100644 --- a/packages/node-medley/package.json +++ b/packages/node-medley/package.json @@ -24,7 +24,7 @@ "scripts": { "clean": "rimraf build out dist", "build": "tsc", - "prebuild": "cross-env JOBS=max prebuildify -t 16.0.0 --tag-libc --napi --strip", + "prebuild": "cross-env JOBS=max prebuildify -t 18.0.0 --tag-libc --napi --strip", "prebuild:linux": "docker build -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp", "package": "tsx scripts/package.ts", "bump-version": "tsx scripts/bump.ts", From 7cbec8df6687e971143b8bdd422b9bd1c493c3d7 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 20 May 2024 21:23:25 +0700 Subject: [PATCH 12/76] chore(node-medley): update GH workflow --- .github/workflows/node-medley.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 55e3ce90..6fbefd46 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -3,6 +3,7 @@ on: push: branches: - main + - feature/node-medley-arm64 paths: - .github/workflows/node-medley.yml - packages/engine/src/** From 2c8c6c39d9864ca8e7b33e74e02e16cc832752ee Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 20 May 2024 22:25:08 +0700 Subject: [PATCH 13/76] chore(engine): update JUCE to 6.1.3 [skip actions] --- .../msvc/medley-playground/medley-playground.vcxproj | 8 ++++---- packages/engine/src/MiniMP3AudioFormatReader.cpp | 8 +++++++- packages/juce | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/engine/msvc/medley-playground/medley-playground.vcxproj b/packages/engine/msvc/medley-playground/medley-playground.vcxproj index cb3c92d6..17c47801 100644 --- a/packages/engine/msvc/medley-playground/medley-playground.vcxproj +++ b/packages/engine/msvc/medley-playground/medley-playground.vcxproj @@ -103,7 +103,7 @@ Level3 true - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;JUCE_STRING_UTF_TYPE=16;TAGLIB_STATIC;DEBUG;_DEBUG;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JUCE_DISPLAY_SPLASH_SCREEN=1;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_USE_MP3AUDIOFORMAT=1;_UNICODE;UNICODE + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;JUCE_STRING_UTF_TYPE=16;JUCE_MODAL_LOOPS_PERMITTED;TAGLIB_STATIC;DEBUG;_DEBUG;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JUCE_DISPLAY_SPLASH_SCREEN=1;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_USE_MP3AUDIOFORMAT=1;_UNICODE;UNICODE false stdcpp17 true @@ -123,7 +123,7 @@ true true true - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;JUCE_STRING_UTF_TYPE=16;TAGLIB_STATIC;NDEBUG;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JUCE_DISPLAY_SPLASH_SCREEN=1;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_USE_MP3AUDIOFORMAT=1;_UNICODE;UNICODE + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;JUCE_STRING_UTF_TYPE=16;JUCE_MODAL_LOOPS_PERMITTED;TAGLIB_STATIC;NDEBUG;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JUCE_DISPLAY_SPLASH_SCREEN=1;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_USE_MP3AUDIOFORMAT=1;_UNICODE;UNICODE true stdcpp17 true @@ -142,7 +142,7 @@ Level3 true - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;JUCE_STRING_UTF_TYPE=16;TAGLIB_STATIC;DEBUG;_DEBUG;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JUCE_DISPLAY_SPLASH_SCREEN=1;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_USE_MP3AUDIOFORMAT=1;_UNICODE;UNICODE + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;JUCE_STRING_UTF_TYPE=16;JUCE_MODAL_LOOPS_PERMITTED;TAGLIB_STATIC;DEBUG;_DEBUG;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JUCE_DISPLAY_SPLASH_SCREEN=1;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_USE_MP3AUDIOFORMAT=1;_UNICODE;UNICODE false stdcpp17 true @@ -164,7 +164,7 @@ true true true - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;JUCE_STRING_UTF_TYPE=16;TAGLIB_STATIC;NDEBUG;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JUCE_DISPLAY_SPLASH_SCREEN=1;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_USE_MP3AUDIOFORMAT=1;_UNICODE;UNICODE + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;JUCE_STRING_UTF_TYPE=16;JUCE_MODAL_LOOPS_PERMITTED;TAGLIB_STATIC;NDEBUG;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JUCE_DISPLAY_SPLASH_SCREEN=1;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_USE_MP3AUDIOFORMAT=1;_UNICODE;UNICODE true stdcpp17 true diff --git a/packages/engine/src/MiniMP3AudioFormatReader.cpp b/packages/engine/src/MiniMP3AudioFormatReader.cpp index 6286b7fd..19815c84 100644 --- a/packages/engine/src/MiniMP3AudioFormatReader.cpp +++ b/packages/engine/src/MiniMP3AudioFormatReader.cpp @@ -60,7 +60,13 @@ bool MiniMP3AudioFormatReader::readSamples(int** destSamples, int numDestChannel auto dst = (float**)destSamples; if (framesRead > 0) { - AudioDataConverters::deinterleaveSamples(buffer, dst, framesRead, numChannels); + using Format = AudioData::Format; + + AudioData::deinterleaveSamples( + AudioData::InterleavedSource { buffer.getData(), (int) numChannels }, + AudioData::NonInterleavedDest { dst, (int)numChannels }, + framesRead + ); } if (framesRead < (unsigned int)numFrames) { diff --git a/packages/juce b/packages/juce index 545e9f35..0bac0e78 160000 --- a/packages/juce +++ b/packages/juce @@ -1 +1 @@ -Subproject commit 545e9f353a6a336c5d1616796024b30d4bbed04b +Subproject commit 0bac0e78c815d9b6328e238fefc2bb151a5163ec From d9fb3d7a915804dddb6dd61a59713ec101db4db8 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 20 May 2024 22:41:22 +0700 Subject: [PATCH 14/76] fix(engine): compiles on Linux [skip actions] --- packages/engine/juce/include_juce_gui_basics.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/engine/juce/include_juce_gui_basics.cpp b/packages/engine/juce/include_juce_gui_basics.cpp index 263bcb67..7836cffa 100644 --- a/packages/engine/juce/include_juce_gui_basics.cpp +++ b/packages/engine/juce/include_juce_gui_basics.cpp @@ -1 +1,2 @@ +#include #include From 39d6f7bfb4e64c782f833c74ea5097d9fa7bdc2d Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 20 May 2024 22:58:19 +0700 Subject: [PATCH 15/76] Revert "chore(node-medley): update MACOSX_DEPLOYMENT_TARGET to 12.0" This reverts commit 612ec346203b28ad2f7117aebaefc135a109c7e7. --- packages/node-medley/binding.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-medley/binding.gyp b/packages/node-medley/binding.gyp index ebb3008e..cc4ee637 100644 --- a/packages/node-medley/binding.gyp +++ b/packages/node-medley/binding.gyp @@ -175,7 +175,7 @@ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'GCC_ENABLE_CPP_RTTI': 'YES', 'CLANG_CXX_LANGUAGE_STANDARD': 'c++17', - 'MACOSX_DEPLOYMENT_TARGET': '12.0' + 'MACOSX_DEPLOYMENT_TARGET': '10.9' }, 'configurations': { 'Debug': { From 86a366ed5b2188369b46bc3d0f7b8cf0bedc4b94 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Mon, 20 May 2024 23:40:06 +0700 Subject: [PATCH 16/76] chore(engine): update JUCE to 7.0.0 [skip actions] --- packages/juce | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/juce b/packages/juce index 0bac0e78..2b16c1b9 160000 --- a/packages/juce +++ b/packages/juce @@ -1 +1 @@ -Subproject commit 0bac0e78c815d9b6328e238fefc2bb151a5163ec +Subproject commit 2b16c1b94c90d0db3072f6dc9da481a9484d0435 From b35cf950066c4e807b637414070e9b27c3512e86 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 00:18:08 +0700 Subject: [PATCH 17/76] chore: test gh build with container --- .github/workflows/node-medley.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 6fbefd46..4c460981 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -36,8 +36,15 @@ jobs: node_arch: arm64 build-arch: darwin-arm64 + - runs-on: [self-hosted, macOS, ARM64] + container: ubuntu:20.04 + node_version: 18 + node_arch: arm64 + build-arch: linux-arm64 + name: Build ${{ matrix.build-arch }} runs-on: ${{ matrix.runs-on }} + container: ${{ matrix.container }} env: BUILD_ARCH: ${{ matrix.build-arch }} DEPS_INSTALL: ./packages/node-medley/scripts/install-deps.sh From 1ea1c97d1905bd07c072834642881ccc320fe6da Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 00:36:44 +0700 Subject: [PATCH 18/76] chore: test gh build with container --- .github/workflows/node-medley.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 4c460981..bfbfeb84 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -26,17 +26,17 @@ jobs: node_arch: x64 build-arch: win32-x64 - - runs-on: macos-12 + - runs-on: macos-14 node_version: 18 node_arch: x64 build-arch: darwin-x64 - - runs-on: [self-hosted, macOS, ARM64] + - runs-on: macos-14 node_version: 18 - node_arch: arm64 - build-arch: darwin-arm64 + node_arch: x64 + build-arch: darwin-x64 - - runs-on: [self-hosted, macOS, ARM64] + - runs-on: macos-14 container: ubuntu:20.04 node_version: 18 node_arch: arm64 From 7d0b3778afea876834cf7d073c96137e4944815f Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 00:39:14 +0700 Subject: [PATCH 19/76] chore: test gh build with container --- .github/workflows/node-medley.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index bfbfeb84..16cb7382 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -36,7 +36,7 @@ jobs: node_arch: x64 build-arch: darwin-x64 - - runs-on: macos-14 + - runs-on: [self-hosted, macOS, ARM64, docker] container: ubuntu:20.04 node_version: 18 node_arch: arm64 From e4aaf8c44b4f81b3e12468eb3f04d814d725894e Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 00:42:15 +0700 Subject: [PATCH 20/76] chore: remove self-hosted runner --- .github/workflows/node-medley.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 16cb7382..bd124013 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -36,12 +36,6 @@ jobs: node_arch: x64 build-arch: darwin-x64 - - runs-on: [self-hosted, macOS, ARM64, docker] - container: ubuntu:20.04 - node_version: 18 - node_arch: arm64 - build-arch: linux-arm64 - name: Build ${{ matrix.build-arch }} runs-on: ${{ matrix.runs-on }} container: ${{ matrix.container }} From 65f72a693b20e5398e48802d55f72984e1b9300a Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 00:46:12 +0700 Subject: [PATCH 21/76] chore: typo --- .github/workflows/node-medley.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index bd124013..34dd64ab 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -28,8 +28,8 @@ jobs: - runs-on: macos-14 node_version: 18 - node_arch: x64 - build-arch: darwin-x64 + node_arch: arm64 + build-arch: darwin-arm64 - runs-on: macos-14 node_version: 18 From ff45b12462610b70aafe9d388031912d81feef99 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 00:53:12 +0700 Subject: [PATCH 22/76] chore: clean up [skip actions] --- .github/workflows/node-medley.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 34dd64ab..b93d4532 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -38,7 +38,6 @@ jobs: name: Build ${{ matrix.build-arch }} runs-on: ${{ matrix.runs-on }} - container: ${{ matrix.container }} env: BUILD_ARCH: ${{ matrix.build-arch }} DEPS_INSTALL: ./packages/node-medley/scripts/install-deps.sh From 510de26d595de4fd4b728bc055612015ce7fd7ee Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 00:57:02 +0700 Subject: [PATCH 23/76] chore: use macos-13 for x64 build --- .github/workflows/node-medley.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index b93d4532..58e0e242 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -31,7 +31,8 @@ jobs: node_arch: arm64 build-arch: darwin-arm64 - - runs-on: macos-14 + # macos-14 runner is arm64, use macos-13 instead + - runs-on: macos-13 node_version: 18 node_arch: x64 build-arch: darwin-x64 From 6f978e83cebf2001a0768b6ef8eac1ec5527e70c Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:06:44 +0700 Subject: [PATCH 24/76] chore: test build for linux-arm64 --- .github/workflows/node-medley.yml | 14 +++++++++++++- packages/node-medley/package.json | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 58e0e242..f2246f09 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -21,6 +21,11 @@ jobs: node_arch: x64 build-arch: linux-x64 + - runs-on: ubuntu-20.04 + node_version: 18 + node_arch: arm64 + build-arch: linux-arm64 + - runs-on: windows-latest node_version: 18 node_arch: x64 @@ -111,9 +116,16 @@ jobs: pnpm install --no-frozen-lockfile --ignore-scripts - name: Build native + if: matrix.build-arch != 'linux-arm64' + working-directory: ./packages/node-medley + run: | + pnpm prebuild + + - name: Build native (Linux/ARM64) + if: matrix.build-arch == 'linux-arm64' working-directory: ./packages/node-medley run: | - pnpm run prebuild + pnpm prebuild:linux-arm64 - name: Upload artifacts uses: actions/upload-artifact@v3 diff --git a/packages/node-medley/package.json b/packages/node-medley/package.json index a152b807..22f7ff4f 100644 --- a/packages/node-medley/package.json +++ b/packages/node-medley/package.json @@ -26,6 +26,7 @@ "build": "tsc", "prebuild": "cross-env JOBS=max prebuildify -t 18.0.0 --tag-libc --napi --strip", "prebuild:linux": "docker build -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp", + "prebuild:linux-arm64": "docker build --platform=linux/arm64 -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp", "package": "tsx scripts/package.ts", "bump-version": "tsx scripts/bump.ts", "demo": "cross-env MEDLEY_DEV=1 tsx test/demo.ts", From 4cff54024be3f7e981522bb3fa14103425cf0852 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:12:36 +0700 Subject: [PATCH 25/76] chore: extract linux-arm64 job --- .github/workflows/node-medley.yml | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index f2246f09..17789820 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -21,11 +21,6 @@ jobs: node_arch: x64 build-arch: linux-x64 - - runs-on: ubuntu-20.04 - node_version: 18 - node_arch: arm64 - build-arch: linux-arm64 - - runs-on: windows-latest node_version: 18 node_arch: x64 @@ -36,7 +31,7 @@ jobs: node_arch: arm64 build-arch: darwin-arm64 - # macos-14 runner is arm64, use macos-13 instead + # macos-14 runner is arm64, use macos-13 instead - runs-on: macos-13 node_version: 18 node_arch: x64 @@ -116,21 +111,25 @@ jobs: pnpm install --no-frozen-lockfile --ignore-scripts - name: Build native - if: matrix.build-arch != 'linux-arm64' working-directory: ./packages/node-medley run: | pnpm prebuild - - name: Build native (Linux/ARM64) - if: matrix.build-arch == 'linux-arm64' - working-directory: ./packages/node-medley - run: | - pnpm prebuild:linux-arm64 - - name: Upload artifacts uses: actions/upload-artifact@v3 with: name: ${{ matrix.build-arch }} path: ./packages/node-medley/prebuilds + build-linux-arm64: + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Build native (Linux/ARM64) + working-directory: ./packages/node-medley + run: | + pnpm prebuild:linux-arm64 From d48203b5e0bed3437632dbc0a9dfeb51bb777835 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:14:39 +0700 Subject: [PATCH 26/76] chore: add node/pnpm steps specifically for linux-arm64 --- .github/workflows/node-medley.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 17789820..b33d052c 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -122,14 +122,30 @@ jobs: path: ./packages/node-medley/prebuilds build-linux-arm64: + name: Build Linux/ARM64 runs-on: ubuntu-20.04 steps: + - name: Install Node + uses: actions/setup-node@v3 + with: + node-version: 18 + architecture: x64 + + - uses: pnpm/action-setup@v2 + with: + version: 8.15.5 + + - name: Make sure pnpm fetch Node + shell: bash + run: | + pnpm &>/dev/null || true + - name: Checkout uses: actions/checkout@v3 with: submodules: recursive - - name: Build native (Linux/ARM64) + - name: Build native working-directory: ./packages/node-medley run: | pnpm prebuild:linux-arm64 From d710c4e126f376f39e2245e16c281212666c6bc3 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:16:44 +0700 Subject: [PATCH 27/76] chore: add more steps --- .github/workflows/node-medley.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index b33d052c..d7ca9297 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -145,7 +145,18 @@ jobs: with: submodules: recursive + - name: Prepare + working-directory: ./packages/node-medley + run: | + pnpm install --no-frozen-lockfile --ignore-scripts + - name: Build native working-directory: ./packages/node-medley run: | pnpm prebuild:linux-arm64 + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: linux-arm64 + path: ./packages/node-medley/prebuilds From 328ef52fe33f33a53253b025edd82cbb9a262cd5 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:27:10 +0700 Subject: [PATCH 28/76] chore: use Ubuntu 24.04 for the linux-arm64 build --- .github/workflows/node-medley.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index d7ca9297..a061050f 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -123,7 +123,7 @@ jobs: build-linux-arm64: name: Build Linux/ARM64 - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Install Node uses: actions/setup-node@v3 From 9a605211f06ef238d9381ed95c1f5cc4c7158120 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:31:52 +0700 Subject: [PATCH 29/76] chore: switch from corepack, use npm to install pnpm --- packages/node-medley/builder/linux/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/node-medley/builder/linux/Dockerfile b/packages/node-medley/builder/linux/Dockerfile index 9a88f8af..03adc56b 100644 --- a/packages/node-medley/builder/linux/Dockerfile +++ b/packages/node-medley/builder/linux/Dockerfile @@ -3,7 +3,8 @@ FROM node:18.16.0 -RUN corepack enable && corepack prepare pnpm@8.15.5 +# RUN corepack enable && corepack prepare pnpm@8.15.5 +RUN npm install -g pnpm@8.15.5 RUN apt -y update \ && apt -y install \ From 22e1bb53549fb7bb59865963397777365a0f88a8 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:33:29 +0700 Subject: [PATCH 30/76] chore: re-run --- .github/workflows/node-medley.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index a061050f..8cb5a278 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -122,7 +122,7 @@ jobs: path: ./packages/node-medley/prebuilds build-linux-arm64: - name: Build Linux/ARM64 + name: Build linux-arm64 runs-on: ubuntu-24.04 steps: - name: Install Node From e7a4906de8cd96ca9da6b2e501968e78792df03a Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:37:03 +0700 Subject: [PATCH 31/76] chore: use docker directly --- .github/workflows/node-medley.yml | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 8cb5a278..f3b29e90 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -125,35 +125,35 @@ jobs: name: Build linux-arm64 runs-on: ubuntu-24.04 steps: - - name: Install Node - uses: actions/setup-node@v3 - with: - node-version: 18 - architecture: x64 + # - name: Install Node + # uses: actions/setup-node@v3 + # with: + # node-version: 18 + # architecture: x64 - - uses: pnpm/action-setup@v2 - with: - version: 8.15.5 + # - uses: pnpm/action-setup@v2 + # with: + # version: 8.15.5 - - name: Make sure pnpm fetch Node - shell: bash - run: | - pnpm &>/dev/null || true + # - name: Make sure pnpm fetch Node + # shell: bash + # run: | + # pnpm &>/dev/null || true - name: Checkout uses: actions/checkout@v3 with: submodules: recursive - - name: Prepare - working-directory: ./packages/node-medley - run: | - pnpm install --no-frozen-lockfile --ignore-scripts + # - name: Prepare + # working-directory: ./packages/node-medley + # run: | + # pnpm install --no-frozen-lockfile --ignore-scripts - name: Build native working-directory: ./packages/node-medley run: | - pnpm prebuild:linux-arm64 + docker build --platform=linux/arm64 -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp - name: Upload artifacts uses: actions/upload-artifact@v3 From 0970b991fc18e5dddd83fda18805ea612a5a6f0d Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:40:27 +0700 Subject: [PATCH 32/76] chore: try without using platform --- .github/workflows/node-medley.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index f3b29e90..49007012 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -153,7 +153,7 @@ jobs: - name: Build native working-directory: ./packages/node-medley run: | - docker build --platform=linux/arm64 -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp + docker build -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp - name: Upload artifacts uses: actions/upload-artifact@v3 From d10d9282a184efcfdae6cc4f330fc3024db425c3 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:48:37 +0700 Subject: [PATCH 33/76] chore: add QEMU and buildx --- .github/workflows/node-medley.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 49007012..426bf797 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -150,6 +150,12 @@ jobs: # run: | # pnpm install --no-frozen-lockfile --ignore-scripts + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build native working-directory: ./packages/node-medley run: | From 54e1a5b281e95b9f81688e2ca7a81fb5d5a1859f Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:49:39 +0700 Subject: [PATCH 34/76] chore: add the `--platform` option back --- .github/workflows/node-medley.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 426bf797..ff794ddb 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -159,7 +159,7 @@ jobs: - name: Build native working-directory: ./packages/node-medley run: | - docker build -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp + docker build --platform=linux/arm64 -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp - name: Upload artifacts uses: actions/upload-artifact@v3 From acb2c450dcad977663756d4c4accb64886b6a747 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:51:28 +0700 Subject: [PATCH 35/76] chore: switch back to corepack --- packages/node-medley/builder/linux/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/node-medley/builder/linux/Dockerfile b/packages/node-medley/builder/linux/Dockerfile index 03adc56b..9a88f8af 100644 --- a/packages/node-medley/builder/linux/Dockerfile +++ b/packages/node-medley/builder/linux/Dockerfile @@ -3,8 +3,7 @@ FROM node:18.16.0 -# RUN corepack enable && corepack prepare pnpm@8.15.5 -RUN npm install -g pnpm@8.15.5 +RUN corepack enable && corepack prepare pnpm@8.15.5 RUN apt -y update \ && apt -y install \ From 04ba4c6e2a6fc5fb01bdff1646df6ba393d914a8 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Tue, 21 May 2024 01:51:54 +0700 Subject: [PATCH 36/76] chore: clean up --- .github/workflows/node-medley.yml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index ff794ddb..92ea0ea7 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -125,31 +125,11 @@ jobs: name: Build linux-arm64 runs-on: ubuntu-24.04 steps: - # - name: Install Node - # uses: actions/setup-node@v3 - # with: - # node-version: 18 - # architecture: x64 - - # - uses: pnpm/action-setup@v2 - # with: - # version: 8.15.5 - - # - name: Make sure pnpm fetch Node - # shell: bash - # run: | - # pnpm &>/dev/null || true - - name: Checkout uses: actions/checkout@v3 with: submodules: recursive - # - name: Prepare - # working-directory: ./packages/node-medley - # run: | - # pnpm install --no-frozen-lockfile --ignore-scripts - - name: Set up QEMU uses: docker/setup-qemu-action@v3 From 1c4ae3a9877e84b651e14ac19898f40d216687df Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 04:28:19 +0700 Subject: [PATCH 37/76] fix(engine): where audioDeviceIOCallback was totally ignored, in favaor of the new API method audioDeviceIOCallbackWithContext --- packages/engine/src/NullAudioDevice.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/engine/src/NullAudioDevice.cpp b/packages/engine/src/NullAudioDevice.cpp index 2a948f6a..fef39b48 100644 --- a/packages/engine/src/NullAudioDevice.cpp +++ b/packages/engine/src/NullAudioDevice.cpp @@ -129,10 +129,11 @@ void NullAudioDevice::run() const ScopedTryLock sl(startStopLock); if (callback) { - callback->audioDeviceIOCallback( + callback->audioDeviceIOCallbackWithContext( const_cast(inputBuffers), 0, outputBuffers, 2, - 480 + 480, + {} ); } From 9d7fc996d4bfffe54fec6f8c4455855529eec6ee Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 04:29:15 +0700 Subject: [PATCH 38/76] fix(engine): update playground, re-order main component's initalization [skip actions] --- .../engine/msvc/medley-playground/medley-playground.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/engine/msvc/medley-playground/medley-playground.cpp b/packages/engine/msvc/medley-playground/medley-playground.cpp index 6bdd1842..457e3129 100644 --- a/packages/engine/msvc/medley-playground/medley-playground.cpp +++ b/packages/engine/msvc/medley-playground/medley-playground.cpp @@ -1465,6 +1465,10 @@ class MedleyApp : public JUCEApplication { } + Queue queue; + QueueModel model; + medley::Medley medley; + OpenGLContext openGLContext; TextButton btnShuffle; @@ -1525,10 +1529,6 @@ class MedleyApp : public JUCEApplication { AudioThumbnailCache thumbnailCache{ 3 }; std::map> thumbnails; - - Queue queue; - QueueModel model; - medley::Medley medley; }; class MainWindow : public DocumentWindow { From 57d26d69b140c0f35f9be3d1ddc92823cee6ea4e Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 22:08:19 +0700 Subject: [PATCH 39/76] feat(node-medley): add tests --- packages/node-medley/package.json | 22 +++++++++ packages/node-medley/test/_force-exit.mjs | 6 +++ packages/node-medley/test/test.ts | 54 +++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 packages/node-medley/test/_force-exit.mjs create mode 100644 packages/node-medley/test/test.ts diff --git a/packages/node-medley/package.json b/packages/node-medley/package.json index 22f7ff4f..6fbe0175 100644 --- a/packages/node-medley/package.json +++ b/packages/node-medley/package.json @@ -27,6 +27,7 @@ "prebuild": "cross-env JOBS=max prebuildify -t 18.0.0 --tag-libc --napi --strip", "prebuild:linux": "docker build -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp", "prebuild:linux-arm64": "docker build --platform=linux/arm64 -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp", + "test": "ava", "package": "tsx scripts/package.ts", "bump-version": "tsx scripts/bump.ts", "demo": "cross-env MEDLEY_DEV=1 tsx test/demo.ts", @@ -37,10 +38,31 @@ "node-gyp-build": "^4.6.1" }, "devDependencies": { + "@ava/typescript": "^5.0.0", "@types/semver": "^7.5.4", + "ava": "^6.1.3", "node-addon-api": "7.0.0", "node-gyp": "^10.0.1", "prebuildify": "^5.0.1", "semver": "^7.5.4" + }, + "ava": { + "files": [ + "test/test.ts" + ], + "require": ["test/_force-exit.mjs"], + "environmentVariables": { + "NODE_NO_WARNINGS": "1", + "MEDLEY_DEV": "1" + }, + "typescript": { + "rewritePaths": { + "src/": "build/" + }, + "compile": false + }, + "nodeArguments": [ + "--loader=tsx" + ] } } diff --git a/packages/node-medley/test/_force-exit.mjs b/packages/node-medley/test/_force-exit.mjs new file mode 100644 index 00000000..8859b64b --- /dev/null +++ b/packages/node-medley/test/_force-exit.mjs @@ -0,0 +1,6 @@ +import process from 'node:process'; +import { registerCompletionHandler } from 'ava'; + +registerCompletionHandler(() => { + process.exit(); +}); diff --git a/packages/node-medley/test/test.ts b/packages/node-medley/test/test.ts new file mode 100644 index 00000000..1e735ae7 --- /dev/null +++ b/packages/node-medley/test/test.ts @@ -0,0 +1,54 @@ +import { Medley, Queue } from '..'; +import test from 'ava'; + +test('Native module loading', t => { + const info = Medley.getInfo(); + t.truthy(info); + t.is(info.versionString, require('../package.json').version); +}); + +const track = __dirname + '/bensound-dance.mp3'; + +test('Track loading', t => { + t.true(Medley.isTrackLoadable(track)); +}); + +test('Null Audio Device playback', t => { + const queue = new Queue(); + const medley = new Medley(queue); + + t.is(medley.constructor, Medley, `${Medley.name} instance expected`); + + t.true( + medley.setAudioDevice({ type: 'Null', device: 'Null Device' }), + 'Null audio device' + ); + + queue.add(track); + + t.true(medley.play()); + + const sampleRate = 48_000; + const playDuration = 2; + + t.timeout(1000 * (playDuration + 5)); + + return new Promise(async (resolve) => { + let started = false; + let count = 0; + + const { stream, id } = await medley.requestAudioStream({ format: 'Int16LE', sampleRate }); + stream.on('data', (data: Buffer) => { + if (started || data.some(v => v !== 0)) { + started = true; + count += data.length / 2 / 2; + + if (count >= sampleRate * playDuration) { + medley.deleteAudioStream(id); + resolve(); + } + } + }); + }); +}) + From af6c350428937f4dd937dea7431a8204df030fe1 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 22:09:25 +0700 Subject: [PATCH 40/76] feat(node-medley): add test command into Docker build --- packages/node-medley/builder/linux/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/node-medley/builder/linux/Dockerfile b/packages/node-medley/builder/linux/Dockerfile index 9a88f8af..2d041427 100644 --- a/packages/node-medley/builder/linux/Dockerfile +++ b/packages/node-medley/builder/linux/Dockerfile @@ -37,3 +37,5 @@ RUN pnpm install WORKDIR /src/${PKG_MEDLEY} RUN pnpm prebuild +RUN pnpm test + From ae1b4a55a313a801d992290e3fe4890fa5f166e3 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 22:09:45 +0700 Subject: [PATCH 41/76] chore(node-medley): clean up --- packages/node-medley/builder/linux/Dockerfile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/node-medley/builder/linux/Dockerfile b/packages/node-medley/builder/linux/Dockerfile index 2d041427..e85daa99 100644 --- a/packages/node-medley/builder/linux/Dockerfile +++ b/packages/node-medley/builder/linux/Dockerfile @@ -1,5 +1,5 @@ -# This is for bulding linux native module using docker -# Usage: pnpm prebuild:docker +# This is for building linux native module using docker +# Usage: pnpm prebuild:linux[-arm64] FROM node:18.16.0 @@ -18,7 +18,7 @@ ENV PKG_MINIMP3=packages/minimp3 ENV PKG_ENGINE=packages/engine ENV PKG_MEDLEY=packages/node-medley -RUN mkdir -p /src /src${PKG_JUCE} /src/${PKG_MINIMP3} /src/${PKG_ENGINE} /src/${PKG_MEDLEY} +RUN mkdir -p /src /src/${PKG_JUCE} /src/${PKG_MINIMP3} /src/${PKG_ENGINE} /src/${PKG_MEDLEY} COPY ./package.json /src COPY ./pnpm-workspace.yaml /src COPY ./${PKG_JUCE} /src/${PKG_JUCE} @@ -31,11 +31,9 @@ WORKDIR /build-deps RUN sh /src/${PKG_MEDLEY}/scripts/install-deps.sh --no-sudo WORKDIR /src - RUN pnpm install WORKDIR /src/${PKG_MEDLEY} - RUN pnpm prebuild RUN pnpm test From deb6d5aef9b02130238791d905484633735c31cd Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 22:10:22 +0700 Subject: [PATCH 42/76] chore(node-medley): update package script --- packages/node-medley/scripts/package.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/node-medley/scripts/package.ts b/packages/node-medley/scripts/package.ts index cadb5c91..46125e1a 100644 --- a/packages/node-medley/scripts/package.ts +++ b/packages/node-medley/scripts/package.ts @@ -9,7 +9,7 @@ async function main() { }; const prebuildStatus = await Promise.all( - ['win32-x64', 'linux-x64', 'darwin-x64', 'darwin-arm64'] + ['win32-x64', 'linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64'] .map(async p => [p, await exists(`prebuilds/${p}`).catch(stubFalse)] as [string, boolean]) ); @@ -42,6 +42,7 @@ async function main() { delete p.gypfile; delete (p as any).devDependencies; + delete (p as any).ava; await outputJson('dist/package.json', p, { spaces: 2 }); } From 32c28ab3318b604dd2881a853407ede76ed22a86 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 22:11:30 +0700 Subject: [PATCH 43/76] feat(node-medley): add test command into GH action job --- .github/workflows/node-medley.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 92ea0ea7..007774e4 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -115,6 +115,11 @@ jobs: run: | pnpm prebuild + - name: Test prebuilt module + working-directory: ./packages/node-medley + run: | + pnpm test + - name: Upload artifacts uses: actions/upload-artifact@v3 with: From de2e35399b20d0a528bd43bd5f1892e750c4ca89 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 22:24:16 +0700 Subject: [PATCH 44/76] chore(noed-medley): temp remove darwin-x64 build --- .github/workflows/node-medley.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 007774e4..59a17f34 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -31,11 +31,11 @@ jobs: node_arch: arm64 build-arch: darwin-arm64 - # macos-14 runner is arm64, use macos-13 instead - - runs-on: macos-13 - node_version: 18 - node_arch: x64 - build-arch: darwin-x64 + # # macos-14 runner is arm64, use macos-13 instead + # - runs-on: macos-13 + # node_version: 18 + # node_arch: x64 + # build-arch: darwin-x64 name: Build ${{ matrix.build-arch }} runs-on: ${{ matrix.runs-on }} From 046fdd5b05633f37bac6199236e544b80a251e80 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 22:49:12 +0700 Subject: [PATCH 45/76] fix(node-medley): add CoreAudioKit for the macOS/Darwin target --- .github/workflows/node-medley.yml | 10 +++++----- packages/node-medley/binding.gyp | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 59a17f34..007774e4 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -31,11 +31,11 @@ jobs: node_arch: arm64 build-arch: darwin-arm64 - # # macos-14 runner is arm64, use macos-13 instead - # - runs-on: macos-13 - # node_version: 18 - # node_arch: x64 - # build-arch: darwin-x64 + # macos-14 runner is arm64, use macos-13 instead + - runs-on: macos-13 + node_version: 18 + node_arch: x64 + build-arch: darwin-x64 name: Build ${{ matrix.build-arch }} runs-on: ${{ matrix.runs-on }} diff --git a/packages/node-medley/binding.gyp b/packages/node-medley/binding.gyp index cc4ee637..d3c72c43 100644 --- a/packages/node-medley/binding.gyp +++ b/packages/node-medley/binding.gyp @@ -167,6 +167,7 @@ '-framework AppKit', '-framework CoreGraphics', '-framework CoreAudio', + '-framework CoreAudioKit', '-framework CoreMidi', '-framework WebKit' ] From 72afd4a534f08f544fe2db33381711d04e9b6a52 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 23:02:13 +0700 Subject: [PATCH 46/76] chore(node-medley): add DiscRecording framework --- packages/node-medley/binding.gyp | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/node-medley/binding.gyp b/packages/node-medley/binding.gyp index d3c72c43..770b840a 100644 --- a/packages/node-medley/binding.gyp +++ b/packages/node-medley/binding.gyp @@ -169,6 +169,7 @@ '-framework CoreAudio', '-framework CoreAudioKit', '-framework CoreMidi', + '-framework DiscRecording', '-framework WebKit' ] }, From 389ebf564c57e2234739b3f0e0f46769e891dcd9 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 23:13:25 +0700 Subject: [PATCH 47/76] chore(node-medley): remove juce_audio_utils --- packages/node-medley/binding.gyp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/node-medley/binding.gyp b/packages/node-medley/binding.gyp index 770b840a..2858edf4 100644 --- a/packages/node-medley/binding.gyp +++ b/packages/node-medley/binding.gyp @@ -55,7 +55,6 @@ 'JUCE_MODULE_AVAILABLE_juce_audio_devices=1', 'JUCE_MODULE_AVAILABLE_juce_audio_formats=1', 'JUCE_MODULE_AVAILABLE_juce_audio_processors=1', - 'JUCE_MODULE_AVAILABLE_juce_audio_utils=1', 'JUCE_MODULE_AVAILABLE_juce_core=1', 'JUCE_MODULE_AVAILABLE_juce_data_structures=1', 'JUCE_MODULE_AVAILABLE_juce_dsp=1', @@ -74,7 +73,6 @@ "../engine/juce/include_juce_audio_devices.cpp", "../engine/juce/include_juce_audio_formats.cpp", "../engine/juce/include_juce_audio_processors.cpp", - "../engine/juce/include_juce_audio_utils.cpp", "../engine/juce/include_juce_core.cpp", "../engine/juce/include_juce_data_structures.cpp", "../engine/juce/include_juce_dsp.cpp", @@ -152,7 +150,6 @@ "../engine/juce/include_juce_audio_devices_mac.mm", "../engine/juce/include_juce_audio_formats_mac.mm", "../engine/juce/include_juce_audio_processors_mac.mm", - "../engine/juce/include_juce_audio_utils_mac.mm", "../engine/juce/include_juce_core_mac.mm", "../engine/juce/include_juce_data_structures_mac.mm", "../engine/juce/include_juce_dsp_mac.mm", @@ -167,9 +164,7 @@ '-framework AppKit', '-framework CoreGraphics', '-framework CoreAudio', - '-framework CoreAudioKit', '-framework CoreMidi', - '-framework DiscRecording', '-framework WebKit' ] }, @@ -220,7 +215,6 @@ "../engine/juce/include_juce_audio_devices.cpp", "../engine/juce/include_juce_audio_formats.cpp", "../engine/juce/include_juce_audio_processors.cpp", - "../engine/juce/include_juce_audio_utils.cpp", "../engine/juce/include_juce_core.cpp", "../engine/juce/include_juce_data_structures.cpp", "../engine/juce/include_juce_dsp.cpp", From 0d9f8bb2fa27ea8465640fb3cbe6a3cb6f08a57b Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 23:39:24 +0700 Subject: [PATCH 48/76] chore(node-medley): run demo instead of test --- .github/workflows/node-medley.yml | 2 +- packages/node-medley/test/demo.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 007774e4..e75b05e7 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -118,7 +118,7 @@ jobs: - name: Test prebuilt module working-directory: ./packages/node-medley run: | - pnpm test + pnpm demo 5 - name: Upload artifacts uses: actions/upload-artifact@v3 diff --git a/packages/node-medley/test/demo.ts b/packages/node-medley/test/demo.ts index 18cfea00..4112fa62 100644 --- a/packages/node-medley/test/demo.ts +++ b/packages/node-medley/test/demo.ts @@ -50,6 +50,13 @@ async function main() { }) medley.play(); + + const timeout = +process.argv.slice(2); + if (timeout > 0) { + setTimeout(() => { + process.exit(0); + }, timeout * 1000) + } } main(); From de2dd68c402f9758162a346dc9d9af61bb4696be Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Wed, 22 May 2024 23:54:32 +0700 Subject: [PATCH 49/76] chore(node-medley): add device type and name in the error message during Medley instance initialization --- packages/engine/src/Medley.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index 434ff4c1..eeefb812 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -59,11 +59,11 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) if (auto device = deviceMgr.getCurrentAudioDevice()) { if (!device->isOpen()) { - throw std::runtime_error("Audio device is not open"); + throw std::runtime_error(String::formatted("Audio device is not open, type=%s, name=%s", device->getTypeName(), device->getName()).toStdString()); } if (!device->isPlaying()) { - throw std::runtime_error("Audio device is not playing"); + throw std::runtime_error(String::formatted("Audio device is not playing, type=%s, name=%s", device->getTypeName(), device->getName()).toStdString()); } } From 2f69b742f75e6587b80bd297bb8ad280329ef92a Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 00:01:25 +0700 Subject: [PATCH 50/76] chore(node-medley): add device type and name in the error message during Medley instance initialization --- packages/engine/src/Medley.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index eeefb812..c21a9cc1 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -59,11 +59,11 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) if (auto device = deviceMgr.getCurrentAudioDevice()) { if (!device->isOpen()) { - throw std::runtime_error(String::formatted("Audio device is not open, type=%s, name=%s", device->getTypeName(), device->getName()).toStdString()); + throw std::runtime_error(("Audio device is not open, type=" + device->getTypeName() + ", name=" + device->getName()).toStdString()); } if (!device->isPlaying()) { - throw std::runtime_error(String::formatted("Audio device is not playing, type=%s, name=%s", device->getTypeName(), device->getName()).toStdString()); + throw std::runtime_error(("Audio device is not playing, type=" + device->getTypeName() + ", name=" + device->getName()).toStdString()); } } From f959763be03a7ae0bb46e53944d526c51237f09c Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 00:07:29 +0700 Subject: [PATCH 51/76] chore(node-medley): add DEBUG flag for demo --- packages/node-medley/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-medley/package.json b/packages/node-medley/package.json index 6fbe0175..7594380a 100644 --- a/packages/node-medley/package.json +++ b/packages/node-medley/package.json @@ -30,7 +30,7 @@ "test": "ava", "package": "tsx scripts/package.ts", "bump-version": "tsx scripts/bump.ts", - "demo": "cross-env MEDLEY_DEV=1 tsx test/demo.ts", + "demo": "cross-env DEBUG=1 MEDLEY_DEV=1 tsx test/demo.ts", "demo-electron": "cross-env MEDLEY_DEV=1 ELECTRON_RUN_AS_NODE=1 electron --require ts-node/register test/demo.ts" }, "gypfile": true, From 3f84958a7f7ad248cad39a8a37b91d2be2e7d2b7 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 00:13:16 +0700 Subject: [PATCH 52/76] chore(node-medley): add more debug messages --- packages/engine/src/Medley.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index c21a9cc1..035f390b 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -26,6 +26,7 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) updateFadingFactor(); + logger->debug("Initializing default devices"); auto error = deviceMgr.initialiseWithDefaultDevices(0, 2); deviceMgr.addAudioDeviceType(std::make_unique()); @@ -39,12 +40,14 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) deviceMgr.addChangeListener(&mixer); + logger->debug("Creating decks"); for (int i = 0; i < numDecks; i++) { decks[i] = new Deck(i, "Deck " + String(i), logWriter, formatMgr, loadingThread, readAheadThread); decks[i]->addListener(this); mixer.addInputSource(decks[i], false); } + logger->debug("Starting threads"); loadingThread.startThread(6); readAheadThread.startThread(9); visualizationThread.startThread(); @@ -53,6 +56,7 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) visualizationThread.addTimeSliceClient(&mixer); audioInterceptionThread.addTimeSliceClient(&audioInterceptor); + logger->debug("Setup main output"); mainOut.setSource(&mixer); deviceMgr.addAudioCallback(&mainOut); deviceMgr.addChangeListener(this); From bf7bb7bc892d219968bd4888ecba13b08509797c Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 00:38:42 +0700 Subject: [PATCH 53/76] chore(node-medley): disable playback liveness check during Medley initialization --- packages/engine/src/Medley.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index 035f390b..99d95627 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -66,9 +66,9 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) throw std::runtime_error(("Audio device is not open, type=" + device->getTypeName() + ", name=" + device->getName()).toStdString()); } - if (!device->isPlaying()) { - throw std::runtime_error(("Audio device is not playing, type=" + device->getTypeName() + ", name=" + device->getName()).toStdString()); - } + // if (!device->isPlaying()) { + // throw std::runtime_error(("Audio device is not playing, type=" + device->getTypeName() + ", name=" + device->getName()).toStdString()); + // } } setMaximumFadeOutDuration(3.0); From 6e8d1ace491b601a265e29fe4723b03b5aaa9026 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 00:39:02 +0700 Subject: [PATCH 54/76] chore(node-medley): add more debug log --- packages/engine/src/Medley.cpp | 7 +++++++ packages/node-medley/test/demo.ts | 29 ++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index 99d95627..4a9fc299 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -62,6 +62,13 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) deviceMgr.addChangeListener(this); if (auto device = deviceMgr.getCurrentAudioDevice()) { + auto names = deviceMgr.getCurrentDeviceTypeObject()->getDeviceNames(); + + logger->debug("Audio device names"); + for (const auto& name: names) { + logger->debug(name); + } + if (!device->isOpen()) { throw std::runtime_error(("Audio device is not open, type=" + device->getTypeName() + ", name=" + device->getName()).toStdString()); } diff --git a/packages/node-medley/test/demo.ts b/packages/node-medley/test/demo.ts index 4112fa62..19bcd769 100644 --- a/packages/node-medley/test/demo.ts +++ b/packages/node-medley/test/demo.ts @@ -1,11 +1,26 @@ import { Medley, Queue } from '..'; +function log(name: string, ...args: any[]) { + console.log(`${new Date().toISOString()}> [${name}]:`, ...args) +} + +function nodeLog(...args: any[]) { + log('demo', ...args); +} + async function main() { - console.log(Medley.getInfo()); + nodeLog(Medley.getInfo()); const queue = new Queue(); const medley = new Medley(queue, { logging: true }); + const env = process.env; + const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env); + + if (isCI) { + medley.setAudioDevice({ type: 'Null', device: 'Null Device' }); + } + const r = await medley.requestAudioStream(); r.stream.on('data', (pcmData) => { @@ -30,23 +45,23 @@ async function main() { }); medley.on('loaded', (deck) => { - console.log('Loaded', deck, medley.getDeckMetadata(deck)); + nodeLog('Loaded', deck, medley.getDeckMetadata(deck)); }); medley.on('unloaded', (deck) => { - console.log('Unloaded', deck); + nodeLog('Unloaded', deck); }); medley.on('started', (deck) => { - console.log('Started', deck); + nodeLog('Started', deck); }); medley.on('finished', (deck) => { - console.log('Finished', deck); + nodeLog('Finished', deck); }); - medley.on('log', (level, name, string) => { - console.log(`[${name}]: ${string}`) + medley.on('log', (level, name, s) => { + log(name, s); }) medley.play(); From 216cc30fd68d412f07b2ebaca1838bec9027e42b Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 01:34:45 +0700 Subject: [PATCH 55/76] refactor(engine): use unique_ptr for deviceMgr --- packages/engine/src/Medley.cpp | 29 +++++++++++++++++++---------- packages/engine/src/Medley.h | 16 ++++++++-------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index 4a9fc299..ee1f3a44 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -24,12 +24,19 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) #endif logger = std::make_unique("medley", logWriter); + logger->debug("Initializing device manager"); + deviceMgr = std::make_unique(); + updateFadingFactor(); logger->debug("Initializing default devices"); - auto error = deviceMgr.initialiseWithDefaultDevices(0, 2); + auto error = deviceMgr->initialiseWithDefaultDevices(0, 2); - deviceMgr.addAudioDeviceType(std::make_unique()); + if (error.isNotEmpty()) { + logger->debug("Could not initialize with default devices: " + error); + } + + deviceMgr->addAudioDeviceType(std::make_unique()); if (error.isNotEmpty() || getCurrentAudioDevice() == nullptr) { setCurrentAudioDeviceType("Null"); @@ -38,7 +45,7 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) mixer.updateAudioConfig(); - deviceMgr.addChangeListener(&mixer); + deviceMgr->addChangeListener(&mixer); logger->debug("Creating decks"); for (int i = 0; i < numDecks; i++) { @@ -58,11 +65,11 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) logger->debug("Setup main output"); mainOut.setSource(&mixer); - deviceMgr.addAudioCallback(&mainOut); - deviceMgr.addChangeListener(this); + deviceMgr->addAudioCallback(&mainOut); + deviceMgr->addChangeListener(this); - if (auto device = deviceMgr.getCurrentAudioDevice()) { - auto names = deviceMgr.getCurrentDeviceTypeObject()->getDeviceNames(); + if (auto device = deviceMgr->getCurrentAudioDevice()) { + auto names = deviceMgr->getCurrentDeviceTypeObject()->getDeviceNames(); logger->debug("Audio device names"); for (const auto& name: names) { @@ -93,7 +100,9 @@ Medley::~Medley() { readAheadThread.stopThread(100); visualizationThread.stopThread(100); - deviceMgr.closeAudioDevice(); + deviceMgr->closeAudioDevice(); + + deviceMgr.release(); for (auto deck : decks) { delete deck; @@ -674,9 +683,9 @@ void Medley::doTransition(Deck* deck, double position) { } void Medley::setAudioDeviceByIndex(int index) { - auto config = deviceMgr.getAudioDeviceSetup(); + auto config = deviceMgr->getAudioDeviceSetup(); config.outputDeviceName = getDeviceNames()[index]; - auto error = deviceMgr.setAudioDeviceSetup(config, true); + auto error = deviceMgr->setAudioDeviceSetup(config, true); if (error.isNotEmpty()) { throw std::runtime_error(error.toStdString()); } diff --git a/packages/engine/src/Medley.h b/packages/engine/src/Medley.h index 5c52d586..4ad51f30 100644 --- a/packages/engine/src/Medley.h +++ b/packages/engine/src/Medley.h @@ -53,19 +53,19 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC virtual ~Medley(); inline const auto& getAvailableDeviceTypes() { - return deviceMgr.getAvailableDeviceTypes(); + return deviceMgr->getAvailableDeviceTypes(); } inline void setCurrentAudioDeviceType(AudioIODeviceType& type) { - deviceMgr.setCurrentAudioDeviceType(type.getTypeName(), true); + deviceMgr->setCurrentAudioDeviceType(type.getTypeName(), true); } inline void setCurrentAudioDeviceType(const juce::String& type) { - deviceMgr.setCurrentAudioDeviceType(type, true); + deviceMgr->setCurrentAudioDeviceType(type, true); } inline auto getCurrentAudioDeviceType() const { - return deviceMgr.getCurrentDeviceTypeObject(); + return deviceMgr->getCurrentDeviceTypeObject(); } inline auto getDeviceNames() const { @@ -73,7 +73,7 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC } inline auto getIndexOfCurrentDevice() const { - return getCurrentAudioDeviceType()->getIndexOfDevice(deviceMgr.getCurrentAudioDevice(), false); + return getCurrentAudioDeviceType()->getIndexOfDevice(deviceMgr->getCurrentAudioDevice(), false); } inline auto getDefaultDeviceIndex() const { @@ -84,9 +84,9 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC inline AudioFormatManager& getAudioFormatManager() { return formatMgr; } - inline AudioIODevice* getCurrentAudioDevice() const { return deviceMgr.getCurrentAudioDevice(); } + inline AudioIODevice* getCurrentAudioDevice() const { return deviceMgr->getCurrentAudioDevice(); } - inline AudioDeviceManager::AudioDeviceSetup getAudioDeviceSetup() const { return deviceMgr.getAudioDeviceSetup(); } + inline AudioDeviceManager::AudioDeviceSetup getAudioDeviceSetup() const { return deviceMgr->getAudioDeviceSetup(); } int getOutputLatency(); @@ -292,7 +292,7 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC PostProcessor processor; }; - AudioDeviceManager deviceMgr; + std::unique_ptr deviceMgr; SupportedFormats formatMgr; From 7e3b771b140d198d2715cecf29cf8a8410f616b6 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 01:35:05 +0700 Subject: [PATCH 56/76] chore(node-medley): add more logs --- packages/node-medley/test/demo.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/node-medley/test/demo.ts b/packages/node-medley/test/demo.ts index 19bcd769..d9206186 100644 --- a/packages/node-medley/test/demo.ts +++ b/packages/node-medley/test/demo.ts @@ -11,8 +11,13 @@ function nodeLog(...args: any[]) { async function main() { nodeLog(Medley.getInfo()); + nodeLog('Creating Queue object'); const queue = new Queue(); + nodeLog('Queue object created'); + + nodeLog('Creating Medley instance'); const medley = new Medley(queue, { logging: true }); + nodeLog('Queue object created'); const env = process.env; const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env); From 91ef24783cf3a5dc3c6c0e3d8bdb309b2bf0534f Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 01:50:28 +0700 Subject: [PATCH 57/76] chore(node-medley): typo --- packages/node-medley/test/demo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-medley/test/demo.ts b/packages/node-medley/test/demo.ts index d9206186..d5bfd79d 100644 --- a/packages/node-medley/test/demo.ts +++ b/packages/node-medley/test/demo.ts @@ -17,7 +17,7 @@ async function main() { nodeLog('Creating Medley instance'); const medley = new Medley(queue, { logging: true }); - nodeLog('Queue object created'); + nodeLog('Medley instance created'); const env = process.env; const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env); From 8c419467df7afc17668980d8800df8d28c6174aa Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 01:50:47 +0700 Subject: [PATCH 58/76] chore(node-medley): add debug log --- packages/node-medley/src/core.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/node-medley/src/core.cpp b/packages/node-medley/src/core.cpp index 15fbac8a..b1a6bf23 100644 --- a/packages/node-medley/src/core.cpp +++ b/packages/node-medley/src/core.cpp @@ -284,6 +284,8 @@ void Medley::audioDeviceChanged() { } void Medley::log(medley::LogLevel level, juce::String& name, juce::String& msg) const { + std::cout << "Medley::log(C++): " + msg << std::endl; + threadSafeEmitter.NonBlockingCall([=](Napi::Env env, Napi::Function emitFn) { try { emitFn.Call(self.Value(), { From a61335133384b94ebb342e81e6494c77edc1efe8 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 01:58:28 +0700 Subject: [PATCH 59/76] chore(node-medley): add timestamp to debug log --- packages/node-medley/src/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-medley/src/core.cpp b/packages/node-medley/src/core.cpp index b1a6bf23..9f83b433 100644 --- a/packages/node-medley/src/core.cpp +++ b/packages/node-medley/src/core.cpp @@ -284,7 +284,7 @@ void Medley::audioDeviceChanged() { } void Medley::log(medley::LogLevel level, juce::String& name, juce::String& msg) const { - std::cout << "Medley::log(C++): " + msg << std::endl; + std::cout << "Medley::log(C++): " + juce::Time::getCurrentTime().toISO8601(true) + "> " + msg << std::endl; threadSafeEmitter.NonBlockingCall([=](Napi::Env env, Napi::Function emitFn) { try { From 2b85306280066536f225c8dc3a3d75e7acb1942e Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 02:21:40 +0700 Subject: [PATCH 60/76] Revert "refactor(engine): use unique_ptr for deviceMgr" This reverts commit 216cc30fd68d412f07b2ebaca1838bec9027e42b. --- packages/engine/src/Medley.cpp | 29 ++++++++++------------------- packages/engine/src/Medley.h | 16 ++++++++-------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index ee1f3a44..4a9fc299 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -24,19 +24,12 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) #endif logger = std::make_unique("medley", logWriter); - logger->debug("Initializing device manager"); - deviceMgr = std::make_unique(); - updateFadingFactor(); logger->debug("Initializing default devices"); - auto error = deviceMgr->initialiseWithDefaultDevices(0, 2); + auto error = deviceMgr.initialiseWithDefaultDevices(0, 2); - if (error.isNotEmpty()) { - logger->debug("Could not initialize with default devices: " + error); - } - - deviceMgr->addAudioDeviceType(std::make_unique()); + deviceMgr.addAudioDeviceType(std::make_unique()); if (error.isNotEmpty() || getCurrentAudioDevice() == nullptr) { setCurrentAudioDeviceType("Null"); @@ -45,7 +38,7 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) mixer.updateAudioConfig(); - deviceMgr->addChangeListener(&mixer); + deviceMgr.addChangeListener(&mixer); logger->debug("Creating decks"); for (int i = 0; i < numDecks; i++) { @@ -65,11 +58,11 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) logger->debug("Setup main output"); mainOut.setSource(&mixer); - deviceMgr->addAudioCallback(&mainOut); - deviceMgr->addChangeListener(this); + deviceMgr.addAudioCallback(&mainOut); + deviceMgr.addChangeListener(this); - if (auto device = deviceMgr->getCurrentAudioDevice()) { - auto names = deviceMgr->getCurrentDeviceTypeObject()->getDeviceNames(); + if (auto device = deviceMgr.getCurrentAudioDevice()) { + auto names = deviceMgr.getCurrentDeviceTypeObject()->getDeviceNames(); logger->debug("Audio device names"); for (const auto& name: names) { @@ -100,9 +93,7 @@ Medley::~Medley() { readAheadThread.stopThread(100); visualizationThread.stopThread(100); - deviceMgr->closeAudioDevice(); - - deviceMgr.release(); + deviceMgr.closeAudioDevice(); for (auto deck : decks) { delete deck; @@ -683,9 +674,9 @@ void Medley::doTransition(Deck* deck, double position) { } void Medley::setAudioDeviceByIndex(int index) { - auto config = deviceMgr->getAudioDeviceSetup(); + auto config = deviceMgr.getAudioDeviceSetup(); config.outputDeviceName = getDeviceNames()[index]; - auto error = deviceMgr->setAudioDeviceSetup(config, true); + auto error = deviceMgr.setAudioDeviceSetup(config, true); if (error.isNotEmpty()) { throw std::runtime_error(error.toStdString()); } diff --git a/packages/engine/src/Medley.h b/packages/engine/src/Medley.h index 4ad51f30..5c52d586 100644 --- a/packages/engine/src/Medley.h +++ b/packages/engine/src/Medley.h @@ -53,19 +53,19 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC virtual ~Medley(); inline const auto& getAvailableDeviceTypes() { - return deviceMgr->getAvailableDeviceTypes(); + return deviceMgr.getAvailableDeviceTypes(); } inline void setCurrentAudioDeviceType(AudioIODeviceType& type) { - deviceMgr->setCurrentAudioDeviceType(type.getTypeName(), true); + deviceMgr.setCurrentAudioDeviceType(type.getTypeName(), true); } inline void setCurrentAudioDeviceType(const juce::String& type) { - deviceMgr->setCurrentAudioDeviceType(type, true); + deviceMgr.setCurrentAudioDeviceType(type, true); } inline auto getCurrentAudioDeviceType() const { - return deviceMgr->getCurrentDeviceTypeObject(); + return deviceMgr.getCurrentDeviceTypeObject(); } inline auto getDeviceNames() const { @@ -73,7 +73,7 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC } inline auto getIndexOfCurrentDevice() const { - return getCurrentAudioDeviceType()->getIndexOfDevice(deviceMgr->getCurrentAudioDevice(), false); + return getCurrentAudioDeviceType()->getIndexOfDevice(deviceMgr.getCurrentAudioDevice(), false); } inline auto getDefaultDeviceIndex() const { @@ -84,9 +84,9 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC inline AudioFormatManager& getAudioFormatManager() { return formatMgr; } - inline AudioIODevice* getCurrentAudioDevice() const { return deviceMgr->getCurrentAudioDevice(); } + inline AudioIODevice* getCurrentAudioDevice() const { return deviceMgr.getCurrentAudioDevice(); } - inline AudioDeviceManager::AudioDeviceSetup getAudioDeviceSetup() const { return deviceMgr->getAudioDeviceSetup(); } + inline AudioDeviceManager::AudioDeviceSetup getAudioDeviceSetup() const { return deviceMgr.getAudioDeviceSetup(); } int getOutputLatency(); @@ -292,7 +292,7 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC PostProcessor processor; }; - std::unique_ptr deviceMgr; + AudioDeviceManager deviceMgr; SupportedFormats formatMgr; From 9dd405861b85074ce0d43d8a5e5c66ce5633af89 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 02:25:36 +0700 Subject: [PATCH 61/76] feat(engine): allow skipping device scanning --- .../msvc/medley-playground/medley-playground.cpp | 2 +- packages/engine/src/Medley.cpp | 12 ++++++++---- packages/engine/src/Medley.h | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/engine/msvc/medley-playground/medley-playground.cpp b/packages/engine/msvc/medley-playground/medley-playground.cpp index 457e3129..ec875703 100644 --- a/packages/engine/msvc/medley-playground/medley-playground.cpp +++ b/packages/engine/msvc/medley-playground/medley-playground.cpp @@ -918,7 +918,7 @@ class MedleyApp : public JUCEApplication { MainContentComponent(ILoggerWriter* logWriter) : Component(), model(queue), - medley(queue, logWriter), + medley(queue, logWriter, false), queueListBox(model), btnShuffle("Shuffle"), btnAdd("Add"), diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index 4a9fc299..5065d8ab 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -9,7 +9,7 @@ namespace medley { -Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) +Medley::Medley(IQueue& queue, ILoggerWriter* logWriter, bool skipDeviceScanning) : audioInterceptor(*this), mixer(*this), @@ -26,12 +26,16 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter) updateFadingFactor(); - logger->debug("Initializing default devices"); - auto error = deviceMgr.initialiseWithDefaultDevices(0, 2); + juce::String error; + + if (!skipDeviceScanning) { + logger->debug("Initializing default devices"); + error = deviceMgr.initialiseWithDefaultDevices(0, 2); + } deviceMgr.addAudioDeviceType(std::make_unique()); - if (error.isNotEmpty() || getCurrentAudioDevice() == nullptr) { + if (skipDeviceScanning || error.isNotEmpty() || getCurrentAudioDevice() == nullptr) { setCurrentAudioDeviceType("Null"); setAudioDeviceByIndex(0); } diff --git a/packages/engine/src/Medley.h b/packages/engine/src/Medley.h index 5c52d586..1570ba6d 100644 --- a/packages/engine/src/Medley.h +++ b/packages/engine/src/Medley.h @@ -48,7 +48,7 @@ class Medley : public Deck::Callback, juce::ChangeListener, public KaraokeParamC static constexpr int numDecks = 3; - Medley(IQueue& queue, ILoggerWriter* logWriter); + Medley(IQueue& queue, ILoggerWriter* logWriter, bool skipDeviceScanning); virtual ~Medley(); From cd3805b821e7e26bb96d4add74e1b73f6551c5e4 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 02:39:54 +0700 Subject: [PATCH 62/76] feat(node-medley): add a new `skipDeviceScanning` option --- packages/node-medley/src/core.cpp | 10 +++++++++- packages/node-medley/src/index.d.ts | 1 + packages/node-medley/test/demo.ts | 12 ++++-------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/node-medley/src/core.cpp b/packages/node-medley/src/core.cpp index 9f83b433..957aea92 100644 --- a/packages/node-medley/src/core.cpp +++ b/packages/node-medley/src/core.cpp @@ -129,6 +129,7 @@ Medley::Medley(const CallbackInfo& info) } bool logging = false; + bool skipDeviceScanning = false; auto arg2 = info[1]; if (arg2.IsObject()) { @@ -139,6 +140,13 @@ Medley::Medley(const CallbackInfo& info) logging = l.ToBoolean().Value(); } } + + if (options.Has("skipDeviceScanning")) { + auto l = options.Get("skipDeviceScanning"); + if (l.IsBoolean()) { + skipDeviceScanning = l.ToBoolean().Value(); + } + } } self = Persistent(info.This()); @@ -152,7 +160,7 @@ Medley::Medley(const CallbackInfo& info) ); queue = Queue::Unwrap(queueObj); - engine = new Engine(*queue, logging ? this : nullptr); + engine = new Engine(*queue, logging ? this : nullptr, skipDeviceScanning); engine->addListener(this); engine->setAudioCallback(this); } diff --git a/packages/node-medley/src/index.d.ts b/packages/node-medley/src/index.d.ts index 03df10f4..f8a0f29c 100644 --- a/packages/node-medley/src/index.d.ts +++ b/packages/node-medley/src/index.d.ts @@ -115,6 +115,7 @@ export type LogListener = (level: number, name: string, msg: string) => void; export type MedleyOptions = { logging?: boolean; + skipDeviceScanning?: boolean; } export declare class Medley { diff --git a/packages/node-medley/test/demo.ts b/packages/node-medley/test/demo.ts index d5bfd79d..5c8b3047 100644 --- a/packages/node-medley/test/demo.ts +++ b/packages/node-medley/test/demo.ts @@ -9,6 +9,9 @@ function nodeLog(...args: any[]) { } async function main() { + const env = process.env; + const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env); + nodeLog(Medley.getInfo()); nodeLog('Creating Queue object'); @@ -16,16 +19,9 @@ async function main() { nodeLog('Queue object created'); nodeLog('Creating Medley instance'); - const medley = new Medley(queue, { logging: true }); + const medley = new Medley(queue, { logging: true, skipDeviceScanning: isCI }); nodeLog('Medley instance created'); - const env = process.env; - const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env); - - if (isCI) { - medley.setAudioDevice({ type: 'Null', device: 'Null Device' }); - } - const r = await medley.requestAudioStream(); r.stream.on('data', (pcmData) => { From e16876e8f159b4ed000476a8415abecea9c363cf Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 02:57:28 +0700 Subject: [PATCH 63/76] chore(engine): clean up --- packages/engine/src/Medley.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/packages/engine/src/Medley.cpp b/packages/engine/src/Medley.cpp index 5065d8ab..6c39276d 100644 --- a/packages/engine/src/Medley.cpp +++ b/packages/engine/src/Medley.cpp @@ -29,7 +29,6 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter, bool skipDeviceScanning) juce::String error; if (!skipDeviceScanning) { - logger->debug("Initializing default devices"); error = deviceMgr.initialiseWithDefaultDevices(0, 2); } @@ -44,14 +43,12 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter, bool skipDeviceScanning) deviceMgr.addChangeListener(&mixer); - logger->debug("Creating decks"); for (int i = 0; i < numDecks; i++) { decks[i] = new Deck(i, "Deck " + String(i), logWriter, formatMgr, loadingThread, readAheadThread); decks[i]->addListener(this); mixer.addInputSource(decks[i], false); } - logger->debug("Starting threads"); loadingThread.startThread(6); readAheadThread.startThread(9); visualizationThread.startThread(); @@ -60,26 +57,14 @@ Medley::Medley(IQueue& queue, ILoggerWriter* logWriter, bool skipDeviceScanning) visualizationThread.addTimeSliceClient(&mixer); audioInterceptionThread.addTimeSliceClient(&audioInterceptor); - logger->debug("Setup main output"); mainOut.setSource(&mixer); deviceMgr.addAudioCallback(&mainOut); deviceMgr.addChangeListener(this); if (auto device = deviceMgr.getCurrentAudioDevice()) { - auto names = deviceMgr.getCurrentDeviceTypeObject()->getDeviceNames(); - - logger->debug("Audio device names"); - for (const auto& name: names) { - logger->debug(name); - } - if (!device->isOpen()) { throw std::runtime_error(("Audio device is not open, type=" + device->getTypeName() + ", name=" + device->getName()).toStdString()); } - - // if (!device->isPlaying()) { - // throw std::runtime_error(("Audio device is not playing, type=" + device->getTypeName() + ", name=" + device->getName()).toStdString()); - // } } setMaximumFadeOutDuration(3.0); From 64a3ed251d311165f3ff2742945a34def531a757 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 02:57:42 +0700 Subject: [PATCH 64/76] chore(node-medley): clean up --- packages/node-medley/src/core.cpp | 2 -- packages/node-medley/test/demo.ts | 5 ----- 2 files changed, 7 deletions(-) diff --git a/packages/node-medley/src/core.cpp b/packages/node-medley/src/core.cpp index 957aea92..aa35d92e 100644 --- a/packages/node-medley/src/core.cpp +++ b/packages/node-medley/src/core.cpp @@ -292,8 +292,6 @@ void Medley::audioDeviceChanged() { } void Medley::log(medley::LogLevel level, juce::String& name, juce::String& msg) const { - std::cout << "Medley::log(C++): " + juce::Time::getCurrentTime().toISO8601(true) + "> " + msg << std::endl; - threadSafeEmitter.NonBlockingCall([=](Napi::Env env, Napi::Function emitFn) { try { emitFn.Call(self.Value(), { diff --git a/packages/node-medley/test/demo.ts b/packages/node-medley/test/demo.ts index 5c8b3047..c5972848 100644 --- a/packages/node-medley/test/demo.ts +++ b/packages/node-medley/test/demo.ts @@ -14,13 +14,8 @@ async function main() { nodeLog(Medley.getInfo()); - nodeLog('Creating Queue object'); const queue = new Queue(); - nodeLog('Queue object created'); - - nodeLog('Creating Medley instance'); const medley = new Medley(queue, { logging: true, skipDeviceScanning: isCI }); - nodeLog('Medley instance created'); const r = await medley.requestAudioStream(); From 9f21cccbcbc30bcd9c3db6caf9979e9832c8ebc5 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 02:58:56 +0700 Subject: [PATCH 65/76] chore(node-medley): skip device scanning while running test --- packages/node-medley/test/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-medley/test/test.ts b/packages/node-medley/test/test.ts index 1e735ae7..df5e4f2f 100644 --- a/packages/node-medley/test/test.ts +++ b/packages/node-medley/test/test.ts @@ -15,7 +15,7 @@ test('Track loading', t => { test('Null Audio Device playback', t => { const queue = new Queue(); - const medley = new Medley(queue); + const medley = new Medley(queue, { skipDeviceScanning: true }); t.is(medley.constructor, Medley, `${Medley.name} instance expected`); From 258b18e80f5d0425b874e7c95aaffff16c226a2b Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 02:59:52 +0700 Subject: [PATCH 66/76] chore(node-medley): run test during CI/CD --- .github/workflows/node-medley.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index e75b05e7..007774e4 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -118,7 +118,7 @@ jobs: - name: Test prebuilt module working-directory: ./packages/node-medley run: | - pnpm demo 5 + pnpm test - name: Upload artifacts uses: actions/upload-artifact@v3 From a6567986664dc1f4d1500895b08c6696938e738d Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 03:03:19 +0700 Subject: [PATCH 67/76] chore(node-medley): use self-hosted macOS/ARM64 running to build for Linux/ARM64 --- .github/workflows/node-medley.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 007774e4..6e7c5c97 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -12,6 +12,7 @@ on: - packages/node-medley/binding.gyp - packages/node-medley/package.json jobs: + # This job builds on each platform directly build: strategy: matrix: @@ -126,21 +127,16 @@ jobs: name: ${{ matrix.build-arch }} path: ./packages/node-medley/prebuilds + # This job builds with Docker multi-platform on a self-hosted Apple Silicon machine build-linux-arm64: name: Build linux-arm64 - runs-on: ubuntu-24.04 + runs-on: [self-hosted, macOS, ARM64, docker] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: recursive - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Build native working-directory: ./packages/node-medley run: | From 6d063938c633ab98b8c2047b99b4a61f54698135 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 03:17:38 +0700 Subject: [PATCH 68/76] chore(node-medley): use ephemeral container id --- .github/workflows/node-medley.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 6e7c5c97..77a51cb1 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -139,8 +139,19 @@ jobs: - name: Build native working-directory: ./packages/node-medley + env: + ephemeral_container_id: node-medley-prebuild-${{ github.run_id }}-${{ github.run_attempt }} run: | - docker build --platform=linux/arm64 -f ./builder/linux/Dockerfile --progress=plain -t node-medley-prebuild ../.. && docker container create --name node-medley-prebuild-cp node-medley-prebuild && docker cp node-medley-prebuild-cp:/src/packages/node-medley/prebuilds ./ && docker rm node-medley-prebuild-cp + docker build \ + --platform=linux/arm64 \ + -f ./builder/linux/Dockerfile \ + --progress=plain \ + -t node-medley-prebuild ../.. && \ + docker container create \ + --name ${{ env.ephemeral_container_id }} node-medley-prebuild && \ + docker cp \ + ${{ env.ephemeral_container_id }}:/src/packages/node-medley/prebuilds ./ && \ + docker rm ${{ env.ephemeral_container_id }} - name: Upload artifacts uses: actions/upload-artifact@v3 From 5c4405b0abc62d165bc7239dbe26deb33e2a9d22 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 03:29:20 +0700 Subject: [PATCH 69/76] chore(node-medley): bump version --- packages/node-medley/package.json | 6 ++++-- packages/node-medley/src/version.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/node-medley/package.json b/packages/node-medley/package.json index 7594380a..df266bb4 100644 --- a/packages/node-medley/package.json +++ b/packages/node-medley/package.json @@ -1,7 +1,7 @@ { "name": "@seamless-medley/medley", "description": "Audio engine for Node.js, with built-in \"radio like\" gapless/seamless playback", - "version": "0.4.3", + "version": "0.4.4-juce7.0", "author": { "name": "Wittawas Nakkasem", "email": "vittee@hotmail.com", @@ -50,7 +50,9 @@ "files": [ "test/test.ts" ], - "require": ["test/_force-exit.mjs"], + "require": [ + "test/_force-exit.mjs" + ], "environmentVariables": { "NODE_NO_WARNINGS": "1", "MEDLEY_DEV": "1" diff --git a/packages/node-medley/src/version.h b/packages/node-medley/src/version.h index c6ad8fd2..f63784cf 100644 --- a/packages/node-medley/src/version.h +++ b/packages/node-medley/src/version.h @@ -1,3 +1,4 @@ #define MEDLEY_VERSION_MAJOR 0 #define MEDLEY_VERSION_MINOR 4 -#define MEDLEY_VERSION_PATCH 3 +#define MEDLEY_VERSION_PATCH 4 +#define MEDLEY_VERSION_PRE_RELEASE "juce7.0" From d715d3ea6f3239f908c01d111014fb39f1d3ed8b Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 03:29:47 +0700 Subject: [PATCH 70/76] docs(node-medley): add skipDeviceScanning --- packages/node-medley/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/node-medley/README.md b/packages/node-medley/README.md index 824fe25e..7aa08ce8 100644 --- a/packages/node-medley/README.md +++ b/packages/node-medley/README.md @@ -272,6 +272,7 @@ new Medley(queue, options) #### Options? - `logging` *(boolean?)* - Enable logging, See [*log* event](#log) +- `skipDeviceScanning` *(boolean?)* - Skip scanning for audio devices **Methods** ### `play(shouldFade = true)` From 06d2ee7b8a969e00e708494c10f6ae06f37169d7 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 03:37:28 +0700 Subject: [PATCH 71/76] chore(node-medley): update actions version --- .github/workflows/node-medley.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 77a51cb1..9f58a94c 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -45,12 +45,12 @@ jobs: DEPS_INSTALL: ./packages/node-medley/scripts/install-deps.sh steps: - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node_version }} architecture: ${{ matrix.node_arch }} - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v4 with: version: 8.15.5 @@ -65,7 +65,7 @@ jobs: run: | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} @@ -74,7 +74,7 @@ jobs: ${{ runner.os }}-${{ runner.arch }}-pnpm-store-native-build - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -93,7 +93,7 @@ jobs: - name: Cache vcpkg if: contains(matrix.build-arch, 'win32') - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | C:\vcpkg\packages @@ -122,7 +122,7 @@ jobs: pnpm test - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.build-arch }} path: ./packages/node-medley/prebuilds @@ -133,7 +133,7 @@ jobs: runs-on: [self-hosted, macOS, ARM64, docker] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive From 74b44acd4261fa129fa298b263d614648037fc44 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 03:45:57 +0700 Subject: [PATCH 72/76] chore(node-medley): update version string to match version string in package.json --- packages/node-medley/src/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-medley/src/core.cpp b/packages/node-medley/src/core.cpp index aa35d92e..c134df55 100644 --- a/packages/node-medley/src/core.cpp +++ b/packages/node-medley/src/core.cpp @@ -1079,7 +1079,7 @@ Napi::Value Medley::static_getInfo(const Napi::CallbackInfo& info) { #ifdef MEDLEY_VERSION_PRE_RELEASE version.Set("prerelease", Napi::String::New(env, MEDLEY_VERSION_PRE_RELEASE)); - versionString += juce::String("." MEDLEY_VERSION_PRE_RELEASE); + versionString += juce::String("-" MEDLEY_VERSION_PRE_RELEASE); #endif result.Set("version", version); From 886dc76bdb36a29bdb8b175215178294b3a0717c Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 03:53:08 +0700 Subject: [PATCH 73/76] chore(node-medley): update action version --- .github/workflows/node-medley.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 9f58a94c..57330209 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -154,7 +154,7 @@ jobs: docker rm ${{ env.ephemeral_container_id }} - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux-arm64 path: ./packages/node-medley/prebuilds From 896bf7dd05a7de391f09da83b3bff9bee6135c24 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 21:27:50 +0700 Subject: [PATCH 74/76] chore(node-medley): update actions version --- .github/workflows/publish-node-medley.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-node-medley.yml b/.github/workflows/publish-node-medley.yml index b5ae4ce5..3ec12621 100644 --- a/.github/workflows/publish-node-medley.yml +++ b/.github/workflows/publish-node-medley.yml @@ -15,13 +15,13 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18 architecture: x64 registry-url: https://registry.npmjs.org - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v4 with: version: 8.15.5 @@ -37,7 +37,7 @@ jobs: run: | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 name: Setup pnpm cache # if: ${{ !github.event.act }} with: @@ -47,7 +47,7 @@ jobs: ${{ runner.os }}-${{ runner.arch }}-pnpm-store-native-build - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive From aaa85ef18378f448da9fa5f98efe95c44d1e02ae Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 21:28:41 +0700 Subject: [PATCH 75/76] chore(node-medley): clean up --- .github/workflows/node-medley.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/node-medley.yml b/.github/workflows/node-medley.yml index 57330209..37af8b5c 100644 --- a/.github/workflows/node-medley.yml +++ b/.github/workflows/node-medley.yml @@ -3,7 +3,6 @@ on: push: branches: - main - - feature/node-medley-arm64 paths: - .github/workflows/node-medley.yml - packages/engine/src/** From a2f7dc23bf6ed3783843a78fc2cf278763db00e0 Mon Sep 17 00:00:00 2001 From: Wittawas Nakkasem Date: Thu, 23 May 2024 21:29:20 +0700 Subject: [PATCH 76/76] chore(node-medley): add macos deployment target into install-deps script --- packages/node-medley/scripts/install-deps.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/node-medley/scripts/install-deps.sh b/packages/node-medley/scripts/install-deps.sh index 04d467d7..e556b834 100644 --- a/packages/node-medley/scripts/install-deps.sh +++ b/packages/node-medley/scripts/install-deps.sh @@ -2,6 +2,7 @@ TAGLIB_VERSION=1.13.1 LIBSAMPLERATE_VERSION=0.2.2 +MACOSX_DEPLOYMENT_TARGET=10.9 SUDO=sudo if [ "$1" = "--no-sudo" ]; then @@ -17,6 +18,7 @@ cd taglib \ -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DCMAKE_BUILD_TYPE=Release \ + -DMACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} . \ && make -j 2 \ && ${SUDO} make install @@ -32,7 +34,9 @@ wget https://github.com/libsndfile/libsamplerate/releases/download/0.2.2/libsamp tar xvf libsamplerate.tar.gz mv libsamplerate-* libsamplerate cd libsamplerate \ - && cmake . \ + && cmake \ + -DMACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ + . \ && make -j 2 \ && ${SUDO} make install