@audiotool/nexus - v0.0.17
    Preparing search index...

    Type Alias TransactionBuilder

    A transaction builder can be used to make changes on a document.

    All changes made using the same transaction builder will be part of the same transaction, and as such applied atomically to the backend.

    While a transaction builder exists, the document is locked, and no other builders can be created, to avoid race conditions.

    To finish a transaction, call send, which will unlock the document and let other builders be created. After send is called, all methods of the builder will throw.

    Modifications to the document with a builder are applied to the local document immediately, and only sent to the backend when send is called.

    Note that if receiving a builder through SyncedDocument.modify, then send method is called automatically once the function returns. See Overview for more information.

    type TransactionBuilder = {
        entities: EntityQuery;
        applyPresetTo(
            entity: NexusEntity<DevicePresetEntityType>,
            preset: NexusPreset,
        ): void;
        clone<T extends keyof EntityTypes>(
            entity: NexusEntity<T>,
            args?: DeepPartial<EntityConstructorType<T>>,
        ): NexusEntityUnion<T>;
        cloneLinked(
            ...entities: (NexusEntity<(keyof EntityTypes)> | EntityWithOverwrites)[],
        ): NexusEntity<keyof EntityTypes>[];
        create<T extends keyof EntityTypes>(
            name: T,
            args: EntityConstructorType<T>,
        ): NexusEntityUnion<T>;
        createDeviceFromPreset<T extends DevicePresetEntityType>(
            preset: NexusPreset<T>,
        ): NexusEntityUnion<T>;
        createPresetFor(entity: NexusEntity<DevicePresetEntityType>): Preset;
        insertSample(
            sample: { bpm?: number; durationSeconds: number; name: string },
            options?: InsertSampleOptions,
        ): NexusEntity<"audioRegion">;
        remove(idOrEntity: string | NexusEntity<keyof EntityTypes>): void;
        removeWithDependencies(
            idOrEntity: string | NexusEntity<keyof EntityTypes>,
        ): void;
        send(): void;
        tryUpdate<P extends PrimitiveType>(
            field: PrimitiveField<P, "mut">,
            value: P,
        ): undefined | string;
        update<P extends PrimitiveType>(
            field: PrimitiveField<P, "mut">,
            value: P,
        ): void;
    }
    Index

    Properties

    entities: EntityQuery

    Allows querying all entities of the document.

    Methods

    • Clone a list of entities, in such a way that pointers that are both from to entities in this list are updated to point to the cloned versions. The resulting creates command are ordered in such a way that all pointers are always valid, and no transaction errors occur.

      Each element in the past list can either be an entity itself, or an object

      {
      entity: NexusEntity<T>,
      overwrites?: ConstructorTypes[T]
      }

      where overwrites work the same as the second parameter of t.clone() or t.create().

      Pointers from and to entities not in the list remain unchanged, unless overwritten.

      Returns the cloned version of the entities in order. Robust towards duplicates in the passed entities list.

      Example

      Let's say we have entities a, b, c, d, e, with pointers between each other like this:

      
       a ──► b ──► c ──► d
                   ▲
                   │
                   e
      

      And we call cloneLinked(b, c). Then:

      • the relationship between b and c is updated to duplicates
      • relationships from b or c to other entities remain untouched
      • relationships from other entities to b or c remain untouched

      Leading to a graph like this:

      
             b'──► c' ───┐
                         ▼
       a ──► b ──► c ──► d
                   ▲
                   │
                   e
      

      If overwrite arguments are given for a specific entity, they overwrite any value after the links have been adjusted.

      Parameters

      Returns NexusEntity<keyof EntityTypes>[]

    • Insert a sample into the timeline, creating all required entities; AudioDevice AudioTrack,AutomationCollection, Sample and AudioRegion.

      Parameters

      • sample: { bpm?: number; durationSeconds: number; name: string }

        The sample to insert. Anything with name / durationSeconds / optional bpm is accepted — typically:

      • Optionaloptions: InsertSampleOptions

        Insertion options like where in time, how long, how to loop, which track to attach to, etc.

      Returns NexusEntity<"audioRegion">

      The created AudioRegion entity

      Examples:

      You can use sample objects returned by SamplesAPI.get, SamplesAPI.list, or SamplesAPI.download:

      // fetch some sample
      const sample = await at.samples.get("samples/abc-123")
      if (sample instanceof Error) throw sample

      After the method completes, get the transaction builder to start modifying:

      const t = await nexus.createTransaction()
      

      Create track, region, cable, mixer channel, etc:

      t.insertSample(sample)
      
      const track: NexusEntity<"audioTrack"> = ...
      t.insertSample(sample, { attachTo: track })
      const device: NexusEntity<"audioDevice"> = ...
      t.insertSample(sample, { attachTo: device })

      To make it play in sync with the metronome, you have to tell audiotool how fast your sample should play relative to the metronome. You can do this by passing in either the sample's own BPM, or by the sample's duration in music time (bars).

      t.insertSample(sample, {
      sample: { bpm: 120 }
      })
      t.insertSample(sample, {
      sample: { musicDurationTicks: Ticks.Bars(3) }
      })

      Position at bar 1, duration 3 bars, loop if sample is shorter:

      t.insertSample(sample, {
      sample: { bpm: 120 },
      region: { positionTicks: Ticks.Bars(1), durationTicks: Ticks.Bars(3) },
      loop: true,
      })
      t.insertSample(sample, {
      sample: { bpm: 120 },
      region: { positionTicks: Ticks.Bars(1), durationTicks: Ticks.Bars(9) },
      loop: { startTicks: Ticks.Bars(2), durationTicks: Ticks.Bars(2) },
      })

      If you have a local sample, you have to upload it first. See the "Insert before processing completes" example below if you want to insert earlier, before the backend has finished transcoding.

      const file: File = ...
      const upload = await at.samples.upload({ file, displayName: "My Sample", bpm: 120 })
      if (upload instanceof Error){
      throw upload
      }

      // wait for the bytes to be on the server - after this point the user
      // can safely close the tab.
      const uploaded = await upload.uploaded
      if (uploaded instanceof Error){
      throw uploaded
      }

      // wait for backend processing (transcoding, duration, waveform).
      const sample = await upload.ready
      if (sample instanceof Error){
      throw sample
      }

      // `sample` is a SampleMeta — pass it directly.
      await nexus.modify(t => {
      t.insertSample(sample)
      })
      const upload = await at.samples.upload({ file, displayName: "My Sample", bpm: 120 })
      if (upload instanceof Error){
      throw upload
      }

      // you should wait for this point - if the user leaves before upload completes, the project will contain a broken sample.
      const uploaded = await upload.uploaded
      if (uploaded instanceof Error){
      throw uploaded
      }

      // The sample is uploaded, we can insert into the project, but we need the sample's duration.
      // In the web, you can do this as follows. Node.js/Bun/Deno may differ:
      const context = new AudioContext()
      const decoded = await context.decodeAudioData(await file.arrayBuffer()) // throws if decoding fails!
      await context.close()

      await nexus.modify(t => {
      t.insertSample(
      {
      name: upload.name,
      durationSeconds: decoded.duration,
      },
      {
      sample: { bpm: 120 }
      }
      )
      })
    • Delete an entity with id, after all entities with pointers to it, transitively, are deleted. In other words, remove entity id, after all entities are deleted that would result in dangling pointers if id was removed.

      Example

      Let's say we have entities a, b, c, d, e, with pointers between each other like this:

        a ─► b ─┐
        │       ├──► d ─► e
        │       │
        └──► c ─┘
      

      Then calling removeWithDependencies(d.id) will remove all entities except e, in an order that keeps all existing pointers valid after every modification.

      Parameters

      Returns void

    • Try to update a primitive field. Don't throw if it fails; return a string explaining the error instead. If this returns undefined, the update was applied. Useful when e.g. a user enters a value and it's not possible to know if the value is valid or not.

      Type Parameters

      Parameters

      Returns undefined | string