<|start_filename|>packages/piece-no-refrain/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  createPrerenderableSampler,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../no-refrain.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    onProgress,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'no-refrain__vsco2-piano-mf',
    getDestination: () =>
      new Tone.Reverb(5)
        .set({ wet: 0.5 })
        .toDestination()
        .generate(),
    notes: ['A2', 'C3', 'G3', 'G2', 'G3'].concat(
      toss(['C', 'E', 'G', 'B'], [4, 5])
    ),
  });

  const rightHandPcs = ['C', 'D', 'E', 'G', 'B', 'C'];
  let rightHandOct = 4;
  const leftHand = () => {
    const time = window.generativeMusic.rng() / 1.5 + 0.7;
    ['A2', 'C3', 'G3', 'G2', 'D3', 'G3'].forEach((note, i) => {
      piano.triggerAttack(note, `+${1 + i * time + window.generativeMusic.rng() / 5 - 0.1}`);
      if (window.generativeMusic.rng() < 0.8) {
        const rightHandPcIndex = Math.floor(
          window.generativeMusic.rng() * rightHandPcs.length
        );
        const rightHandPc = rightHandPcs[rightHandPcIndex];
        const oct =
          rightHandPcIndex < rightHandPcs.length - 1
            ? rightHandOct
            : rightHandOct + 1;
        piano.triggerAttack(
          `${rightHandPc}${oct}`,
          `+${1 + i * time + window.generativeMusic.rng() / 5 - 0.1}`
        );
      }
    });

    if (window.generativeMusic.rng() < 0.1) {
      const rightHandPcIndex = Math.floor(window.generativeMusic.rng() * rightHandPcs.length);
      const rightHandPc = rightHandPcs[rightHandPcIndex];
      const oct =
        rightHandPcIndex < rightHandPcs.length - 1
          ? rightHandOct
          : rightHandOct + 1;
      piano.triggerAttack(
        `${rightHandPc}${oct}`,
        `+${1 + 12 * time + window.generativeMusic.rng() / 5 - 0.1}`
      );
    }

    if (rightHandOct === 4 && window.generativeMusic.rng() < 0.05) {
      rightHandOct = 5;
    } else if (rightHandOct === 5 && window.generativeMusic.rng() < 0.1) {
      rightHandOct = 4;
    }

    Tone.Transport.scheduleOnce(() => {
      leftHand();
    }, `+${time * 16 + window.generativeMusic.rng() * 1.5}`);
  };

  const schedule = ({ destination }) => {
    piano.connect(destination);
    leftHand();
    return () => {
      piano.releaseAll(0);
    };
  };

  const deactivate = () => {
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['no-refrain'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>test-player/src/index.js<|end_filename|>
import * as Tone from 'tone';
import getSamplesByFormat from '@generative-music/samples-alex-bainter';
import createProvider from '@generative-music/web-provider';
import createLibrary from '@generative-music/web-library';
import SaveWorker from '@generative-music/web-provider/worker/save-worker.esm';

const sampleIndex = getSamplesByFormat({
  host: 'http://localhost:6969',
  format: 'ogg',
});
const provider = createProvider(new SaveWorker());
const library = createLibrary({ sampleIndex, provider });

// overridden by webpack to load a specific piece;
import activate from 'piece';

const toneContext = Tone.getContext();

const onProgress = value => {
  console.log(`${Math.round(value * 100)}%`);
};

const meter = new Tone.Meter().toDestination();
let maxDb = -Infinity;
const meterInterval = setInterval(() => {
  maxDb = Math.max(maxDb, meter.getValue());
});
const logInterval = setInterval(() => {
  console.log(maxDb);
}, 1000);

activate({
  onProgress,
  context: toneContext,
  sampleLibrary: library,
  destination: meter,
}).then(([deactivate, schedule]) => {
  console.log('activated');

  let dispose = schedule();

  Tone.Transport.start();

  window.stop = () => {
    Tone.Transport.stop();
    Tone.Transport.cancel();
    dispose();
  };

  window.start = () => {
    dispose = schedule();

    Tone.Transport.start();
  };

  window.reset = () => {
    console.log('resetting');
    window.stop();
    window.start();
  };

  window.deactivate = () => {
    console.log('deactivating');
    deactivate();
  };
});


<|start_filename|>packages/utilities/src/sample-note.js<|end_filename|>
import * as Tone from 'tone';
import getClosestNote from './get-closest-note';

const sampleNote = ({ note, sampledNotes = [], pitchShift = 0 }) => {
  const midi = Tone.Midi(note).toMidi();
  const sampledMidiSet = new Set(
    sampledNotes.map(sampledNote =>
      typeof sampledNote === 'number'
        ? sampledNote
        : Tone.Midi(sampledNote).toMidi()
    )
  );
  const closestMidi = getClosestNote({
    targetMidi: midi,
    searchedMidiSet: sampledMidiSet,
  });
  const playbackRate = Tone.intervalToFrequencyRatio(
    midi - closestMidi + pitchShift
  );
  const sampledNoteIndex = Array.from(sampledMidiSet).indexOf(closestMidi);
  const sampledNote = sampledNotes[sampledNoteIndex];
  return {
    sampledNote,
    playbackRate,
  };
};

export default sampleNote;


<|start_filename|>normalize/main.js<|end_filename|>
'use strict';

const { app, BrowserWindow, ipcMain } = require('electron');
const { byId } = require('../packages/pieces-alex-bainter');
const gain = require('./gain');

const RENDER_TIME = 60; //seconds

app.on('window-all-closed', e => e.preventDefault());

const getDecibels = pieceId =>
  new Promise(resolve => {
    const win = new BrowserWindow({
      width: 800,
      height: 600,
      webPreferences: {
        nodeIntegration: true,
      },
    });
    win.loadFile('index.html');
    win.webContents.openDevTools();
    ipcMain.once('ready', event => {
      event.reply('begin', { pieceId, renderTime: RENDER_TIME });
    });
    ipcMain.once('complete', (event, maxDb) => {
      win.close();
      resolve(maxDb);
    });
  });

app.whenReady().then(() => {
  Object.keys(byId)
    .filter(pieceId => typeof gain[pieceId] !== 'number')
    .reduce(
      (resultPromise, pieceId) =>
        resultPromise.then(results =>
          getDecibels(pieceId).then(gain => {
            results[pieceId] = gain;
            console.log(results);
            return results;
          })
        ),
      Promise.resolve({})
    );
});


<|start_filename|>packages/utilities/src/theory/sort-notes.js<|end_filename|>
import simplifyNote from './simplify-note';
import getPitchClass from './get-pitch-class';
import getOctave from './get-octave';
import pitchClassIndiciesByValue from './pitch-class-indicies-by-value';

const sortNotes = (notes = []) =>
  notes
    .map(simplifyNote)
    .map(note => [getPitchClass(note), getOctave(note)])
    .sort((a, b) => {
      const [pcA, octA] = a;
      const [pcB, octB] = b;
      if (octA === octB || octA === null) {
        return pitchClassIndiciesByValue[pcA] - pitchClassIndiciesByValue[pcB];
      }
      return octA - octB;
    })
    .map(parts => parts.join(''));

export default sortNotes;


<|start_filename|>packages/piece-above-the-rain/above-the-rain.gfm.manifest.json<|end_filename|>
{
  "title": "Above the Rain",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "above-the-rain",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "drone",
    "calm",
    "electronic"
  ],
  "releaseDate": "2019-09-22T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "above-the-rain__sso-chorus-female",
      "sso-chorus-female"
    ],
    [
      "above-the-rain__vsco2-trumpet-sus-mf",
      "vsco2-trumpet-sus-mf"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/above-the-rain-excerpts"
}

<|start_filename|>packages/piece-above-the-rain/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  createPrerenderableBuffers,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../above-the-rain.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const getReverb = () => new Tone.Reverb(15).toDestination().generate();
  const chorus = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'sso-chorus-female',
    renderedInstrumentName: 'above-the-rain__sso-chorus-female',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.5),
  });

  const trumpet = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: ['C', 'E', 'G'].map(pc => `${pc}3`),
    sourceInstrumentName: 'vsco2-trumpet-sus-mf',
    renderedInstrumentName: 'above-the-rain__vsco2-trumpet-sus-mf',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.5 + 0.5),
  });

  trumpet.set({ attack: 5, curve: 'linear' });

  const compressor = new Tone.Compressor();
  trumpet.connect(compressor);

  const playbackRate = 0.25;
  const vol = new Tone.Volume(-10);
  const activeSources = [];
  const play = notes => {
    const note = notes[Math.floor(window.generativeMusic.rng() * notes.length)];
    const buf = chorus.get(note);
    const source = new Tone.BufferSource(buf)
      .set({
        playbackRate,
        fadeIn: 4,
        fadeOut: 4,
        curve: 'linear',
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i > -1) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(vol);
    source.start('+1', 0, buf.duration / playbackRate);
    activeSources.push(source);

    if (window.generativeMusic.rng() < 0.15) {
      const [pc] = note;
      trumpet.triggerAttack(`${pc}3`, `${1 + window.generativeMusic.rng() * 5}`);
    }

    Tone.Transport.scheduleOnce(() => {
      play(notes);
    }, `+${buf.duration / playbackRate - 4 + window.generativeMusic.rng() * 5 - 2.5}`);
  };

  const schedule = ({ destination }) => {
    compressor.connect(destination);
    const autoFilter = new Tone.AutoFilter(window.generativeMusic.rng() / 100 + 0.01, 100, 4);
    autoFilter.connect(compressor);
    autoFilter.start();

    vol.connect(autoFilter);

    play(['C5']);
    play(['A5', 'G5', 'F5', 'D5', 'E5']);
    play(['C6']);

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
      trumpet.releaseAll(0);
      autoFilter.dispose();
    };
  };

  const deactivate = () => {
    [chorus, trumpet, vol].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['above-the-rain'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-lullaby/src/piece.js<|end_filename|>
import {
  getContext,
  Transport,
  Reverb,
  Gain,
  Filter,
  ToneBufferSource,
  AutoFilter,
} from 'tone';
import {
  wrapActivate,
  createPitchShiftedSampler,
  createPrerenderableBuffers,
} from '@generative-music/utilities';
import { sampleNames } from '../piece.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const playProgression = piano => {
  piano.triggerAttack('C4', `+${1 + window.generativeMusic.rng() * 0.1 - 0.05}`);
  piano.triggerAttack('G4', `+${1 + window.generativeMusic.rng() * 0.1 - 0.05}`);
  const t2 = 6 + window.generativeMusic.rng() * 6;
  piano.triggerAttack('C4', `+${t2 + window.generativeMusic.rng() * 0.1 - 0.05}`);
  piano.triggerAttack('A4', `+${t2 + window.generativeMusic.rng() * 0.1 - 0.05}`);
  const t3 = t2 + 1 + window.generativeMusic.rng() * 4;

  if (window.generativeMusic.rng() < 0.9) {
    piano.triggerAttack('C4', `+${t3 + window.generativeMusic.rng() * 0.1 - 0.05}`);
    piano.triggerAttack('F4', `+${t3 + window.generativeMusic.rng() * 0.1 - 0.05}`);
  }

  const now = new Date();
  const minutes = now.getMinutes();

  if (window.generativeMusic.rng() < minutes / 60) {
    piano.triggerAttack('C6', `+${1 + window.generativeMusic.rng()}`);
  }

  if (window.generativeMusic.rng() < 0.2) {
    piano.triggerAttack('E6', `+${1 + window.generativeMusic.rng() * t2}`);
  }

  if (window.generativeMusic.rng() < (minutes % 3) / 3) {
    piano.triggerAttack('A6', `+${t2 + window.generativeMusic.rng()}`);
  }

  if (window.generativeMusic.rng() < (60 - minutes) / 60) {
    piano.triggerAttack('C7', `+${t3 + window.generativeMusic.rng()}`);
  }

  Transport.scheduleOnce(() => {
    playProgression(piano);
  }, `+${t3 + window.generativeMusic.rng() * 10 + 5}`);
};

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(getContext(), sampleNames);
  const pianos = await Promise.all([
    createPitchShiftedSampler({
      samplesByNote: samples['vsco2-piano-mf'],
      pitchShift: -12,
    }),
    createPitchShiftedSampler({
      samplesByNote: samples['vsco2-piano-mf'],
      pitchShift: -24,
    }),
  ]);

  const birdBuffers = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'birds',
    renderedInstrumentName: 'lullaby__birds',
    getDestination: () => new Reverb(15).toDestination().generate(),
    onProgress: val => onProgress(val / 2),
  });

  const birdBuffer = birdBuffers.get(0);

  const activeSources = [];

  const playBirdSnippet = ({ destination }) => {
    const startTime = window.generativeMusic.rng() * (birdBuffer.duration - 6);
    const duration = Math.max(
      6,
      window.generativeMusic.rng() * (birdBuffer.duration - startTime)
    );
    const playbackRate = window.generativeMusic.rng() * 0.1 + 0.1;
    const source = new ToneBufferSource(birdBuffer).set({
      fadeIn: 3,
      fadeOut: 3,
      onended: () => {
        const index = activeSources.indexOf(source);
        if (index > -1) {
          activeSources.splice(index, 1);
        }
      },
      playbackRate,
    });
    activeSources.push(source);
    source.connect(destination);
    source.start('+1', startTime, duration / playbackRate, 0.33);
    Transport.scheduleOnce(() => {
      playBirdSnippet({ destination });
    }, `+${duration + window.generativeMusic.rng() * 5}`);
  };

  if (samples['explosion']) {
    samples['explosion'][0].reverse = true;
  }

  const explosionBuffers = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'explosion',
    renderedInstrumentName: 'lullaby__explosion',
    getDestination: () => new Reverb(15).toDestination().generate(),
    onProgress: val => onProgress(val / 2 + 0.5),
  });

  const explosionGain = new Gain(0.05);
  const lowpass = new Filter(200).connect(explosionGain);
  const explosionBuffer = explosionBuffers.get(0);

  const playReverseExplosion = () => {
    const explosionSource = new ToneBufferSource(explosionBuffer)
      .set({
        playbackRate: window.generativeMusic.rng() * 0.1 + 0.05,
        fadeOut: 3,
        onended: () => {
          const index = activeSources.indexOf(explosionSource);
          if (index > -1) {
            activeSources.splice(index, 1);
          }
        },
      })
      .connect(lowpass);
    activeSources.push(explosionSource);
    explosionSource.start();

    Transport.scheduleOnce(() => {
      playReverseExplosion();
    }, `+${window.generativeMusic.rng() * 100 + 60}`);
  };

  const schedule = ({ destination }) => {
    explosionGain.connect(destination);
    const pianoAutoFilters = pianos.map(piano => {
      const autoFilter = new AutoFilter(0.01 * window.generativeMusic.rng() + 0.005).connect(
        destination
      );
      autoFilter.start();
      piano.connect(autoFilter);
      playProgression(piano);
      return autoFilter;
    });
    playBirdSnippet({ destination });
    playReverseExplosion();

    return () => {
      pianos.forEach(piano => {
        piano.releaseAll(0);
      });
      activeSources.forEach(source => {
        source.stop(0);
      });
      pianoAutoFilters.forEach(autoFilter => {
        autoFilter.dispose();
      });
    };
  };

  const deactivate = () => {
    pianos
      .concat(activeSources)
      .concat([birdBuffers, explosionBuffers])
      .forEach(node => {
        node.dispose();
      });
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['lullaby'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-lemniscate/src/combine-notes-with-octaves.js<|end_filename|>
const combineNotesWithOctaves = (notes, octaves) =>
  octaves.reduce(
    (collection, octave) =>
      collection.concat(notes.map(note => `${note}${octave}`)),
    []
  );

export default combineNotesWithOctaves;


<|start_filename|>packages/piece-buttafingers/buttafingers.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Buttafingers",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "buttafingers",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "drone",
    "electronic"
  ],
  "releaseDate": "2019-03-31T00:00:00.000Z",
  "sampleNames": [
    "vcsl-wine-glasses-slow",
    [
      "buttafingers__vcsl-claves",
      "vcsl-claves"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/buttafingers-excerpts"
}

<|start_filename|>packages/piece-uun/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createSampler,
  transpose,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../uun.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

function* makeNoteGenerator(notes) {
  for (
    let i = 0;
    i < notes.length;
    i === notes.length - 1 ? (i = 0) : (i += 1)
  ) {
    yield notes[i];
  }
}

const trillNoteSets = [['D5', 'C5'], ['D#5', 'D5'], ['F5', 'D#5']];

const trillGenerators = trillNoteSets.map(notes => makeNoteGenerator(notes));

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await createSampler(samples['vsco2-piano-mf']);
  const splatterNotes = transposeUp => {
    const multiplier = Math.pow(window.generativeMusic.rng(), 2) * 0.5 + 0.01;
    ['C3', 'D#3', 'G3', 'A#3', 'D4']
      .map(note => (transposeUp ? transpose(note, 5) : note))
      .forEach((note, i) => {
        piano.triggerAttack(
          note,
          `+${1 + multiplier * i + window.generativeMusic.rng() / 50 - 0.01}`
        );
      });
  };

  const trillNotes = transposeUp => {
    const trillerGeneratorIndex = Math.floor(
      window.generativeMusic.rng() * trillGenerators.length
    );
    const trillGenerator = trillGenerators[trillerGeneratorIndex];

    const trill = Array.from({
      length: Math.ceil(window.generativeMusic.rng() * 8) + 12,
    })
      .map(() => trillGenerator.next().value)
      .map(note =>
        transposeUp && trillGenerator !== 3 ? transpose(note, 5) : note
      );
    const upper = window.generativeMusic.rng() * 0.5 + 0.4;
    const lower = window.generativeMusic.rng() * 0.1 + 0.2;
    const getNoteWaitTime = x =>
      -4 * (lower - upper) * Math.pow(x, 2) + 4 * (lower - upper) * x + upper;
    const lastTrillTime = (window.generativeMusic.rng() < 0.5
      ? trill
      : trill.reverse()
    ).reduce((lastNoteTime, note, i) => {
      const noteTime = lastNoteTime + getNoteWaitTime(i / (trill.length - 1));
      piano.triggerAttack(
        note,
        `+${noteTime + window.generativeMusic.rng() / 50 - 0.01}`,
        0.5
      );
      return noteTime;
    }, 1 - upper);

    return lastTrillTime;
  };

  const playMoment = () => {
    const up = window.generativeMusic.rng() < 0.5;
    splatterNotes(up);
    const lastTrillTime = trillNotes(up);

    Tone.Transport.scheduleOnce(() => {
      playMoment();
    }, `+${window.generativeMusic.rng() * 5 + lastTrillTime - 0.5}`);
  };

  const schedule = ({ destination }) => {
    piano.connect(destination);
    playMoment();
    return () => {
      piano.releaseAll(0);
    };
  };

  const deactivate = () => {
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['uun'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>scripts/build-samples-names.js<|end_filename|>
'use strict';

const glob = require('glob');
const pfs = require('fs').promises;
const sampleNameRegex = /samples(?:(?:\.([\w-]+))|(?:\['([\w-]+)'\]))/g;

const findSampleNames = filename =>
  pfs.readFile(filename, 'utf8').then(content => {
    const sampleNames = [];
    let match = sampleNameRegex.exec(content);
    while (match) {
      //eslint-disable-next-line no-unused-vars
      const [group1, group2, group3] = match;
      if (group2) {
        sampleNames.push(group2);
      } else {
        sampleNames.push(group3);
      }
      match = sampleNameRegex.exec(content);
    }
    return Array.from(new Set(sampleNames));
  });

const globPromise = pattern =>
  new Promise((resolve, reject) => {
    glob(pattern, (err, filenames) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(filenames);
    });
  });

Promise.all([
  globPromise('./packages/*/src/piece.js'),
  globPromise('./packages/*/*.gfm.manifest.json'),
]).then(([pieceFilenames, manifestFilenames]) =>
  Promise.all(
    pieceFilenames.map((pieceFilename, i) => {
      const manifestFilename = manifestFilenames[i];
      return Promise.all([
        findSampleNames(pieceFilename),
        pfs
          .readFile(manifestFilename, 'utf8')
          .then(content => JSON.parse(content)),
      ]).then(([sampleNames, manifest]) =>
        pfs.writeFile(
          manifestFilename,
          //eslint-disable-next-line no-magic-numbers
          JSON.stringify(Object.assign(manifest, { sampleNames }), null, 2)
        )
      );
    })
  )
);


<|start_filename|>packages/piece-drones/drones.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Drones",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "drones",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "drone",
    "electronic"
  ],
  "releaseDate": "2019-03-13T00:00:00.000Z",
  "sampleNames": [
    "vsco2-trumpet-sus-f",
    "vsco2-trumpet-sus-mf"
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/drones-excerpts"
}

<|start_filename|>packages/piece-transmission/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  transpose,
  wrapActivate,
  createPrerenderableSampler,
  sortNotes,
  getDistance,
} from '@generative-music/utilities';
import { sampleNames } from '../transmission.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const TREMOLO_PATTERN = [
  true,
  false,
  true,
  false,
  true,
  true,
  false,
  true,
  false,
  true,
  false,
  true,
  true,
  false,
  true,
  false,
];

const MIN_BPM = 40;
const MAX_BPM = 80;

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const renderedNotes = sortNotes(
    ['C3', 'E3', 'G3', 'B3']
      .map(note => [-15, -5, 0, 5, 15, 27].map(transpose(note)))
      .flat()
  ).filter((note, i, notes) => {
    if (i === notes.length - 1) {
      return true;
    }
    const distanceToNext = getDistance(note, notes[i + 1]);
    return distanceToNext > 2;
  });

  const [tremPiano, piano] = await Promise.all(
    Array.from({ length: 2 }, () =>
      createPrerenderableSampler({
        samples,
        sampleLibrary,
        onProgress,
        notes: renderedNotes,
        sourceInstrumentName: 'vsco2-piano-mf',
        renderedInstrumentName: 'transmission__vsco2-piano-mf',
        getDestination: () =>
          new Tone.Reverb(15)
            .set({ wet: 0.5 })
            .toDestination()
            .generate(),
      })
    )
  );

  const filter = new Tone.Filter();
  const gain = new Tone.Gain().connect(filter);
  const synth = new Tone.Synth().set({ volume: -25 });

  const tremoloChord = (
    transposition = 0,
    bpm = window.generativeMusic.rng() * (MAX_BPM - MIN_BPM) + MIN_BPM,
    up = window.generativeMusic.rng() < 0.5
  ) => {
    const notes = ['C3', 'E3', 'G3', 'B3'].map(transpose(transposition));
    const [bassNote] = notes;
    synth.set({ envelope: { release: 60 / bpm } });
    synth.triggerAttackRelease(transpose(bassNote, -12), (60 / bpm) * 3, '+1');
    tremPiano.triggerAttack(notes, '+1');

    notes
      .filter(() => window.generativeMusic.rng() < 0.5)
      .forEach(note => {
        piano.triggerAttack(
          transpose(note, 12),
          `+${window.generativeMusic.rng() * ((60 / bpm) * 3 - 1) + 1}`
        );
      });

    TREMOLO_PATTERN.forEach((isOn, i) => {
      gain.gain.setValueAtTime(isOn ? 1 : 0.5, `+${1 + i * (60 / bpm / 4)}`);
    });

    let semitoneChange = 0;
    if (window.generativeMusic.rng() < 0.25) {
      if (transposition === -10) {
        semitoneChange = 5;
      } else if (transposition === 10) {
        semitoneChange = -5;
      } else {
        semitoneChange = window.generativeMusic.rng() < 0.5 ? 5 : -5;
      }
    }

    let nextBpm;
    let nextUp = up;
    const pctDelta = window.generativeMusic.rng() * 0.005;
    if (bpm <= MIN_BPM && !up) {
      nextBpm = bpm * (1 + pctDelta);
      nextUp = true;
    } else if (bpm >= MAX_BPM && up) {
      nextBpm = bpm * (1 - pctDelta);
      nextUp = false;
    } else if (up) {
      nextBpm = bpm * (1 + pctDelta);
    } else {
      nextBpm = bpm * (1 - pctDelta);
    }

    Tone.Transport.scheduleOnce(() => {
      tremoloChord(transposition + semitoneChange, nextBpm, nextUp);
    }, `+${(60 / bpm) * 4}`);
  };

  tremPiano.connect(gain);

  const chorus = new Tone.Chorus();
  piano.connect(chorus);

  const schedule = ({ destination }) => {
    filter.connect(destination);
    synth.connect(destination);
    chorus.connect(destination);
    const chorusWetLfo = new Tone.LFO(window.generativeMusic.rng() / 1000 + 0.001).set({
      phase: window.generativeMusic.rng() * 360,
    });

    const filterFreqLfo = new Tone.LFO(
      window.generativeMusic.rng() / 1000 + 0.001,
      200,
      1000
    ).set({ phase: window.generativeMusic.rng() * 360 });
    filterFreqLfo.connect(filter.frequency);
    filterFreqLfo.start();

    chorusWetLfo.connect(chorus.wet);
    chorusWetLfo.start();

    tremoloChord();

    return () => {
      gain.gain.cancelScheduledValues(Tone.now());
      tremPiano.releaseAll(0);
      piano.releaseAll(0);
      chorusWetLfo.dispose();
      filterFreqLfo.dispose();
    };
  };

  const deactivate = () => {
    [filter, gain, synth, chorus, tremPiano, piano].forEach(node => {
      node.dispose();
    });
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['transmission'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-eno-machine/eno-machine.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "title": "Eno Machine",
  "id": "eno-machine",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "calm",
    "acoustic"
  ],
  "releaseDate": "2018-05-07T00:00:00.000Z",
  "sampleNames": [
    "vsco2-piano-mf"
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/eno-machine-excerpts"
}

<|start_filename|>packages/piece-sevenths/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createSampler,
  wrapActivate,
  getRandomNumberBetween,
  getRandomElement,
  major7th,
  minor7th,
  dominant7th,
  invert,
} from '@generative-music/utilities';
import { sampleNames } from '../sevenths.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const CHORDS = [major7th, minor7th, dominant7th];
// eslint-disable-next-line no-magic-numbers
const OCTAVES = [2, 3, 4, 5];
const MIN_ARPEGGIO_TIME_S = 0.25;
const MAX_ARPEGGIO_TIME_S = 5;
const MIN_NEXT_CHORD_TIME_S = 3;
const MAX_NEXT_CHORD_TIME_S = 15;

const makeScheduleChord = instrument => {
  const scheduleChord = () => {
    const pitchClass = getRandomElement([
      'C',
      'C#',
      'D',
      'D#',
      'E',
      'F',
      'F#',
      'G',
      'G#',
      'A',
      'A#',
      'B',
    ]);
    const octave = getRandomElement(OCTAVES);
    const tonic = `${pitchClass}${octave}`;
    const chord = getRandomElement(CHORDS);
    const inversion = Math.floor(getRandomNumberBetween(0, 4));
    const notes = invert(chord(tonic), inversion);
    const chordTime =
      window.generativeMusic.rng() * (MAX_NEXT_CHORD_TIME_S - MIN_NEXT_CHORD_TIME_S) +
      MIN_NEXT_CHORD_TIME_S;
    const arpeggioTime = getRandomNumberBetween(
      MIN_ARPEGGIO_TIME_S,
      MAX_ARPEGGIO_TIME_S
    );
    notes.forEach(note => {
      const noteTime = getRandomNumberBetween(0, arpeggioTime);
      instrument.triggerAttack(note, `+${noteTime}`);
    });
    Tone.Transport.scheduleOnce(() => {
      scheduleChord();
    }, `+${chordTime}`);
  };
  return scheduleChord;
};

const getPiano = samples => createSampler(samples['vsco2-piano-mf']);

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await getPiano(samples);
  const scheduleChord = makeScheduleChord(piano);
  const schedule = ({ destination }) => {
    piano.connect(destination);
    scheduleChord();
    return () => {
      piano.releaseAll(0);
    };
  };
  const deactivate = () => {
    piano.dispose();
  };
  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['sevenths'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>rollup.config.js<|end_filename|>
'use strict';

const glob = require('glob');
const { babel } = require('@rollup/plugin-babel');
const json = require('@rollup/plugin-json');
const { terser } = require('rollup-plugin-terser');
const { nodeResolve } = require('@rollup/plugin-node-resolve');
const commonJs = require('@rollup/plugin-commonjs');

const globPromise = (pattern, options) =>
  new Promise((resolve, reject) =>
    glob(pattern, options, (err, result) => {
      if (err) {
        return reject(err);
      }
      return resolve(result);
    })
  );

const createPiecePackageConfig = (
  dirname,
  dependencies = {},
  peerDependencies = {}
) => ({
  input: `${dirname}/src/piece.js`,
  output: [
    {
      file: `${dirname}/dist/esm.js`,
      format: 'esm',
    },
    {
      file: `${dirname}/dist/cjs.js`,
      format: 'cjs',
      exports: 'auto',
    },
  ],
  external: [/rxjs/, /@babel\/runtime/]
    .concat(Reflect.ownKeys(dependencies))
    .concat(Reflect.ownKeys(peerDependencies)),
  plugins: [
    json(),
    babel({
      exclude: 'node_modules/**',
      babelHelpers: 'runtime',
      plugins: ['@babel/plugin-transform-runtime'],
      presets: ['@babel/preset-env'],
    }),
  ],
});

const convertKebabToCamel = kebabCaseString =>
  kebabCaseString
    .split('-')
    .map((word, i) =>
      i === 0 ? word : `${word.charAt(0).toUpperCase()}${word.slice(1)}`
    )
    .join('');

const createPieceScriptConfig = dirname => ({
  input: `${dirname}/src/piece.js`,
  output: {
    file: `${dirname}/dist/umd.min.js`,
    format: 'umd',
    name: convertKebabToCamel(dirname.replace('./packages/', '')),
    globals: {
      tone: 'Tone',
    },
  },
  external: ['tone'],
  plugins: [
    json(),
    commonJs(),
    babel({
      exclude: 'node_modules/**',
      babelHelpers: 'bundled',
      presets: ['@babel/preset-env'],
    }),
    nodeResolve({ extensions: ['.js', '.json'] }),
    terser(),
  ],
});

const pieceConfigsPromise = globPromise('./packages/piece-*').then(dirnames =>
  dirnames.reduce((buildConfigs, dirname) => {
    const {
      dependencies,
      peerDependencies,
      //eslint-disable-next-line global-require
    } = require(`${dirname}/package.json`);
    return buildConfigs.concat([
      createPiecePackageConfig(dirname, dependencies, peerDependencies),
      createPieceScriptConfig(dirname),
    ]);
  }, [])
);

const utilitiesConfig = {
  input: 'packages/utilities/src/index.js',
  output: [
    {
      format: 'esm',
      file: 'packages/utilities/dist/esm.js',
    },
    {
      format: 'cjs',
      file: 'packages/utilities/dist/cjs.js',
      exports: 'auto',
    },
  ],
  external: ['tone', /@babel\/runtime/],
  plugins: [
    babel({
      exclude: 'node_modules/**',
      babelHelpers: 'runtime',
      plugins: ['@babel/plugin-transform-runtime'],
      presets: ['@babel/preset-env'],
    }),
  ],
};

const makeOxalisConfig = {
  input: 'packages/make-piece-oxalis/src/make-oxalis.js',
  output: [
    {
      format: 'esm',
      file: 'packages/make-piece-oxalis/dist/esm.js',
    },
    {
      format: 'cjs',
      file: 'packages/make-piece-oxalis/dist/cjs.js',
      exports: 'auto',
    },
  ],
  external: ['tone', '@generative-music/utilities', /@babel\/runtime/],
  plugins: [
    json(),
    babel({
      exclude: 'node_modules/**',
      babelHelpers: 'runtime',
      plugins: ['@babel/plugin-transform-runtime'],
      presets: ['@babel/preset-env'],
    }),
  ],
};

module.exports = pieceConfigsPromise.then(pieceConfigs =>
  [utilitiesConfig, makeOxalisConfig].concat(pieceConfigs)
);


<|start_filename|>packages/piece-awash/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  createPrerenderableSampler,
  createPrerenderableBuffers,
} from '@generative-music/utilities';
import { sampleNames } from '../awash.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const NOTES = ['C3', 'D#3', 'G3', 'A#3', 'C4', 'D#4', 'G4', 'A#4'];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const oceanDrumSamples =
    samples['awash__vcsl-ocean-drum'] || samples['vcsl-ocean-drum'];

  const guitar = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: NOTES,
    sourceInstrumentName: 'dry-guitar-vib',
    renderedInstrumentName: 'awash__dry-guitar-vib',
    getDestination: () => new Tone.Reverb(7).toDestination(),
    additionalRenderLength: 2,
    onProgress: val => onProgress(val / 2),
  });

  const oceanDrum = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vcsl-ocean-drum',
    renderedInstrumentName: 'awash__vcsl-ocean-drum',
    getDestination: () => new Tone.Reverb(7).toDestination(),
    additionalRenderLength: 1,
    onProgress: val => onProgress(val / 2 + 0.5),
  });

  guitar.set({ curve: 'linear', attack: 10, release: 15 });

  const filter = new Tone.Filter(5000, 'notch', -12);
  guitar.connect(filter);

  const schedule = ({ destination }) => {
    const delay1 = new Tone.FeedbackDelay({
      feedback: 0.7,
      delayTime: window.generativeMusic.rng() * 0.2 + 0.8,
    }).connect(destination);
    const delay2 = new Tone.FeedbackDelay({
      feedback: 0.5,
      delayTime: 0.25,
    }).connect(delay1);
    const autoFilter = new Tone.AutoFilter(window.generativeMusic.rng() / 10, 200, 5)
      .start()
      .connect(delay2);

    filter.connect(autoFilter);

    const activeSources = [];

    const firstOceanDelays = oceanDrumSamples.map(() => window.generativeMusic.rng() * 30);
    const minOceanDelay = Math.min(...firstOceanDelays);

    oceanDrumSamples.forEach((_, i) => {
      const buffer = oceanDrum.get(i);
      const play = () => {
        buffer.reverse = window.generativeMusic.rng() < 0.5;
        const source = new Tone.BufferSource(buffer)
          .set({
            fadeIn: 3,
            fadeOut: 3,
            curve: 'linear',
            playbackRate: 0.5,
            onended: () => {
              const index = activeSources.indexOf(source);
              if (index >= 0) {
                activeSources.splice(index, 1);
              }
            },
          })
          .connect(destination);
        activeSources.push(source);
        source.start('+1');
        Tone.Transport.scheduleOnce(() => {
          play();
        }, `+${window.generativeMusic.rng() * buffer.duration * 2 + buffer.duration * 2 + 1}`);
      };
      Tone.Transport.scheduleOnce(() => {
        play();
      }, `+${firstOceanDelays[i] - minOceanDelay}`);
    });

    const firstIndex = Math.floor(window.generativeMusic.rng() * NOTES.length);

    NOTES.forEach((note, i) => {
      const play = () => {
        guitar.triggerAttack(note, '+1');
        Tone.Transport.scheduleOnce(() => {
          play();
        }, `+${window.generativeMusic.rng() * 25 + 25}`);
      };
      if (i === firstIndex) {
        play();
      } else {
        Tone.Transport.scheduleOnce(() => {
          play();
        }, `+${window.generativeMusic.rng() * 50}`);
      }
    });

    return () => {
      guitar.releaseAll(0);
      activeSources.forEach(source => {
        source.dispose();
      });
      [delay1, delay2, autoFilter].forEach(node => node.dispose());
    };
  };

  const deactivate = () => {
    [filter, guitar, oceanDrum].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['awash'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-meditation/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createSampler,
  wrapActivate,
  getRandomElement,
  transpose,
  P5,
  M2,
  P4,
} from '@generative-music/utilities';
import { sampleNames } from '../meditation.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const P_SECOND_NOTE = 0.3;
const SECOND_NOTE_DELAY_MULTIPLIER_S = 15;
const MIN_SECOND_NOTE_DELAY_S = 1;
const LOW_OCTAVE_1 = 1;
const LOW_OCTAVE_2 = 2;
const HIGH_OCTAVE_1 = 3;
const HIGH_OCTAVE_2 = 4;
const LOWER_OCTAVES = [LOW_OCTAVE_1, LOW_OCTAVE_2];
const HIGHER_OCTAVES = [HIGH_OCTAVE_1, HIGH_OCTAVE_2];
const MIN_LOW_NOTE_INTERVAL_S = 45;
const MIN_HIGH_NOTE_INTERVAL_S = 75;
const INTERVAL_MULTIPLIER_S = 5;
const VOLUME_ADJUSTMENT = -15;
const TONIC_PITCH_CLASSES = ['C', 'C#', 'D', 'D#'];

const startInterval = (notes, minIntervalInSeconds, instrument) => {
  const playNotes = () => {
    instrument.triggerAttack(getRandomElement(notes), '+1');
    if (window.generativeMusic.rng() > P_SECOND_NOTE) {
      instrument.triggerAttack(
        getRandomElement(notes),
        `+${1 +
          window.generativeMusic.rng() * SECOND_NOTE_DELAY_MULTIPLIER_S +
          MIN_SECOND_NOTE_DELAY_S}`
      );
    }
    Tone.Transport.scheduleOnce(() => {
      playNotes();
    }, `+${window.generativeMusic.rng() * INTERVAL_MULTIPLIER_S + minIntervalInSeconds}`);
  };
  playNotes();
};

const getBowls = samples => createSampler(samples['kasper-singing-bowls']);

const pitchClassesOverOctaves = (pitchClasses, octaves) =>
  pitchClasses.reduce(
    (notes, pitchClass) =>
      notes.concat(octaves.map(octave => `${pitchClass}${octave}`)),
    []
  );

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const bowls = await getBowls(samples);
  const volume = new Tone.Volume(VOLUME_ADJUSTMENT);

  const schedule = ({ destination }) => {
    volume.connect(destination);
    const delay = new Tone.FeedbackDelay({
      wet: 0.5,
      delayTime: 20,
      maxDelay: 20,
      feedback: 0.8,
    });
    bowls.chain(delay, volume);

    const tonicPitchClass = getRandomElement(TONIC_PITCH_CLASSES);

    const lowPitchClasses = [tonicPitchClass, transpose(tonicPitchClass, P5)];
    const highPitchClasses = lowPitchClasses.concat(
      [M2, P4].map(interval => transpose(tonicPitchClass, interval))
    );
    const lowNotes = pitchClassesOverOctaves(lowPitchClasses, LOWER_OCTAVES);
    const highNotes = pitchClassesOverOctaves(highPitchClasses, HIGHER_OCTAVES);

    startInterval(lowNotes, MIN_LOW_NOTE_INTERVAL_S, bowls);
    startInterval(highNotes, MIN_HIGH_NOTE_INTERVAL_S, bowls);

    return () => {
      bowls.releaseAll(0);
      delay.dispose();
    };
  };

  const deactivate = () => {
    [bowls, volume].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['meditation'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-sevenths/sevenths.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Sevenths",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "sevenths",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "acoustic"
  ],
  "releaseDate": "2018-07-21T00:00:00.000Z",
  "sampleNames": [
    "vsco2-piano-mf"
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/sevenths-excerpts"
}

<|start_filename|>scripts/util.js<|end_filename|>
'use strict';

const fsp = require('fs').promises;
const glob = require('glob');

const filenames = glob.sync('./packages/piece-*/src/piece.js');

const findFirstNonImportIndex = lines => {
  for (let index = lines.length - 1; index > 0; index -= 1) {
    if (lines[index - 1].startsWith('import')) {
      return index;
    }
  }
  return -1;
};

Promise.all(
  filenames
    .filter(filename => !filename.includes('oxalis'))
    .map(filename =>
      fsp.readFile(filename, 'utf8').then(data => {
        const pieceId = filename
          .replace('./packages/piece-', '')
          .replace('/src/piece.js', '');
        const gainImport = `import gainAdjustments from '../../../normalize/gain.json';`;
        const gainAdjustmentDeclaration = `const GAIN_ADJUSTMENT = gainAdjustments['${pieceId}'];`;
        const exportWithGain = `export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });`;
        const lines = data.split('\n');
        const importEndIndex = findFirstNonImportIndex(lines);
        const exportLineIndex = lines.findIndex(line =>
          line.startsWith('export default wrapActivate(activate)')
        );
        if (importEndIndex === -1) {
          console.error('couldnt find last import line in ', filename);
          process.exit(1);
        }
        if (exportLineIndex === -1) {
          console.error('couldnt find export line in ', filename);
          process.exit(1);
        }

        const newLines = lines
          .slice(0, importEndIndex)
          .concat([gainImport])
          .concat(lines.slice(importEndIndex, exportLineIndex))
          .concat([gainAdjustmentDeclaration, '\r', exportWithGain, '\r']);

        const newFileContent = newLines.join('\n');
        return fsp.writeFile(filename, newFileContent);
      })
    )
).then(() => {
  console.log('done');
});


<|start_filename|>packages/utilities/src/create-buffer.spec.js<|end_filename|>
import * as Tone from 'tone';
import createBuffer from './create-buffer';

describe('createBuffer', () => {
  it('should return a promise', () => {
    expect(createBuffer('')).to.be.instanceOf(Promise);
    expect(
      createBuffer(Tone.context.createBuffer(1, 44100, 44100))
    ).to.be.an.instanceOf(Promise);
  });
  it('should resolve a string url with a Tone Buffer', () => {
    return createBuffer('./base/test-assets/noise-1s.ogg').then(result =>
      expect(result).to.be.an.instanceOf(Tone.ToneAudioBuffer)
    );
  });
  it('should resolve an AudioBuffer with a Tone Buffer', () => {
    return createBuffer(Tone.context.createBuffer(1, 44100, 44100)).then(
      result => expect(result).to.be.an.instanceOf(Tone.ToneAudioBuffer)
    );
  });
});


<|start_filename|>packages/utilities/src/theory/index.js<|end_filename|>
export { default as chord } from './chord';
export { default as transpose } from './transpose';
export { default as interval } from './transpose'; // deprecated
export { default as invert } from './invert';
export { default as getOctave } from './get-octave';
export { default as getPitchClass } from './get-pitch-class';
export { default as simplifyNote } from './simplify-note';
export { default as getDistance } from './get-distance';
export { default as sortNotes } from './sort-notes';

export * from './intervals';
export * from './chords';


<|start_filename|>packages/piece-return-to-form/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  shuffleArray,
  wrapActivate,
  P1,
  M3,
  P4,
  P5,
  transpose,
  getOctave,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../return-to-form.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const INTERVALS = [P1, M3, P4, P5];
const STARTING_TONICS = ['C3', 'C4'];
const NOTE_TIME_S = 2;

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await createPrerenderableSampler({
    notes: toss(['C', 'E', 'G', 'B'], [2, 3, 4, 5]),
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'return-to-form__vsco2-piano-mf',
    getDestination: () =>
      Promise.resolve(new Tone.Freeverb({ roomSize: 0.5 }).toDestination()),
    onProgress,
  });

  let tonics = STARTING_TONICS;

  const play = () => {
    if (window.generativeMusic.rng() < 0.2) {
      const up =
        (tonics.some(tonic => getOctave(tonic) <= 2) || window.generativeMusic.rng() < 0.5) &&
        !tonics.some(tonic => getOctave(tonic) >= 5);
      const change = window.generativeMusic.rng() < 0.5 ? P5 : M3;
      tonics = tonics.map(tonic => transpose(tonic, up ? change : -change));
    }
    shuffleArray(
      tonics.reduce(
        (notes, tonic) => notes.concat(INTERVALS.map(transpose(tonic))),
        []
      )
    )
      .slice(0, 5)
      .forEach((note, i) => {
        for (let j = 0; j < 4; j += 1) {
          piano.triggerAttack(
            note,
            `+${i * NOTE_TIME_S + j * 8 * NOTE_TIME_S}`
          );
        }
      });
    Tone.Transport.scheduleOnce(() => {
      play();
    }, `+${32 * NOTE_TIME_S}`);
  };

  const schedule = ({ destination }) => {
    tonics = STARTING_TONICS;
    const delay = new Tone.FeedbackDelay({
      delayTime: NOTE_TIME_S / 2,
      maxDelay: NOTE_TIME_S / 2,
      feedback: 0.7,
      wet: 0.3,
    }).connect(destination);
    piano.connect(delay);
    play();

    return () => {
      piano.releaseAll(0);
      delay.dispose();
    };
  };

  const deactivate = () => {
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['return-to-form'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-western-medicine/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampledBuffers,
  toss,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../western-medicine.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const getCustomSampler = async ({ prerenderOptions, destination }) => {
  const buffers = await createPrerenderableSampledBuffers(prerenderOptions);

  const activeSources = [];
  const triggerAttack = (note, time = Tone.now()) => {
    const buffer = buffers.get(note);
    buffer.reverse = window.generativeMusic.rng() < 0.5;
    const bufferSource = new Tone.BufferSource(buffer)
      .set({
        onended: () => {
          const i = activeSources.indexOf(bufferSource);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(destination);
    activeSources.push(bufferSource);
    bufferSource.start(time);
  };

  const releaseAll = time => {
    activeSources.forEach(source => {
      source.stop(time);
    });
  };

  const dispose = () => {
    [buffers, ...activeSources].forEach(node => node.dispose());
  };

  return {
    triggerAttack,
    releaseAll,
    dispose,
  };
};

const HARMONICS_NOTES = ['C4', 'G4', 'C3', 'C5'];
const MARIMBA_OCTS = [2, 3, 4, 5, 6];
const HARP_NOTES = ['C4', 'G4', 'C5', 'G5', 'C6', 'G6'];
const PIANO_NOTES = toss(['C', 'E', 'G'], [3, 4, 5, 6]);

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const filter = new Tone.Filter(3000);

  const [harmonics, marimba, harp, piano] = await Promise.all(
    [
      ['guitar-harmonics', HARMONICS_NOTES],
      ['vsco2-marimba', toss(['C', 'G'], MARIMBA_OCTS)],
      ['vsco2-harp', HARP_NOTES],
      ['vsco2-piano-mf', PIANO_NOTES],
    ].map(([instrumentName, notes], i) =>
      getCustomSampler({
        prerenderOptions: {
          samples,
          sampleLibrary,
          notes,
          pitchShift: -24,
          sourceInstrumentName: instrumentName,
          renderedInstrumentName: `western-medicine__${instrumentName}`,
          getDestination: () =>
            new Tone.Reverb(15)
              .set({ wet: 0.625 })
              .toDestination()
              .generate(),
          onProgress: val => onProgress(val * 0.25 + 0.25 * i),
          additionalRenderLength: 0.5,
        },
        destination: filter,
      })
    )
  );

  const playHarmonics = () => {
    const note =
      HARMONICS_NOTES[Math.floor(window.generativeMusic.rng() * HARMONICS_NOTES.length)];
    harmonics.triggerAttack(note, '+1');
    Tone.Transport.scheduleOnce(() => {
      playHarmonics();
    }, `+${window.generativeMusic.rng() * 20 + 5}`);
  };

  const playMarimba = () => {
    const notes = ['C', 'G'].map(
      note =>
        `${note}${
          MARIMBA_OCTS[Math.floor(window.generativeMusic.rng() * MARIMBA_OCTS.length)]
        }`
    );
    notes.forEach(note => {
      marimba.triggerAttack(note, `+${window.generativeMusic.rng() * 0.2 + 1}`);
    });
    Tone.Transport.scheduleOnce(() => {
      playMarimba();
    }, `+${window.generativeMusic.rng() * 20 + 20}`);
  };

  const playHarp = () => {
    const note = HARP_NOTES[Math.floor(window.generativeMusic.rng() * HARP_NOTES.length)];

    harp.triggerAttack(note, '+1');
    Tone.Transport.scheduleOnce(() => {
      playHarp();
    }, `+${window.generativeMusic.rng() * 10 + 10}`);
  };

  piano.triggerAttack('C3');
  const playPiano = () => {
    const notes = Array.from({
      length: Math.ceil(window.generativeMusic.rng() * 5),
    }).map(() => PIANO_NOTES[Math.floor(window.generativeMusic.rng() * PIANO_NOTES.length)]);
    const maxOffset = window.generativeMusic.rng() * 3;

    notes.forEach(note => {
      piano.triggerAttack(note, `+${1 + window.generativeMusic.rng() * maxOffset}`);
    });

    Tone.Transport.scheduleOnce(() => {
      playPiano();
    }, `+${window.generativeMusic.rng() * 15 + 15}`);
  };

  const schedule = ({ destination }) => {
    filter.connect(destination);
    playHarmonics();
    playMarimba();
    playHarp();
    playPiano();

    return () => {
      [harmonics, marimba, harp, piano].forEach(sampler => {
        sampler.releaseAll(0);
      });
    };
  };

  const deactivate = () => {
    [harmonics, marimba, harp, piano, filter].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['western-medicine'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-a-viable-system/a-viable-system.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "A Viable System",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "a-viable-system",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "acoustic"
  ],
  "releaseDate": "2019-01-25T00:00:00.000Z",
  "sampleNames": [
    [
      "a-viable-system__vsco2-piano-mf",
      "vsco2-piano-mf"
    ],
    [
      "a-viable-system__vsco2-contrabass-susvib",
      "vsco2-contrabass-susvib"
    ],
    [
      "a-viable-system__vsco2-violin-arcvib",
      "vsco2-violin-arcvib"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/a-viable-system-excerpts"
}

<|start_filename|>packages/utilities/src/create-prerenderable-sampler.js<|end_filename|>
import createSampler from './create-sampler';
import createPrerenderableSampledBuffers from './create-prerenderable-sampled-buffers';

const createPrerenderableSampler = async options => {
  const { notes } = options;
  const prerenderedBuffers = await createPrerenderableSampledBuffers(options);
  const prerenderedNoteMap = notes.reduce((o, note) => {
    o[note] = prerenderedBuffers.get(note);
    return o;
  }, {});
  return createSampler(prerenderedNoteMap);
};

export default createPrerenderableSampler;


<|start_filename|>packages/piece-animalia-chordata/animalia-chordata.gfm.manifest.json<|end_filename|>
{
  "title": "Animalia Chordata",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "animalia-chordata",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "noise",
    "electronic"
  ],
  "releaseDate": "2019-09-15T00:00:00.000Z",
  "visualizationType": "partialLattice",
  "sampleNames": [
    [
      "animalia-chordata__whales-dryer",
      "whales"
    ],
    [
      "animalia-chordata__whales-wetter",
      "whales"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/animalia-chordata-excerpts"
}

<|start_filename|>packages/utilities/src/create-buffer.js<|end_filename|>
import * as Tone from 'tone';

const cloneAudioBuffer = audioBuffer => {
  const clone = Tone.context.createBuffer(
    audioBuffer.numberOfChannels,
    audioBuffer.length,
    audioBuffer.sampleRate
  );
  for (
    let channelNumber = 0;
    channelNumber < audioBuffer.numberOfChannels;
    channelNumber += 1
  ) {
    clone.copyToChannel(
      audioBuffer.getChannelData(channelNumber),
      channelNumber
    );
  }
  return clone;
};

const createBuffer = url => {
  if (url instanceof AudioBuffer) {
    return Promise.resolve(new Tone.ToneAudioBuffer(cloneAudioBuffer(url)));
  }
  if (url instanceof Tone.ToneAudioBuffer) {
    return Promise.resolve(
      new Tone.ToneAudioBuffer(cloneAudioBuffer(url.get()))
    );
  }
  return new Promise(resolve => {
    const buffer = new Tone.ToneAudioBuffer(url, () => {
      resolve(buffer);
    });
  });
};

export default createBuffer;


<|start_filename|>packages/piece-remembering/remembering.gfm.manifest.json<|end_filename|>
{
  "title": "Remembering",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "remembering",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "acoustic"
  ],
  "releaseDate": "2019-10-20T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "remembering__vsco2-piano-mf",
      "vsco2-piano-mf"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/remembering-excerpts"
}

<|start_filename|>packages/piece-aisatsana/src/piece.js<|end_filename|>
import Chain from 'markov-chains';
import * as Tone from 'tone';
import { createSampler, wrapActivate } from '@generative-music/utilities';
import instructions from './instructions.json';
import { sampleNames } from '../aisatsana.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const BPM = 102;
const SECONDS_PER_MINUTE = 60;
const EIGHTH_NOTES_IN_BEAT = 2;
const EIGHTH_NOTE_INTERVAL_S =
  SECONDS_PER_MINUTE / (EIGHTH_NOTES_IN_BEAT * BPM);
const DELIMITER = ',';
const SONG_LENGTH = 301;

const getPiano = samples => createSampler(samples['vsco2-piano-mf']);

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const notes = instructions.tracks[1].notes.slice(0);
  const eighthNotes = [];

  for (let time = 0; time <= SONG_LENGTH; time += EIGHTH_NOTE_INTERVAL_S) {
    const names = notes
      .filter(
        note => time <= note.time && note.time < time + EIGHTH_NOTE_INTERVAL_S
      )
      .map(({ name }) => name)
      .sort();
    eighthNotes.push(names.join(DELIMITER));
  }

  const phrases = [];
  const phraseLength = 32;
  const enCopy = eighthNotes.slice(0);
  while (enCopy.length > 0) {
    phrases.push(enCopy.splice(0, phraseLength));
  }

  const phrasesWithIndex = phrases.map(phrase =>
    phrase.map((names, i) =>
      names.length === 0 ? `${i}` : `${i}${DELIMITER}${names}`
    )
  );

  const chain = new Chain(phrasesWithIndex);

  const piano = await getPiano(samples);

  const schedule = ({ destination }) => {
    piano.connect(destination);

    const schedulePhrase = () => {
      const phrase = chain.walk();
      phrase.forEach(str => {
        const [t, ...names] = str.split(DELIMITER);
        const parsedT = Number.parseInt(t, 10);
        names.forEach(name => {
          const waitTime = parsedT * EIGHTH_NOTE_INTERVAL_S;
          piano.triggerAttack(
            name,
            `+${waitTime + 1 + window.generativeMusic.rng() * 0.05 - 0.025}`
          );
        });
      });
    };
    Tone.Transport.scheduleRepeat(
      schedulePhrase,
      phraseLength * EIGHTH_NOTE_INTERVAL_S
    );
    return () => {
      piano.releaseAll(0);
    };
  };

  const deactivate = () => {
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['aisatsana'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/get-random-element.js<|end_filename|>
import getRandomNumberBetween from './get-random-number-between';

const pickRandomElement = (arr = []) =>
  arr[Math.floor(getRandomNumberBetween(0, arr.length))];

export default pickRandomElement;


<|start_filename|>packages/piece-bhairav/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  transpose,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../bhairav.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const ragaBhairav = [
  [0, 2],
  [4, 2],
  [5, 2],
  [7, 2],
  [4, 2],
  [5, 2],
  [8, 4],
  [11, 2],
  [12, 4],
  [8, 2],
  [11, 2],
  [8, 2],
  [7, 2],
  [5, 2],
  [7, 2],
  [4, 2],
  [1, 2],
  [0, 4],
];

function* makeRagaGenerator(raga) {
  function getNewPhrase() {
    const phrase = [];
    let noteIdx = Math.floor(window.generativeMusic.rng() * (raga.length - 4));
    const maxPhraseLength = Math.ceil(window.generativeMusic.rng() * 12);
    const divisor = window.generativeMusic.rng() + 0.5;
    for (
      let count = 0;
      phrase.length < maxPhraseLength &&
      noteIdx < raga.length &&
      noteIdx >= 0 &&
      (phrase.length > 1 || window.generativeMusic.rng() < 0.95);
      count += 1
    ) {
      const [interval, time] = raga[noteIdx];

      const multiplier =
        phrase.length === maxPhraseLength - 1 || noteIdx === raga.length - 1
          ? 4
          : 1;

      phrase.push([interval, (time / divisor) * multiplier]);

      const step = Math.ceil(window.generativeMusic.rng() * 3);
      noteIdx += window.generativeMusic.rng() < 0.2 ? -step : step;
    }
    return phrase;
  }

  let phrase;

  while (true) {
    if (
      typeof phrase === 'undefined' ||
      phrase.length <= 1 ||
      window.generativeMusic.rng() < 0.5
    ) {
      phrase = getNewPhrase();
    }
    for (const note of phrase) {
      yield note;
    }
  }
}

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const getReverb = () =>
    new Tone.Reverb(15)
      .toDestination()
      .set({ wet: 0.6 })
      .generate();

  const piano = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'bhairav__vsco2-piano-mf',
    getDestination: getReverb,
    notes: toss([0, 4, 7].map(transpose('C#')), [4, 5]),
    onProgress: val => onProgress(val * 0.5),
  });

  const cellos = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-cellos-susvib-mp',
    renderedInstrumentName: 'bhairav__vsco2-cellos-susvib-mp',
    getDestination: getReverb,
    notes: toss(['C#', 'G#'], [1, 2]),
    onProgress: val => onProgress(val * 0.5 + 0.5),
  });
  cellos.set({ attack: 2, curve: 'linear' });

  let tonic = window.generativeMusic.rng() < 0.5 ? 'C#4' : 'C#5';

  const playNote = (note, time = 0, velocity = 1) =>
    piano.triggerAttack(note, `+${1 + time}`, velocity);

  const celloDrone = note => {
    cellos.triggerAttack(note, '+1');
    Tone.Transport.scheduleOnce(() => {
      celloDrone(note);
    }, `+${window.generativeMusic.rng() * 10 + 5}`);
  };

  const playRaga = ragaGenerator => {
    const { value } = ragaGenerator.next();
    const [interval, time] = value;
    const note = transpose(tonic, interval);
    playNote(note);
    if (window.generativeMusic.rng() < (interval === 0 || interval === 12 ? 0.5 : 0.1)) {
      const lowNote = window.generativeMusic.rng() < 0.5 ? 'C#3' : transpose(note, -12);
      playNote(lowNote);
    }
    Tone.Transport.scheduleOnce(() => {
      if (time > 8 && window.generativeMusic.rng() < 0.4) {
        tonic = tonic === 'C#4' ? 'C#5' : 'C#4';
      }
      playRaga(ragaGenerator);
    }, `+${time + window.generativeMusic.rng() - 0.5}`);
  };

  const schedule = ({ destination }) => {
    piano.connect(destination);

    const ragaGenerator = makeRagaGenerator(ragaBhairav);

    Tone.Transport.scheduleOnce(() => {
      playRaga(ragaGenerator);
    }, `+${window.generativeMusic.rng() * 2 + 2}`);

    const celloFilter = new Tone.AutoFilter(
      window.generativeMusic.rng() / 100 + 0.01,
      50,
      3
    ).connect(destination);
    celloFilter.start();
    cellos.connect(celloFilter);

    ['C#2', 'C#1', 'G#1', 'G#2'].forEach(note => {
      Tone.Transport.scheduleOnce(() => {
        celloDrone(note);
      }, `+${window.generativeMusic.rng() * 5}`);
    });

    return () => {
      piano.releaseAll();
      cellos.releaseAll();
      celloFilter.dispose();
    };
  };

  const deactivate = () => {
    piano.dispose();
    cellos.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['bhairav'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-spring-again/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  createSampler,
  wrapActivate,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../spring-again.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const OCTAVES = [3, 4, 5];
const NOTES = toss(['C', 'D', 'E', 'F', 'G', 'A', 'B'], OCTAVES);

const MELODY_DISTANCE = 3;
const melodyFromNote = (
  note = NOTES[Math.floor(window.generativeMusic.rng() * NOTES.length)]
) => {
  const index = NOTES.findIndex(n => n === note);
  const min = Math.max(index - MELODY_DISTANCE, 0);
  const max = Math.min(index + MELODY_DISTANCE + 1, NOTES.length);
  return NOTES[Math.floor(window.generativeMusic.rng() * (max - min) + min)];
};

const repeat = (fn, interval) => {
  const schedule = time => {
    Tone.Transport.scheduleOnce((...args) => {
      fn(...args);
      schedule(`+${interval}`);
    }, time);
  };
  schedule();
};

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await createPrerenderableSampler({
    notes: NOTES.filter((_, i) => i % 2 === 0),
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'spring-again__vsco2-piano-mf',
    getDestination: () =>
      Promise.resolve(
        new Tone.Freeverb({
          roomSize: 0.7,
          dampening: 6000,
        }).toDestination()
      ),
    onProgress,
  });
  const [violins, cello] = await Promise.all([
    createSampler(samples['vsco2-violins-susvib']),
    createSampler(samples['vsco2-cello-susvib-f']),
  ]);
  const volume = new Tone.Volume(-5);

  const schedule = ({ destination }) => {
    volume.connect(destination);
    const delay = new Tone.FeedbackDelay({
      feedback: 0.5,
      delayTime: 0.44,
    }).connect(volume);
    piano.connect(delay);
    const playableNotes = NOTES.filter(
      ([pc, octave]) => octave < 5 || pc === 'C'
    );
    const phrase = [
      playableNotes[Math.floor(window.generativeMusic.rng() * playableNotes.length)],
    ];
    for (let i = 1; i < window.generativeMusic.rng() * 5 + 5; i += 1) {
      phrase.push(melodyFromNote(phrase[phrase.length - 1]));
    }

    const notesPerSecond = window.generativeMusic.rng() * 2 + 5;

    repeat(() => {
      phrase.forEach((note, i) => {
        piano.triggerAttack(note, `+${i / notesPerSecond}`);
      });
    }, phrase.length / notesPerSecond);
    repeat(() => {
      const index = Math.floor(window.generativeMusic.rng() * phrase.length);
      phrase[index] = melodyFromNote(phrase[index]);
    }, (phrase.length / notesPerSecond) * 2);
    violins.connect(volume);
    violins.volume.value = -2;
    cello.connect(volume);
    cello.volume.value = -8;
    repeat(() => {
      if (window.generativeMusic.rng() < 0.9) {
        const note = phrase[Math.floor(window.generativeMusic.rng() * phrase.length)];
        violins.triggerAttack(note);
        const [pc] = note;
        if (window.generativeMusic.rng() < 0.2) {
          cello.triggerAttack(`${pc}${1}`);
        }
      }
    }, (phrase.length * 5) / notesPerSecond);

    return () => {
      [piano, violins, cello].forEach(sampler => {
        sampler.releaseAll(0);
      });
      delay.dispose();
    };
  };

  const deactivate = () => {
    [piano, violins, cello, volume].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['spring-again'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-soundtrack/soundtrack.gfm.manifest.json<|end_filename|>
{
  "title": "Soundtrack",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "soundtrack",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "electronic",
    "drone",
    "dark"
  ],
  "releaseDate": "2019-10-06T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "soundtrack__vsco2-cellos-susvib-mp",
      "vsco2-cellos-susvib-mp"
    ],
    [
      "soundtrack__vsco2-glock",
      "vsco2-glock"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/soundtrack-excerpts"
}

<|start_filename|>packages/piece-impact/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createSampler,
  minor7th,
  wrapActivate,
  getRandomNumberBetween,
  getRandomElement,
  createReverseSampler,
} from '@generative-music/utilities';
import arpeggiateOnce from './arpeggiate-once';
import { sampleNames } from '../impact.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const TONIC = 'A#';
const CHORD = minor7th(TONIC);
const REVERSE_OCTAVES = [1, 2];
const REGULAR_OCTAVES = [4, 5];
const REGULAR_ARPEGGIATE_MIN_TIME = 0.5;
const REGULAR_ARPEGGIATE_MAX_TIME = 2;
const EXTRA_NOTE_CHANCE_P = 0.3;
const EXTRA_NOTE_VELOCITY = 0.3;
const EXTRA_CHORD_CHANCE_P = 0.2;

const makeNextNote = (
  reverseInstrument,
  regularInstrument,
  durationsByMidi
) => {
  const nextNote = () => {
    const note = getRandomElement(CHORD);
    const reverseNote = `${note}${getRandomElement(REVERSE_OCTAVES)}`;
    const inversion = Math.floor(getRandomNumberBetween(0, 4));
    const regularOctave = getRandomElement(REGULAR_OCTAVES);
    const regularNotes = minor7th(`${note}${regularOctave}`, inversion);
    Tone.Transport.scheduleOnce(() => {
      reverseInstrument.triggerAttack(reverseNote);
      //eslint-disable-next-line new-cap
      const midi = Tone.Frequency(reverseNote).toMidi();
      if (!durationsByMidi.has(midi)) {
        const [bufferSource] = reverseInstrument._activeSources.get(midi);
        const duration =
          bufferSource.buffer.duration / bufferSource.playbackRate.value;
        durationsByMidi.set(midi, duration);
      }
      Tone.Transport.scheduleOnce(() => {
        arpeggiateOnce({
          instrument: regularInstrument,
          notes: regularNotes,
          withinTime: getRandomNumberBetween(
            REGULAR_ARPEGGIATE_MIN_TIME,
            REGULAR_ARPEGGIATE_MAX_TIME
          ),
        });
        nextNote();
        if (window.generativeMusic.rng() < EXTRA_NOTE_CHANCE_P) {
          const extraNote = getRandomElement(regularNotes);
          Tone.Transport.scheduleOnce(() => {
            regularInstrument.triggerAttack(
              extraNote,
              Tone.now(),
              EXTRA_NOTE_VELOCITY
            );
          }, `+${durationsByMidi.get(midi) / 4}`);
        } else if (window.generativeMusic.rng() < EXTRA_CHORD_CHANCE_P) {
          let extraChordOctave = regularOctave;
          while (
            REGULAR_OCTAVES.length > 1 &&
            extraChordOctave === regularOctave
          ) {
            extraChordOctave = getRandomElement(REGULAR_OCTAVES);
          }
          const extraNotes = minor7th(`${note}${extraChordOctave}`, inversion);
          Tone.Transport.scheduleOnce(() => {
            arpeggiateOnce({
              instrument: regularInstrument,
              notes: extraNotes,
              withinTime: getRandomNumberBetween(
                REGULAR_ARPEGGIATE_MIN_TIME,
                REGULAR_ARPEGGIATE_MAX_TIME
              ),
              velocity: EXTRA_NOTE_VELOCITY,
            });
          }, `+${durationsByMidi.get(midi) / 4}`);
        }
      }, `+${durationsByMidi.get(midi)}`);
    }, `+1`);
  };
  return nextNote;
};

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const [regularInstrument, reverseInstrument] = await Promise.all([
    createSampler(samples['vsco2-piano-mf']),
    createReverseSampler(samples['vsco2-piano-mf']),
  ]);

  const durationsByMidi = new Map();
  const nextNote = makeNextNote(
    reverseInstrument,
    regularInstrument,
    durationsByMidi
  );

  const schedule = ({ destination }) => {
    [reverseInstrument, regularInstrument].forEach(instrument =>
      instrument.connect(destination)
    );
    nextNote();
    return () =>
      [regularInstrument, reverseInstrument].forEach(instrument =>
        instrument.releaseAll(0)
      );
  };

  const deactivate = () => {
    [regularInstrument, reverseInstrument].forEach(instrument =>
      instrument.dispose()
    );
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['impact'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-420hz-gamma-waves-for-big-brain/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffer,
  createPitchShiftedSampler,
  wrapActivate,
  getRandomElement,
  getPitchClass,
  getOctave,
} from '@generative-music/utilities';
import { sampleNames } from '../420hz-gamma-waves-for-big-brain.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const getRandomPhase = () => window.generativeMusic.rng() * 360;

const MAX_DRONE_GAIN = 0.4;

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const stereoWidener = new Tone.StereoWidener();
  const crossFade = new Tone.CrossFade().connect(stereoWidener);
  const pianoGain = new Tone.Gain().connect(stereoWidener);

  const chords = [
    ['G3', 'C4', 'E4'],
    ['E4', 'A4', 'C5'],
    ['D4', 'G4', 'B4'],
    ['C4', 'F4', 'G4'],
  ];

  const [waveBuffer, pitchShiftPiano, ...droneStacks] = await Promise.all([
    createBuffer(samples.waves[0]),
    createPitchShiftedSampler({
      samplesByNote: samples['vsco2-piano-mf'],
      pitchShift: -24,
    }),
    ...chords.map(async chord => {
      const gain = new Tone.Gain(0);
      const sampler = await createPitchShiftedSampler({
        samplesByNote: samples['sso-chorus-male'],
        pitchShift: -24,
      });
      sampler.connect(gain);
      return [chord, gain, sampler];
    }),
  ]);

  pitchShiftPiano.connect(pianoGain);

  const activeSources = [];

  const loopWaves = () => {
    const playbackRate = 0.5;
    const source = new Tone.BufferSource(waveBuffer)
      .set({
        fadeIn: 5,
        fadeOut: 5,
        curve: 'linear',
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
        playbackRate,
      })
      .connect(crossFade.b);
    activeSources.push(source);
    source.start('+1', 0, waveBuffer.duration / playbackRate - 5);

    Tone.Transport.scheduleOnce(() => {
      loopWaves();
    }, `+${waveBuffer.duration / playbackRate - 5}`);
  };

  const drone = (chorus, note, min, max) => {
    chorus.triggerAttack(note, '+1');

    Tone.Transport.scheduleOnce(() => {
      drone(chorus, note, min, max);
    }, `+${window.generativeMusic.rng() * (max - min) + min}`);
  };

  let unmutedDroneGain = getRandomElement(droneStacks)[1];
  unmutedDroneGain.gain.setValueAtTime(MAX_DRONE_GAIN, Tone.now());

  const changeChord = () => {
    const transitionTime = Math.ceil(window.generativeMusic.rng() * 10 + 10);
    const mutedDroneStacks = droneStacks.filter(
      ([, gain]) => gain !== unmutedDroneGain
    );
    const nextUnmutedDroneStack = getRandomElement(mutedDroneStacks);
    unmutedDroneGain.gain.cancelScheduledValues(Tone.now());
    unmutedDroneGain.gain.setValueAtTime(
      unmutedDroneGain.gain.value,
      Tone.now()
    );
    unmutedDroneGain.gain.linearRampToValueAtTime(0, `+${transitionTime}`);
    unmutedDroneGain = nextUnmutedDroneStack[1];
    unmutedDroneGain.gain.setValueAtTime(0, Tone.now());
    unmutedDroneGain.gain.linearRampToValueAtTime(
      MAX_DRONE_GAIN,
      `+${transitionTime}`
    );

    const nextChordChangeDelay = window.generativeMusic.rng() * 10 + 10;

    const [unmutedDroneChord] = nextUnmutedDroneStack;

    const date = new Date();
    const minutes = date.getMinutes();
    const hours = date.getHours();
    const playChance = (minutes / 60) % 1;
    const octaveChange = hours <= 6 || hours >= 18 ? 2 : 1;
    unmutedDroneChord
      .filter(() => window.generativeMusic.rng() < playChance)
      .forEach(note => {
        const noteTime =
          window.generativeMusic.rng() * (nextChordChangeDelay + transitionTime / 2) +
          transitionTime / 2;
        const pc = getPitchClass(note);
        const oct = getOctave(note);
        pitchShiftPiano.triggerAttack(
          `${pc}${oct + octaveChange}`,
          `+${noteTime}`
        );
      });

    Tone.Transport.scheduleOnce(() => {
      changeChord();
    }, `+${transitionTime + nextChordChangeDelay}`);
  };

  const schedule = ({ destination }) => {
    stereoWidener.connect(destination);

    const pianoGainLfo = new Tone.LFO(window.generativeMusic.rng() / 10000 + 0.0001).set({
      phase: getRandomPhase(),
    });
    pianoGainLfo.connect(pianoGain.gain);
    pianoGainLfo.start();

    const crossFadeLfo = new Tone.LFO(window.generativeMusic.rng() / 10000 + 0.0001).set({
      phase: 90,
    });
    crossFadeLfo.connect(crossFade.fade);
    crossFadeLfo.start();

    const stereoLfo = new Tone.LFO(window.generativeMusic.rng() / 100 + 0.01, 0.5, 1).set({
      phase: getRandomPhase(),
    });
    stereoLfo.connect(stereoWidener.width);
    stereoLfo.start();

    const autoLowPass = new Tone.AutoFilter(window.generativeMusic.rng() / 100 + 0.01, 100, 1)
      .set({ filter: { rolloff: -96 } })
      .connect(crossFade.a);
    autoLowPass.start();

    Tone.Transport.scheduleOnce(() => {
      changeChord();
    }, `+${window.generativeMusic.rng() * 10 + 10}`);

    droneStacks.forEach(([chord, volume, sampler]) => {
      volume.connect(autoLowPass);
      chord.forEach(note => {
        drone(sampler, note, 20, 10);
      });
    });

    loopWaves();

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
      pitchShiftPiano.releaseAll(0);
      droneStacks.forEach(([, gainNode, sampler]) => {
        gainNode.gain.cancelScheduledValues(Tone.now());
        gainNode.gain.setValueAtTime(0, Tone.now());
        sampler.releaseAll(0);
      });
      [pianoGainLfo, crossFadeLfo, stereoLfo, autoLowPass].forEach(node => {
        node.dispose();
      });
    };
  };

  const deactivate = () => {
    [
      stereoWidener,
      crossFade,
      pianoGain,
      waveBuffer,
      pitchShiftPiano,
      ...droneStacks.reduce(
        (disposableNodes, [, gain, sampler]) =>
          disposableNodes.concat([gain, sampler]),
        []
      ),
    ].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['420hz-gamma-waves-for-big-brain'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/get-random-number-between.js<|end_filename|>
const getRandomNumberBetween = (min, max) => window.generativeMusic.rng() * (max - min) + min;

export default getRandomNumberBetween;


<|start_filename|>packages/piece-otherness/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import { createSampler, wrapActivate } from '@generative-music/utilities';
import { sampleNames } from '../otherness.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const PITCH_CLASSES = [
  'C',
  'C#',
  'Db',
  'D',
  'D#',
  'Eb',
  'E',
  'F',
  'F#',
  'Gb',
  'G',
  'G#',
  'Ab',
  'A',
  'A#',
  'Bb',
  'B',
];
const OCTAVES = [2, 3, 4];
const notes = OCTAVES.reduce(
  (allNotes, octave) =>
    allNotes.concat(PITCH_CLASSES.map(pitchClass => `${pitchClass}${octave}`)),
  []
);

const playNote = (instrument, sineSynth, lastNoteMidi) => {
  const newNotes = notes.filter(n => Tone.Midi(n).toMidi() !== lastNoteMidi);
  const note = newNotes[Math.floor(window.generativeMusic.rng() * newNotes.length)];
  instrument.triggerAttack(note, '+1.5');
  const pitchClass = note.slice(0, -1);
  sineSynth.triggerAttackRelease(`${pitchClass}1`, 5, '+1.5');
  Tone.Transport.scheduleOnce(() => {
    playNote(instrument, sineSynth, Tone.Midi(note).toMidi());
  }, `+${window.generativeMusic.rng() * 10 + 10}`);
};

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const instrument = await createSampler(samples.otherness);
  const volume = new Tone.Volume(-5);
  instrument.connect(volume);

  const schedule = ({ destination }) => {
    volume.connect(destination);
    const sineSynth = new Tone.MonoSynth({
      oscillator: { type: 'sine' },
      envelope: {
        attack: 3,
        release: 10,
      },
    }).connect(volume);

    sineSynth.volume.value = -25;
    instrument.volume.value = -5;

    playNote(instrument, sineSynth);

    return () => {
      sineSynth.dispose();
      instrument.releaseAll(0);
    };
  };

  const deactivate = () => {
    instrument.dispose();
    volume.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['otherness'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-a-viable-system/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../a-viable-system.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const MAX_STEP_DISTANCE = 3;
const MAX_PHRASE_LENGTH = 3;
const PHRASE_P_BASE = 0.5;

const getNextNotesForNote = (notes, note) => {
  const index = notes.findIndex(n => n === note);
  const lowestIndex = Math.max(0, index - MAX_STEP_DISTANCE);
  return notes
    .slice(lowestIndex, index)
    .concat(notes.slice(index + 1, index + MAX_STEP_DISTANCE + 1));
};

const generatePhrase = (
  notes,
  phrase = [notes[Math.floor(window.generativeMusic.rng() * notes.length)]]
) => {
  if (
    phrase.length < MAX_PHRASE_LENGTH &&
    window.generativeMusic.rng() < PHRASE_P_BASE ** phrase.length
  ) {
    const lastNote = phrase[phrase.length - 1];
    const possibleNextNotes = getNextNotesForNote(notes, lastNote);
    return generatePhrase(
      notes,
      phrase.concat([
        possibleNextNotes[Math.floor(window.generativeMusic.rng() * possibleNextNotes.length)],
      ])
    );
  }
  return phrase;
};

const instrumentConfigs = {
  'vsco2-piano-mf': {
    isSingleNote: false,
    secondsBetweenNotes: 2,
    notes: [
      'C2',
      'D2',
      'E2',
      'F2',
      'G2',
      'A2',
      'B2',
      'C3',
      'D3',
      'E3',
      'F3',
      'G3',
      'A3',
      'B3',
      'C4',
      'D4',
      'E4',
      'F4',
      'G4',
      'A4',
      'B4',
      'C5',
      'D5',
      'E5',
      'F5',
      'G5',
      'A5',
      'B5',
      'C6',
      'D6',
      'E6',
      'F6',
      'G6',
      'A6',
      'B6',
    ],
  },
  'vsco2-contrabass-susvib': {
    isSingleNote: true,
    notes: [
      'G0',
      'A0',
      'B0',
      'C1',
      'D1',
      'E1',
      'F1',
      'G1',
      'A1',
      'B1',
      'C2',
      'D2',
      'E2',
      'F2',
      'G2',
      'A2',
      'B2',
    ],
  },
  'vsco2-violin-arcvib': {
    isSingleNote: false,
    secondsBetweenNotes: 8,
    notes: [
      'G3',
      'A3',
      'B3',
      'C4',
      'D4',
      'E4',
      'F4',
      'G4',
      'A4',
      'B4',
      'C5',
      'D5',
      'E5',
      'F5',
      'G5',
      'A5',
      'B5',
      'C6',
      'D6',
      'E6',
      'F6',
      'G6',
      'A6',
      'B6',
      'C7',
    ],
  },
};

const startInstrument = (instrument, instrumentConfig) => {
  const { isSingleNote, secondsBetweenNotes, notes } = instrumentConfig;
  const playPhrase = () => {
    const phrase = generatePhrase(notes);
    if (isSingleNote) {
      instrument.triggerAttack(phrase[0], `+1`);
    } else {
      phrase.forEach((note, i) => {
        instrument.triggerAttack(note, `+${i * secondsBetweenNotes + 1}`);
      });
    }
  };

  Tone.Transport.scheduleRepeat(() => {
    playPhrase();
  }, window.generativeMusic.rng() * 10 + 10);
};

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const instrumentNames = Object.keys(instrumentConfigs);
  const getPrerenderableeDestination = () =>
    Promise.resolve(new Tone.Freeverb({ roomSize: 0.6 }).toDestination());
  const instruments = [];
  for (let i = 0; i < instrumentNames.length; i += 1) {
    const instrumentName = instrumentNames[i];
    const { notes } = instrumentConfigs[instrumentName];
    //eslint-disable-next-line no-await-in-loop
    const sampler = await createPrerenderableSampler({
      notes: notes.filter((_, noteIndex) => noteIndex % 2 === 0),
      samples,
      sampleLibrary,
      additionalRenderLength: 1,
      sourceInstrumentName: instrumentName,
      renderedInstrumentName: `a-viable-system__${instrumentName}`,
      getDestination: getPrerenderableeDestination,
      onProgress: val => onProgress((1 / instrumentNames.length) * (val + i)),
    });
    instruments.push(sampler);
  }

  const schedule = ({ destination }) => {
    const delayTime = 10 + window.generativeMusic.rng() * 2;
    const delay = new Tone.FeedbackDelay({
      delayTime,
      feedback: 0.3 + window.generativeMusic.rng() / 30,
      wet: 0.5,
      maxDelay: delayTime,
    });
    delay.connect(destination);
    instruments.forEach((instrument, i) => {
      instrument.connect(delay);
      startInstrument(instrument, instrumentConfigs[instrumentNames[i]]);
    });

    return () => {
      instruments.forEach(instrument => {
        instrument.releaseAll(0);
      });
      delay.dispose();
    };
  };

  const deactivate = () => {
    instruments.forEach(instrument => {
      instrument.dispose();
    });
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['a-viable-system'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-pinwheels/src/piece.js<|end_filename|>
import shuffle from 'shuffle-array';
import * as Tone from 'tone';
import {
  createSampler,
  getRandomElement,
  getRandomNumberBetween,
  wrapActivate,
  minor7th,
  invert,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../pinwheels.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const P_SPAWN_TWO = 0.33;
const OCTAVES = [3, 4, 5];
const PITCH_CLASSES = [
  'C',
  'C#',
  'D',
  'D#',
  'E',
  'F',
  'F#',
  'G',
  'G#',
  'A',
  'A#',
  'B',
];
const TONICS = toss(PITCH_CLASSES, OCTAVES);
const MIN_MAX_DELAY_S = 2;
const MAX_MAX_DELAY_S = 5;
const MIN_MIN_DELAY_S = 0.075;
const MAX_MIN_DELAY_S = 0.3;
const MAX_INVERSION = 3;
const MIN_ACCELERATION_MULTIPLIER = 0.85;
const MAX_ACCELERATION_MULTIPLIER = 0.95;
const MIN_DECELERATION_MULTIPLIER = 1.05;
const MAX_DECELERATION_MULTIPLIER = 1.15;

function* makeArrayLooper(arr) {
  for (let i = 0; i < arr.length; i === arr.length - 1 ? (i = 0) : (i += 1)) {
    yield arr[i];
  }
}

const getNewMaxDelay = () =>
  getRandomNumberBetween(MIN_MAX_DELAY_S, MAX_MAX_DELAY_S);

const startPinwheelChain = instrument => {
  const generatePinwheel = (
    tonic = getRandomElement(TONICS),
    maxDelay = getNewMaxDelay(),
    spawnAnother = true
  ) => {
    const inversion = Math.floor(0, MAX_INVERSION + 1);
    const notes = invert(minor7th(tonic), inversion);
    const noteGenerator = makeArrayLooper(shuffle(notes));
    const minDelay = getRandomNumberBetween(MIN_MIN_DELAY_S, MAX_MIN_DELAY_S);
    const playNextNote = (delay, multiplier) => {
      Tone.Transport.scheduleOnce(() => {
        instrument.triggerAttack(noteGenerator.next().value, '+1');
        const nextDelay = delay * multiplier;
        if (nextDelay < minDelay) {
          playNextNote(
            nextDelay,
            getRandomNumberBetween(
              MIN_DECELERATION_MULTIPLIER,
              MAX_DECELERATION_MULTIPLIER
            )
          );
        } else if (nextDelay > maxDelay) {
          if (spawnAnother) {
            Tone.Transport.scheduleOnce(() => {
              if (window.generativeMusic.rng() < P_SPAWN_TWO) {
                const nextPitchClass = getRandomElement(PITCH_CLASSES);
                const shuffledOctaves = shuffle(OCTAVES);
                const delay1 = getNewMaxDelay();
                const delay2 = getNewMaxDelay();
                generatePinwheel(
                  `${nextPitchClass}${shuffledOctaves.pop()}`,
                  delay1,
                  delay1 >= delay2
                );
                generatePinwheel(
                  `${nextPitchClass}${shuffledOctaves.pop()}`,
                  delay2,
                  delay1 < delay2
                );
              } else {
                generatePinwheel();
              }
            }, `+${getNewMaxDelay()}`);
          }
        } else {
          playNextNote(nextDelay, multiplier);
        }
      }, `+${delay}`);
    };
    playNextNote(
      maxDelay,
      getRandomNumberBetween(
        MIN_ACCELERATION_MULTIPLIER,
        MAX_ACCELERATION_MULTIPLIER
      )
    );
  };
  generatePinwheel();
};

const getPiano = samples => createSampler(samples['vsco2-piano-mf']);

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await getPiano(samples);
  const schedule = ({ destination }) => {
    piano.connect(destination);
    startPinwheelChain(piano);
    return () => {
      piano.releaseAll(0);
    };
  };
  const deactivate = () => {
    piano.dispose();
  };
  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['pinwheels'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-drones/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffers,
  wrapActivate,
  sampleNote,
} from '@generative-music/utilities';
import { sampleNames } from '../drones.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const NOTES = ['C4', 'G4', 'E4'];

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const instrumentBuffers = await Promise.all(
    sampleNames.map(instrumentName => createBuffers(samples[instrumentName]))
  );

  const schedule = ({ destination }) => {
    const disposableNodes = [];

    instrumentBuffers.forEach((buffers, i) => {
      const instrumentName = sampleNames[i];
      const samplesByNote = samples[instrumentName];
      const drone = (
        note,
        droneDestination,
        pitchShift = 0,
        reverse = false
      ) => {
        const { sampledNote, playbackRate } = sampleNote({
          note,
          pitchShift,
          sampledNotes: Object.keys(samplesByNote),
        });
        const buffer = buffers.get(sampledNote);
        const source = new Tone.BufferSource(buffer)
          .set({
            reverse,
            playbackRate,
            onended: () => {
              const index = disposableNodes.indexOf(source);
              if (index >= 0) {
                disposableNodes.splice(index, 1);
              }
            },
          })
          .connect(droneDestination);
        source.start('+1');
      };
      const autoFilter = new Tone.AutoFilter(
        window.generativeMusic.rng() / 10,
        150,
        4
      )
        .connect(destination)
        .start();

      const lfoMin = window.generativeMusic.rng() / 100;
      const lfoMax = lfoMin * 10;

      const frequencyLfo = new Tone.LFO({ min: lfoMin, max: lfoMax });

      frequencyLfo.connect(autoFilter.frequency);
      frequencyLfo.start();

      const lastGain = new Tone.Gain();
      lastGain.connect(autoFilter);

      const lastGainLfo = new Tone.LFO({
        frequency: window.generativeMusic.rng() / 100,
        phase: 90,
      });
      lastGainLfo.connect(lastGain.gain);
      lastGainLfo.start();

      NOTES.forEach((note, noteIndex) => {
        const playDrone = () => {
          if (noteIndex === NOTES.length - 1) {
            drone(note, lastGain, -36);
          }
          drone(note, autoFilter, -36);
          Tone.Transport.scheduleOnce(() => {
            playDrone();
          }, `+${window.generativeMusic.rng() * 20 + 40}`);
        };
        playDrone();
      });

      disposableNodes.push(autoFilter, frequencyLfo, lastGainLfo, lastGain);
    });

    return () => {
      disposableNodes.forEach(node => {
        node.dispose();
      });
    };
  };

  const deactivate = () => {
    instrumentBuffers.forEach(buffers => {
      buffers.dispose();
    });
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['drones'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-moment/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  getPitchClass,
  getOctave,
} from '@generative-music/utilities';
import { sampleNames } from '../moment.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const NOTES = ['C2', 'E2', 'G2', 'C3', 'E3', 'G3', 'C4', 'E4', 'G4'];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const basePrerenderableOpts = {
    samples,
    sampleLibrary,
    notes: NOTES,
    getDestination: () =>
      new Tone.Reverb(10)
        .set({ wet: 0.5 })
        .toDestination()
        .generate(),
  };

  const guitar = await createPrerenderableSampler(
    Object.assign({}, basePrerenderableOpts, {
      sourceInstrumentName: 'acoustic-guitar',
      renderedInstrumentName: 'moment__acoustic-guitar',
      onProgress: val => onProgress(val * 0.33),
    })
  );

  const hum1 = await createPrerenderableSampler(
    Object.assign({}, basePrerenderableOpts, {
      sourceInstrumentName: 'alex-hum-1',
      renderedInstrumentName: 'moment__alex-hum-1',
      onProgress: val => onProgress(val * 0.33 + 0.33),
    })
  );

  const hum2 = await createPrerenderableSampler(
    Object.assign({}, basePrerenderableOpts, {
      sourceInstrumentName: 'alex-hum-1',
      renderedInstrumentName: 'moment__alex-hum-2',
      onProgress: val => onProgress(val * 0.33 + 0.66),
    })
  );

  const compressor = new Tone.Compressor();
  const humVolume = new Tone.Volume(-15).connect(compressor);

  [hum1, hum2].forEach(humSampler => {
    humSampler.set({
      attack: 3,
      release: 3,
      curve: 'linear',
    });
    humSampler.connect(humVolume);
  });

  const lastHumTime = new Map();
  const playHums = note => {
    const now = Tone.now();
    if (!lastHumTime.has(note) || now - lastHumTime.get(note) > 30) {
      [hum1, hum2].forEach(humSampler =>
        humSampler.triggerAttackRelease(note, window.generativeMusic.rng() + 4)
      );
      lastHumTime.set(note, now);
    }
  };

  const schedule = ({ destination }) => {
    guitar.connect(destination);
    compressor.connect(destination);
    const firstDelays = NOTES.map(
      note =>
        window.generativeMusic.rng() *
        20 *
        (getPitchClass(note) === 'E' ? 3 : 1)
    );
    const minFirstDelay = Math.min(...firstDelays);

    NOTES.forEach((note, i) => {
      const pc = getPitchClass(note);
      const play = (
        time = (window.generativeMusic.rng() * 20 + 5) * (pc === 'E' ? 3 : 1)
      ) => {
        Tone.Transport.scheduleOnce(() => {
          const octave = getOctave(note);
          if (
            (octave === 3 || (octave === 2 && pc === 'G')) &&
            window.generativeMusic.rng() < 0.1
          ) {
            playHums(note);
          } else if (window.generativeMusic.rng() < 0.1) {
            playHums('E3');
          }
          guitar.triggerAttack(note);
          play();
        }, `+${time}`);
      };
      play(firstDelays[i] - minFirstDelay);
    });

    return () => {
      [guitar, hum1, hum2].forEach(sampler => {
        sampler.releaseAll(0);
      });
    };
  };

  const deactivate = () => {
    [guitar, hum1, hum2, compressor, humVolume].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['moment'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-timbral-oscillations/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../timbral-oscillations.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const OCTAVES = [3, 4, 5, 6];
const MAX_STEP_DISTANCE = 2;
const MAX_PHRASE_LENGTH = 3;
const PHRASE_P_BASE = 0.5;
const PITCH_CLASSES = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];

const notes = OCTAVES.reduce(
  (allNotes, octave) =>
    allNotes.concat(PITCH_CLASSES.map(pc => `${pc}${octave}`)),
  []
);

const getNextNotesForNote = note => {
  const index = notes.findIndex(n => n === note);
  const lowestIndex = Math.max(0, index - MAX_STEP_DISTANCE);
  return notes
    .slice(lowestIndex, index)
    .concat(notes.slice(index + 1, index + MAX_STEP_DISTANCE + 1));
};

const generatePhrase = (
  phrase = [notes[Math.floor(window.generativeMusic.rng() * notes.length)]]
) => {
  if (
    phrase.length < MAX_PHRASE_LENGTH &&
    window.generativeMusic.rng() < PHRASE_P_BASE ** phrase.length
  ) {
    const lastNote = phrase[phrase.length - 1];
    const possibleNextNotes = getNextNotesForNote(lastNote);
    return generatePhrase(
      phrase.concat([
        possibleNextNotes[Math.floor(window.generativeMusic.rng() * possibleNextNotes.length)],
      ])
    );
  }
  return phrase;
};

const playPhrase = piano => {
  const phrase = generatePhrase();
  phrase.forEach((note, i) => {
    piano.triggerAttack(note, `+${i * 1.5}`);
  });
  Tone.Transport.scheduleOnce(() => {
    playPhrase(piano);
  }, `+${window.generativeMusic.rng() * 10 + 10}`);
};

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const getPianoDestination = () =>
    Promise.resolve(new Tone.Freeverb().toDestination());

  const piano = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    onProgress,
    notes: notes.filter((_, i) => i % 2 === 0),
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'timbral-oscillations__vsco2-piano-mf',
    additionalRenderLength: 0,
    getDestination: getPianoDestination,
  });

  const schedule = ({ destination }) => {
    const delayFudge = window.generativeMusic.rng() * 3;
    const delay = new Tone.FeedbackDelay({
      wet: 0.5,
      delayTime: 5 + delayFudge,
      feedback: 0.8 - delayFudge / 100,
    });
    const synth = new Tone.MonoSynth({
      oscillator: { type: 'sine' },
      volume: -45,
      envelope: { release: 3, attack: 0.5 },
    }).chain(delay);

    const chorusLfo = new Tone.LFO({
      frequency: window.generativeMusic.rng() / 100,
      phase: 90,
    });
    chorusLfo.start();
    const chorus = new Tone.Chorus({ wet: 0 });
    chorusLfo.connect(chorus.wet);

    const autoFilter = new Tone.AutoFilter({
      frequency: window.generativeMusic.rng() / 100,
      baseFrequency: 250,
      octaves: 5,
      type: 'sine',
    });
    autoFilter.start();

    const pitchLfo = new Tone.LFO({
      frequency: window.generativeMusic.rng() / 100,
      phase: 90,
    });
    pitchLfo.start();
    const pitchShift = new Tone.PitchShift({ pitch: 7 });
    pitchLfo.connect(pitchShift.wet);

    const tremoloFrequencyLfo = new Tone.LFO({
      frequency: window.generativeMusic.rng() / 100,
      phase: 90,
      min: 0.1,
      max: 10,
    });
    const tremoloLfo = new Tone.LFO({
      frequency: window.generativeMusic.rng() / 100,
      phase: 90,
    });
    tremoloFrequencyLfo.start();
    tremoloLfo.start();
    const tremolo = new Tone.Tremolo();
    tremoloFrequencyLfo.connect(tremolo.frequency);
    tremoloLfo.connect(tremolo.wet);
    tremolo.start();

    const compressor = new Tone.Compressor();

    piano.chain(
      pitchShift,
      delay,
      chorus,
      autoFilter,
      tremolo,
      compressor,
      destination
    );

    playPhrase(piano);

    return () => {
      piano.releaseAll(0);
      [
        delay,
        chorusLfo,
        chorus,
        autoFilter,
        pitchLfo,
        pitchShift,
        tremoloFrequencyLfo,
        tremoloLfo,
        tremolo,
        compressor,
        synth,
      ].forEach(node => node.dispose());
    };
  };

  const deactivate = () => {
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['timbral-oscillations'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-stream-of-consciousness/stream-of-consciousness.gfm.manifest.json<|end_filename|>
{
  "title": "Stream of Consciousness",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "stream-of-consciousness",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "electronic"
  ],
  "releaseDate": "2020-02-27T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "stream-of-consciousness__snare-brush-stir",
      "snare-brush-stir"
    ],
    "snare-brush-hit-p",
    "ride-brush-p",
    [
      "stream-of-consciousness__vsco2-piano-mf-reverse",
      "vsco2-piano-mf"
    ],
    [
      "stream-of-consciousness__vsco2-piano-mf-low",
      "vsco2-piano-mf"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/stream-of-consciousness-excerpts"
}

<|start_filename|>packages/piece-ritual/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  createPrerenderableBuffers,
  wrapActivate,
  getRandomElement,
} from '@generative-music/utilities';
import { sampleNames } from '../ritual.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const createPercussionSampler = async (prerenderOptions = {}) => {
  const buffers = await createPrerenderableBuffers(prerenderOptions);
  const {
    samples,
    sourceInstrumentName,
    renderedInstrumentName,
  } = prerenderOptions;
  const instrumentSamples =
    samples[renderedInstrumentName] || samples[sourceInstrumentName];
  const activeSources = [];
  const gain = new Tone.Gain();

  const triggerAttack = time => {
    const buffer = buffers.get(
      Math.floor(window.generativeMusic.rng() * instrumentSamples.length)
    );
    const source = new Tone.BufferSource(buffer)
      .set({
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(gain);
    source.start(time);
  };

  const connect = destination => {
    gain.connect(destination);
  };

  const releaseAll = time => {
    activeSources.forEach(source => {
      source.stop(time);
    });
  };

  const dispose = () => {
    [buffers, gain, ...activeSources].forEach(node => {
      node.dispose();
    });
  };

  return {
    triggerAttack,
    connect,
    releaseAll,
    dispose,
  };
};

const violinPhrases = [['G#4', 'F4', 'F#4', 'C#4'], ['A#4', 'F#4', 'G#4']];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const didgeridooSamples =
    samples['ritual__vcsl-didgeridoo-sus'] || samples['vcsl-didgeridoo-sus'];

  const dideridooBuffers = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vcsl-didgeridoo-sus',
    renderedInstrumentName: 'ritual__vcsl-didgeridoo-sus',
    getDestination: () => new Tone.Reverb(30).toDestination().generate(),
    onProgress: val => onProgress(val / 8),
  });

  const violins = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: ['C#4', 'G#4'],
    sourceInstrumentName: 'vsco2-violins-susvib',
    renderedInstrumentName: 'ritual__vsco2-violins-susvib',
    getDestination: () => new Tone.Reverb(30).toDestination().generate(),
    onProgress: val => onProgress((1 + val) / 8),
    pitchShift: -24,
  });

  const bassdrum = await createPercussionSampler({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vcsl-bassdrum-hit-ff',
    renderedInstrumentName: 'ritual__vcsl-bassdrum-hit-ff',
    additionalRenderLength: 1,
    getDestination: () =>
      new Tone.Reverb(15)
        .set({ wet: 0.5 })
        .toDestination()
        .generate(),
    onProgress: val => onProgress((2 + val) / 8),
  });

  const darbukas = [];
  for (let id = 1; id <= 5; id += 1) {
    //eslint-disable-next-line no-await-in-loop
    const darbuka = await createPercussionSampler({
      samples,
      sampleLibrary,
      sourceInstrumentName: `vcsl-darbuka-${id}-f`,
      renderedInstrumentName: `ritual__vcsl-darbuka-${id}-f`,
      additionalRenderLength: 1,
      getDestination: () =>
        new Tone.Reverb(15)
          .set({ wet: 0.5 })
          .toDestination()
          .generate(),
      onProgress: val => onProgress((id + 2 + val) / 8),
    });
    darbukas.push(darbuka);
  }

  const activeSources = [];

  const drone = ({ destination }) => {
    const buffer = dideridooBuffers.get(
      Math.floor(window.generativeMusic.rng() * didgeridooSamples.length)
    );
    const playbackRate = window.generativeMusic.rng() < 0.9 ? 1 : 0.5;
    const source = new Tone.BufferSource(buffer)
      .set({
        fadeIn: 5,
        playbackRate,
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(destination);
    activeSources.push(source);
    source.start('+1');
    Tone.Transport.scheduleOnce(() => {
      drone({ destination });
    }, `+${1 + buffer.duration / 3 / playbackRate}`);
  };

  const playViolinPhrase = () => {
    const phrase = getRandomElement(violinPhrases);

    const totalDelay = phrase.reduce((delay, note) => {
      violins.triggerAttack(note, `+${delay}`);
      return delay + window.generativeMusic.rng() * 20 + 20;
    }, window.generativeMusic.rng() * 5);

    Tone.Transport.scheduleOnce(() => {
      playViolinPhrase();
    }, `+${totalDelay + 10}`);
  };

  const percussionGain = new Tone.Gain();
  bassdrum.connect(percussionGain);

  darbukas.forEach(darbuka => {
    darbuka.connect(percussionGain);
  });

  const percussion = (beatTime, up) => {
    bassdrum.triggerAttack('+1');
    bassdrum.triggerAttack(`+${1 + beatTime * 2}`);

    for (let i = 0; i < 32; i += 1) {
      if (i === 0 || i === 2 || window.generativeMusic.rng() < 0.3) {
        darbukas[Math.floor(window.generativeMusic.rng() * darbukas.length)].triggerAttack(
          `+${1 + beatTime * i}`
        );
      }
    }

    Tone.Transport.scheduleOnce(() => {
      if (up && beatTime > 0.6) {
        percussion(beatTime * (1 - window.generativeMusic.rng() * 0.02), false);
      } else if (!up && beatTime < 0.2) {
        percussion(beatTime * (1 + window.generativeMusic.rng() * 0.02), true);
      } else if (up) {
        percussion(beatTime * (1 + window.generativeMusic.rng() * 0.02), true);
      } else {
        percussion(beatTime * (1 - window.generativeMusic.rng() * 0.02), false);
      }
    }, `+${32 * beatTime}`);
  };

  const schedule = ({ destination }) => {
    percussionGain.connect(destination);
    violins.connect(destination);
    const percussionGainLfo = new Tone.LFO(
      window.generativeMusic.rng() / 1000 + 0.001,
      0,
      0.9
    ).set({ phase: 90 });
    percussionGainLfo.connect(percussionGain.gain);
    percussionGainLfo.start();

    drone({ destination });
    playViolinPhrase();
    percussion(window.generativeMusic.rng() * 0.4 + 0.2, true);

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
      violins.releaseAll(0);
      bassdrum.releaseAll(0);
      darbukas.forEach(darbuka => {
        darbuka.releaseAll(0);
      });

      percussionGainLfo.dispose();
    };
  };

  const deactivate = () => {
    [violins, bassdrum, percussionGain, dideridooBuffers, ...darbukas].forEach(
      node => {
        node.dispose();
      }
    );
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['ritual'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/create-prerenderable-sampled-buffers.js<|end_filename|>
import noop from './utilities/noop';
import createBuffers from './create-buffers';
import createPrerenderableSampledBuffer from './create-prerenderable-sampled-buffer';

const inProgress = new Map();

const createPrerenderableSampledBuffers = async ({
  notes,
  samples,
  sampleLibrary,
  sourceInstrumentName,
  renderedInstrumentName,
  getDestination,
  additionalRenderLength = 0,
  onProgress = noop,
  bufferSourceOptions = {},
  pitchShift = 0,
  reverse = false,
} = {}) => {
  if (samples[renderedInstrumentName]) {
    return createBuffers(samples[renderedInstrumentName]);
  }
  if (inProgress.has(renderedInstrumentName)) {
    const renderedBuffersByNote = await inProgress.get(renderedInstrumentName);
    return createBuffers(renderedBuffersByNote);
  }
  const samplesByNote = samples[sourceInstrumentName];
  const promise = Promise.all(
    notes.map(async (note, i) => {
      const buffer = await createPrerenderableSampledBuffer({
        note,
        samplesByNote,
        getDestination,
        additionalRenderLength,
        bufferSourceOptions,
        pitchShift,
        reverse,
      });
      onProgress((i + 1) / notes.length);
      return buffer;
    })
  ).then(renderedBuffers =>
    renderedBuffers.reduce((o, renderedBuffer, i) => {
      const note = notes[i];
      o[note] = renderedBuffer;
      return o;
    }, {})
  );
  inProgress.set(renderedInstrumentName, promise);
  const renderedBuffersByNote = await promise;
  sampleLibrary.save([[renderedInstrumentName, renderedBuffersByNote]]);
  inProgress.delete(renderedInstrumentName);
  return createBuffers(renderedBuffersByNote);
};

export default createPrerenderableSampledBuffers;


<|start_filename|>packages/piece-nakaii/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPitchShiftedSampler,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../nakaii.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const phraseProto = [
  ['C4'],
  ['C6'],
  ['B5'],
  ['D6', 'C6'],
  ['C6', 'B5'],
  ['A5', 'G5'],
  ['G5', 'F5'],
  ['B5', 'A5'],
  ['E5', 'G5'],
  ['C5'],
];

const getPhrase = () =>
  phraseProto.reduce((phrase, nextProtoNotes) => {
    const nextPossibleNotes = nextProtoNotes.filter(
      note => note !== phrase[phrase.length - 1]
    );
    return phrase.concat(
      nextPossibleNotes[Math.floor(window.generativeMusic.rng() * nextPossibleNotes.length)]
    );
  }, []);

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const violinGain = new Tone.Gain(0);

  const piano = await createPitchShiftedSampler({
    samplesByNote: samples['vsco2-piano-mf'],
    pitchShift: -24,
  });

  const violins = await createPitchShiftedSampler({
    samplesByNote: samples['vsco2-violins-susvib'],
    pitchShift: -36,
  });

  violins.connect(violinGain);

  const playRandomPhrase = () => {
    let phrase = getPhrase();
    if (window.generativeMusic.rng() < 0.5) {
      phrase = phrase.map(
        note => `${note[0]}${Number.parseInt(note[1], 10) + 1}`
      );
    }
    const multiplier = window.generativeMusic.rng() + 1.75;
    phrase.forEach((note, i) => {
      const offset = window.generativeMusic.rng() * 0.1 - 0.05 + 1;
      if (i <= 2) {
        piano.triggerAttack(note, `+${i * multiplier + offset}`);
      } else if (i >= 3 && i <= 5) {
        piano.triggerAttack(
          note,
          `+${3 * multiplier + ((i - 3) * multiplier) / 3 + offset}`
        );
      } else if (i < phrase.length - 1 || window.generativeMusic.rng() < 0.95) {
        piano.triggerAttack(
          note,
          `+${4.5 * multiplier + ((i - 4.5) * multiplier) / 2 + offset}`
        );
      }
    });

    Tone.Transport.scheduleOnce(() => {
      playRandomPhrase();
    }, `+${window.generativeMusic.rng() * 5 + multiplier * phrase.length + 3}`);
  };

  const schedule = ({ destination }) => {
    violinGain.connect(destination);
    piano.connect(destination);
    const gainLfo = new Tone.LFO(0.001, 0, 1).set({
      phase: 90,
    });
    gainLfo.connect(violinGain.gain);
    gainLfo.start();

    playRandomPhrase();

    ['C4', 'G3', 'C5'].forEach(note => {
      const play = () => {
        violins.triggerAttack(note, '+1');

        Tone.Transport.scheduleOnce(() => {
          play();
        }, `+${window.generativeMusic.rng() * 30 + 30}`);
      };
      play();
    });

    return () => {
      violins.releaseAll(0);
      piano.releaseAll(0);
      gainLfo.dispose();
    };
  };

  const deactivate = () => {
    [violinGain, piano, violins].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['nakaii'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-eno-machine/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createSampler,
  wrapActivate,
  getRandomNumberBetween,
  toss,
  invert,
  major9th,
} from '@generative-music/utilities';
import { sampleNames } from '../eno-machine.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const OCTAVES = [3, 4, 5];
const MIN_REPEAT_S = 20;
const MAX_REPEAT_S = 60;
const NOTES = toss(invert(major9th('Db'), 1), OCTAVES);

const getPiano = samples => createSampler(samples['vsco2-piano-mf']);

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await getPiano(samples);

  const schedule = ({ destination }) => {
    piano.connect(destination);
    NOTES.forEach(note => {
      const interval = getRandomNumberBetween(MIN_REPEAT_S, MAX_REPEAT_S);
      const delay = getRandomNumberBetween(0, MAX_REPEAT_S - MIN_REPEAT_S);
      const playNote = () => piano.triggerAttack(note, '+1');
      Tone.Transport.scheduleRepeat(playNote, interval, `+${delay}`);
    });

    return () => {
      piano.releaseAll(0);
    };
  };

  const deactivate = () => {
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['eno-machine'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/create-buffers.js<|end_filename|>
import * as Tone from 'tone';

const createBuffers = urlMap => {
  const urls = Array.isArray(urlMap) ? urlMap : Object.values(urlMap);
  if (
    urls.every(
      url => url instanceof AudioBuffer || url instanceof Tone.ToneAudioBuffer
    )
  ) {
    return Promise.resolve(new Tone.ToneAudioBuffers(urlMap));
  }
  return new Promise(resolve => {
    const buffers = new Tone.ToneAudioBuffers(urlMap, () => {
      resolve(buffers);
    });
  });
};

export default createBuffers;


<|start_filename|>packages/utilities/src/theory/chord.js<|end_filename|>
import transpose from './transpose';

const chord = (tonic, intervals) =>
  [tonic].concat(intervals.map(transpose(tonic)));

export default chord;


<|start_filename|>packages/piece-documentary-films/documentary-films.gfm.manifest.json<|end_filename|>
{
  "title": "Documentary Films",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "documentary-films",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "drone",
    "calm"
  ],
  "releaseDate": "2019-11-24T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "documentary-films__vsco2-trumpet-sus-mf",
      "vsco2-trumpet-sus-mf"
    ],
    [
      "documentary-films__vsco2-trombone-sus-mf",
      "vsco2-trombone-sus-mf"
    ],
    [
      "documentary-films__vsco2-tuba-sus-mf",
      "vsco2-tuba-sus-mf"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/documentary-films-excerpts"
}

<|start_filename|>packages/piece-remembering/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  createPrerenderableSampler,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../remembering.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const PITCH_CLASSES = ['C', 'D', 'E', 'G', 'A', 'C'];
const getPhrase = octave => {
  const notes = toss(PITCH_CLASSES, [octave])
    .concat()
    .concat([`${PITCH_CLASSES[0]}${octave + 1}`]);
  return Array.from({ length: 4 }).map(
    () => notes[Math.floor(window.generativeMusic.rng() * notes.length)]
  );
};

const getPhrases = (octaves = [3, 4, 5, 6]) =>
  octaves.map(octave => getPhrase(octave));

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    onProgress,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'remembering__vsco2-piano-mf',
    notes: toss(
      PITCH_CLASSES.slice(0, -1).filter(pc => pc !== 'D' && pc !== 'A'),
      [3, 4, 5, 6]
    ).concat(['C7']),
    getDestination: () =>
      Promise.resolve(new Tone.Freeverb(0.5).set({ wet: 0.5 }).toDestination()),
  });

  const playPhrase = () => {
    const phrases = getPhrases();
    const divisor = window.generativeMusic.rng() * 0.15 + 0.5;
    phrases.forEach(phrase =>
      phrase.forEach((note, i) => {
        if (window.generativeMusic.rng() < 0.85) {
          piano.triggerAttack(
            note,
            `+${i / divisor + window.generativeMusic.rng() / 5 - 0.1}`
          );
        }
      })
    );

    Tone.Transport.scheduleOnce(() => {
      playPhrase();
    }, `+${phrases[0].length / divisor + window.generativeMusic.rng() * 5 + 3}`);
  };

  const schedule = ({ destination }) => {
    piano.connect(destination);
    playPhrase();

    return () => {
      piano.releaseAll(0);
    };
  };

  const deactivate = () => {
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['remembering'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-peace/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../peace.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const NOTES = ['A3', 'C4', 'D4', 'E4', 'G4', 'A4'];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const fluteVolume = new Tone.Volume(-8);

  const flute = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    onProgress,
    notes: NOTES,
    sourceInstrumentName: 'native-american-flute-susvib',
    renderedInstrumentName: 'peace__native-american-flute-susvib',
    additionalRenderLength: 1,
    getDestination: () =>
      new Tone.Reverb({ decay: 10 }).toDestination().generate(),
    pitchShift: -9,
  });

  flute.set({ fadeIn: 5, fadeOut: 5, curve: 'linear' });

  const schedule = ({ destination }) => {
    const delay = new Tone.FeedbackDelay({
      feedback: 0.7,
      delayTime: 1,
      wet: 0.3,
    }).connect(destination);
    fluteVolume.connect(delay);
    flute.connect(fluteVolume);

    const playRandom = lastNote => {
      const eligibleNotes = NOTES.filter(note => note !== lastNote);
      const randomNote =
        eligibleNotes[Math.floor(window.generativeMusic.rng() * eligibleNotes.length)];
      flute.triggerAttackRelease(randomNote, window.generativeMusic.rng() * 2 + 10, '+1');
      Tone.Transport.scheduleOnce(() => {
        playRandom(randomNote);
      }, `+${window.generativeMusic.rng() * 10 + 15}`);
    };

    playRandom();

    return () => {
      flute.releaseAll(0);
      delay.dispose();
    };
  };

  const deactivate = () => {
    flute.dispose();
    fluteVolume.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['peace'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-animalia-chordata/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffer,
  createPrerenderableBufferArray,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../animalia-chordata.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const activeSources = [];
  const filter = new Tone.Filter(500);
  const compressor = new Tone.Compressor().connect(filter);
  const crossFade = new Tone.CrossFade().connect(compressor);

  const temporaryBuffers = [];

  if (
    !samples['animalia-chordata__whales-dryer'] &&
    !samples['animalia-chordata__whales-wetter']
  ) {
    const buffer = await createBuffer(samples.whales[0]);
    const reverseBuffer = Tone.ToneAudioBuffer.fromArray(buffer.toArray());
    reverseBuffer.reverse = true;
    onProgress(0.02);
    samples.whales.push(reverseBuffer);
    temporaryBuffers.push(buffer, reverseBuffer);
  }

  const dryerBuffers = await createPrerenderableBufferArray({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'whales',
    renderedInstrumentName: 'animalia-chordata__whales-dryer',
    getDestination: () =>
      new Tone.Reverb(30)
        .set({ wet: 0.5 })
        .toDestination()
        .generate(),
    onProgress: val => onProgress(val * 0.5),
  });

  const wetterBuffers = await createPrerenderableBufferArray({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'whales',
    renderedInstrumentName: 'animalia-chordata__whales-wetter',
    getDestination: () =>
      new Tone.Reverb(30)
        .set({ wet: 0.9 })
        .toDestination()
        .generate(),
    onProgress: val => onProgress(val * 0.5 + 0.5),
  });

  temporaryBuffers.forEach(buffer => {
    buffer.dispose();
  });

  const play = () => {
    const bufferIndex = Math.round(window.generativeMusic.rng() * 0.5);
    const buffers = [dryerBuffers, wetterBuffers].map(
      bufferArray => bufferArray[bufferIndex]
    );
    const playbackRate = window.generativeMusic.rng() * 0.2 + 0.1;
    buffers.forEach(buf => {
      const source = new Tone.BufferSource(buf)
        .set({
          playbackRate,
          fadeIn: 5,
          fadeOut: 5,
          curve: 'linear',
          onended: () => {
            const i = activeSources.indexOf(source);
            if (i >= 0) {
              activeSources.splice(i, 1);
            }
          },
        })
        .connect(bufferIndex === 0 ? crossFade.a : crossFade.b);
      activeSources.push(source);
      source.start('+1', 3, buf.duration / playbackRate - 3);
    });

    Tone.Transport.scheduleOnce(() => {
      play();
    }, `+${buffers[0].duration / playbackRate - window.generativeMusic.rng() * 15 - 15}`);
  };

  const schedule = ({ destination }) => {
    const feedbackDelay = new Tone.FeedbackDelay({
      delayTime: 0.7,
      feedback: 0.8,
    });
    filter.connect(feedbackDelay);
    const lfo = new Tone.LFO(window.generativeMusic.rng() * 0.005 + 0.005).set({
      phase: 90,
    });
    lfo.connect(crossFade.fade);
    lfo.start();

    play();

    feedbackDelay.connect(destination);

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
      feedbackDelay.dispose();
      lfo.dispose();
    };
  };

  const deactivate = () => {
    [compressor, filter, crossFade, ...dryerBuffers, ...wetterBuffers].forEach(
      node => node.dispose()
    );
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['animalia-chordata'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/get-closest-note.js<|end_filename|>
// https://github.com/Tonejs/Tone.js/blob/ed0d3b08be2b95220fffe7cce7eac32a5b77580e/Tone/instrument/Sampler.ts#L183

const MAX_INTERVAL = 96;
const getClosestNote = ({ targetMidi, searchedMidiSet }) => {
  for (let interval = 0; interval <= MAX_INTERVAL; interval += 1) {
    const closestMidi = [targetMidi + interval, targetMidi - interval].find(
      midi => searchedMidiSet.has(midi)
    );
    if (typeof closestMidi !== 'undefined') {
      return closestMidi;
    }
  }
  throw new Error(`No nearby samples found for midi ${targetMidi}`);
};

export default getClosestNote;


<|start_filename|>packages/piece-trees/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  transpose,
  P1,
  M2,
  M3,
  P4,
  P5,
  M6,
  M7,
  getRandomElement,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../trees.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const MAJOR_SCALE_INTERVALS = [P1, M2, M3, P4, P5, M6, M7];
const OCTAVES = [3, 4, 5, 6];

const getOffsetProgression = () => {
  const progression = [];
  const startingStep = window.generativeMusic.rng() < 0.5 ? 0 : 1;
  const largestStep = window.generativeMusic.rng() * (5 - startingStep) + startingStep;
  for (
    let step = startingStep;
    step <= largestStep;
    step += window.generativeMusic.rng() < 0.5 ? 1 : 2
  ) {
    const chord = [];
    for (let i = step; i >= 0; i -= 2) {
      if (i === 0) {
        chord.push(i);
      } else {
        chord.push(i, -i);
      }
    }
    progression.push(chord);
  }
  return progression;
};

const makeOffsetProgressionToIndiciesProgression = (
  notes,
  startingIndex
) => offsetProgression =>
  offsetProgression.map(chord =>
    chord
      .map(offset => startingIndex + offset)
      .filter(index => index >= 0 && index < notes.length)
  );

const makeIndiciesProgressionToNoteProgression = notes => indiciesProgression =>
  indiciesProgression.map(chord => chord.map(index => notes[index]));

const pipe = (...fns) => x => fns.reduce((y, fn) => fn(y), x);

const getProgression = notes =>
  pipe(
    getOffsetProgression,
    makeOffsetProgressionToIndiciesProgression(
      notes,
      Math.floor(window.generativeMusic.rng() * notes.length)
    ),
    makeIndiciesProgressionToNoteProgression(notes)
  )();

const playProgression = (piano, notes) => {
  const progression = getProgression(notes);
  const perChordDelay = window.generativeMusic.rng() * 3 + 2;
  progression.forEach((chord, i) => {
    chord.forEach(note =>
      piano.triggerAttack(note, `+${i * perChordDelay + window.generativeMusic.rng() / 10}`)
    );
  });
  Tone.Transport.scheduleOnce(() => {
    playProgression(piano, notes);
  }, `+${window.generativeMusic.rng() * 3 + (progression.length + 1) * perChordDelay}`);
};

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const getPianoDestination = () =>
    Promise.resolve(new Tone.Freeverb({ roomSize: 0.6 }).toDestination());

  const [[renderedInstrumentName, sourceInstrumentName]] = sampleNames;

  const piano = await createPrerenderableSampler({
    samples,
    sourceInstrumentName,
    renderedInstrumentName,
    sampleLibrary,
    onProgress,
    notes: toss(['C', 'E', 'G'], OCTAVES).concat(['B6']),
    additionalRenderLength: 0,
    getDestination: getPianoDestination,
  });

  const schedule = ({ destination }) => {
    piano.connect(destination);
    const tonic = getRandomElement([
      'C',
      'C#',
      'D',
      'D#',
      'E',
      'F',
      'F#',
      'G',
      'G#',
      'A',
      'A#',
      'B',
    ]);
    const scalePitchClasses = MAJOR_SCALE_INTERVALS.map(transpose(tonic));
    const notes = toss(scalePitchClasses, OCTAVES);

    playProgression(piano, notes);

    return () => {
      piano.releaseAll(0);
    };
  };

  const deactivate = () => {
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['trees'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/make-active-stage.js<|end_filename|>
import { Gain, now, Transport } from 'tone';
import undefinedValue from './utilities/undefined';
import noop from './utilities/noop';

const FADE_TIME = 0.1;

const makeActiveStage = ({ deactivate, schedule, destination }) => {
  let isDeactivated = false;
  const endFns = [];

  const wrappedSchedule = () => {
    if (isDeactivated) {
      throw new Error("Can't schedule after deactivation");
    }
    if (endFns.length > 0) {
      console.warn("Rescheduling a piece that wasn't ended");
    }
    const gainNode = new Gain(0).connect(destination);
    const end = schedule({ destination: gainNode });
    if (typeof end !== 'function') {
      return noop;
    }
    let isEnded = false;
    const wrappedEnd = () => {
      if (isEnded) {
        return undefinedValue;
      }
      const currentTime = now();
      gainNode.gain.cancelScheduledValues(currentTime);
      gainNode.gain.setValueAtTime(gainNode.gain.value, currentTime);
      gainNode.gain.linearRampToValueAtTime(0, currentTime + FADE_TIME);
      Transport.scheduleOnce(() => {
        gainNode.gain.dispose();
      }, currentTime + FADE_TIME);
      isEnded = true;
      endFns.splice(endFns.indexOf(wrappedEnd), 1);
      return end();
    };
    endFns.push(wrappedEnd);
    const currentTime = now();
    gainNode.gain.setValueAtTime(0, currentTime);
    gainNode.gain.linearRampToValueAtTime(1, currentTime + FADE_TIME);
    return wrappedEnd;
  };

  const wrappedDeactivate = () => {
    if (isDeactivated) {
      return undefinedValue;
    }
    isDeactivated = true;
    endFns.forEach(end => end());
    return deactivate();
  };

  return [wrappedDeactivate, wrappedSchedule];
};

export default makeActiveStage;


<|start_filename|>packages/make-piece-oxalis/src/make-oxalis.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  getPitchClass,
  getOctave,
} from '@generative-music/utilities';
import gainAdjustments from '../../../normalize/gain.json';

const getReverb = () =>
  new Tone.Reverb(15)
    .set({ wet: 0.5 })
    .toDestination()
    .generate();

const makeOxalis = ({ notes = [], renderedInstrumentPrefix = '' }) => {
  const renderedPianoName = `${renderedInstrumentPrefix}__vsco2-piano-mf`;
  const renderedGlockName = `${renderedInstrumentPrefix}__vsco2-glock`;
  const activate = async ({ sampleLibrary, onProgress }) => {
    const samples = await sampleLibrary.request(Tone.context, [
      [renderedPianoName, 'vsco2-piano-mf'],
      [renderedGlockName, 'vsco2-glock'],
    ]);
    const piano = await createPrerenderableSampler({
      samples,
      sampleLibrary,
      notes,
      sourceInstrumentName: 'vsco2-piano-mf',
      renderedInstrumentName: renderedPianoName,
      getDestination: getReverb,
      onProgress: val => onProgress(val / 2),
    });

    const glock = await createPrerenderableSampler({
      samples,
      sampleLibrary,
      notes: notes.slice(1).map(note => {
        const pc = getPitchClass(note);
        const oct = getOctave(note);
        return `${pc}${oct + 1}`;
      }),
      sourceInstrumentName: 'vsco2-glock',
      renderedInstrumentName: renderedGlockName,
      getDestination: getReverb,
      onProgress: val => onProgress((val + 1) / 2),
    });
    const glockVol = new Tone.Volume(-15);

    const schedule = ({ destination }) => {
      const delay = new Tone.FeedbackDelay({
        delayTime: 5,
        maxDelay: 5,
        feedback: 0.5,
      }).connect(destination);
      const first = Math.floor(window.generativeMusic.rng() * notes.length);
      glockVol.connect(delay);
      glock.connect(glockVol);
      piano.connect(delay);
      notes.forEach((note, i) => {
        let initialized = false;
        const play = () => {
          const isFirst = i === first;
          if (isFirst) {
            piano.triggerAttack(note, '+1');
          }
          Tone.Transport.scheduleOnce(() => {
            piano.triggerAttack(note, '+1');
            if (window.generativeMusic.rng() < 0.05) {
              const pc = note.slice(0, note.length - 1);
              const oct = Number.parseInt(note.slice(-1), 10);
              glock.triggerAttack(`${pc}${Math.max(oct + 1, 5)}`, '+1');
            }
            play();
          }, `+${window.generativeMusic.rng() * 15 + (initialized || isFirst ? 15 : 0) * (i === 0 ? 3 : 1)}`);
          initialized = true;
        };
        play();
      });

      return () => {
        piano.releaseAll(0);
        glock.releaseAll(0);
        delay.dispose();
      };
    };

    const deactivate = () => {
      [piano, glock].forEach(node => node.dispose());
    };

    return [deactivate, schedule];
  };

  const GAIN_ADJUSTMENT = gainAdjustments['oxalis-1'];

  return wrapActivate(activate, { gain: GAIN_ADJUSTMENT });
};

export default makeOxalis;


<|start_filename|>packages/piece-pulse-code-modulation/src/get-similar-pitch-classes.js<|end_filename|>
import {
  P1,
  M2,
  M3,
  P4,
  P5,
  M6,
  M7,
  transpose,
  getRandomElement,
} from '@generative-music/utilities';

const majorScale = tonic => [P1, M2, M3, P4, P5, M6, M7].map(transpose(tonic));
const majorScaleSets = [
  'C',
  'C#',
  'D',
  'D#',
  'E',
  'F',
  'F#',
  'G',
  'G#',
  'A',
  'A#',
  'B',
]
  .map(majorScale)
  .map(arr => new Set(arr));

const majorScalesWithNotes = notes =>
  majorScaleSets.filter(scaleNoteSet =>
    notes.every(note => scaleNoteSet.has(note))
  );

const getSimilarPitchClasses = (includingPitchClasses = []) => {
  const compatibleMajorScales = majorScalesWithNotes(includingPitchClasses);
  const randomCompatibleScale = getRandomElement(compatibleMajorScales);
  return Array.from(
    new Set([
      ...includingPitchClasses,
      ...Array.from(randomCompatibleScale).filter(() => window.generativeMusic.rng() < 0.5),
    ])
  );
};

export default getSimilarPitchClasses;


<|start_filename|>packages/piece-last-transit/last-transit.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "partialLattice",
  "title": "Last Transit",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "last-transit",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "noise",
    "electronic"
  ],
  "releaseDate": "2019-08-18T00:00:00.000Z",
  "sampleNames": [
    [
      "last-transit__idling-truck",
      "idling-truck"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/last-transit-excerpts"
}

<|start_filename|>packages/piece-pulse-code-modulation/src/get-similar-notes.js<|end_filename|>
import { getPitchClass, toss, getDistance } from '@generative-music/utilities';
import getSimilarPitchClasses from './get-similar-pitch-classes';

const getSimilarNotes = (currentNotes, octaves) => {
  const pitchClasses = Array.from(
    new Set(currentNotes.map(note => getPitchClass(note)))
  );
  return toss(getSimilarPitchClasses(pitchClasses), octaves)
    .filter(() => window.generativeMusic.rng() < (11 - pitchClasses.length) / 10)
    .reduce(
      (noYuckyIntervals, note) =>
        noYuckyIntervals.every(otherNote => {
          const distance = Math.abs(getDistance(note, otherNote));
          return distance !== 6 && distance !== 1;
        })
          ? noYuckyIntervals.concat([note])
          : noYuckyIntervals,
      []
    );
};

export default getSimilarNotes;


<|start_filename|>packages/utilities/src/utilities/undefined.js<|end_filename|>
//eslint-disable-next-line no-undefined
const undefinedValue = undefined;

export default undefinedValue;


<|start_filename|>packages/piece-oxalis-1/oxalis-1.gfm.manifest.json<|end_filename|>
{
  "title": "Oxalis 1",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "oxalis-1",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "acoustic",
    "calm"
  ],
  "releaseDate": "2020-03-31T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "oxalis-1__vsco2-piano-mf",
      "vsco2-piano-mf"
    ],
    [
      "oxalis-1__vsco2-glock",
      "vsco2-glock"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/oxalis-1-excerpts-2"
}

<|start_filename|>packages/utilities/src/utilities/swap-2.js<|end_filename|>
export default fn => (arg1, arg2) => fn(arg2, arg1);


<|start_filename|>packages/piece-homage/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  major7th,
  createPitchShiftedSampler,
  createPrerenderableSampler,
  getRandomElement,
} from '@generative-music/utilities';
import { sampleNames } from '../homage.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const violinNotes = ['C3', 'C4'];
  const pianoNotes = [...major7th('C5'), ...major7th('C6'), 'C7'];

  const piano = await createPitchShiftedSampler({
    samplesByNote: samples['vsco2-piano-mf'],
    pitchShift: -24,
    attack: 0,
  });

  const violins = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    onProgress,
    notes: violinNotes,
    sourceInstrumentName: 'vsco2-violins-susvib',
    renderedInstrumentName: 'homage__vsco2-violins-susvib',
    getDestination: () => new Tone.Reverb(50).toDestination().generate(),
    pitchShift: -24,
  });

  const violinVolume = new Tone.Volume(-15);
  const filter = new Tone.Filter(50).connect(violinVolume);
  violins.connect(filter);

  const pianoChain = () => {
    const note = getRandomElement(pianoNotes);
    piano.triggerAttack(note, '+1');
    Tone.Transport.scheduleOnce(() => {
      pianoChain();
    }, `+${window.generativeMusic.rng() * 10}`);
  };

  const violinDrone = note => {
    violins.triggerAttack(note, '+1');
    Tone.Transport.scheduleOnce(() => {
      violinDrone(note);
    }, `+${window.generativeMusic.rng() * 10}`);
  };

  const schedule = ({ destination }) => {
    const filterLfo = new Tone.LFO(
      window.generativeMusic.rng() * 0.001 + 0.0005,
      100,
      2000
    ).set({
      phase: 90,
    });
    filterLfo.connect(filter.frequency);
    filterLfo.start();
    piano.connect(destination);
    violinVolume.connect(destination);

    violinNotes.forEach(note => violinDrone(note));
    pianoChain();

    return () => {
      piano.releaseAll(0);
      violins.releaseAll(0);
      filterLfo.dispose();
    };
  };

  const deactivate = () => {
    [piano, violins, filter].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['homage'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-apoapsis/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../apoapsis.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const pianoNotes = toss(['C', 'E', 'G', 'B'], [3, 4, 5]);
const violinNotes = toss(['C', 'E', 'G', 'B'], [2, 3, 4]);

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const getPianoDestination = () =>
    Promise.resolve(
      new Tone.Freeverb({ roomSize: 0.9, wet: 0.5 }).toDestination()
    );
  const getViolinDestination = () =>
    Promise.resolve(
      new Tone.Freeverb({ roomSize: 0.8, wet: 0.5 }).toDestination()
    );

  const reversePiano = await createPrerenderableSampler({
    notes: pianoNotes,
    samples,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'apoapsis__vsco2-piano-mf',
    sampleLibrary,
    additionalRenderLength: 1,
    getDestination: getPianoDestination,
    onProgress: val => onProgress(val * 0.5),
    reverse: true,
  });

  const violins = await createPrerenderableSampler({
    notes: violinNotes,
    samples,
    sourceInstrumentName: 'vsco2-violins-susvib',
    renderedInstrumentName: 'apoapsis__vsco2-violins-susvib',
    sampleLibrary,
    additionalRenderLength: 1,
    getDestination: getViolinDestination,
    onProgress: val => onProgress(val * 0.5 + 0.5),
    bufferSourceOptions: {
      fadeOut: 8,
      curve: 'linear',
    },
  });

  const violinVol = new Tone.Volume(-25);

  violins.connect(violinVol);

  const schedule = ({ destination }) => {
    violinVol.connect(destination);
    const delay1 = new Tone.FeedbackDelay({
      feedback: 0.7,
      delayTime: 0.2,
      wet: 0.5,
    });
    const delay2Time = window.generativeMusic.rng() * 10 + 20;
    const delay2 = new Tone.FeedbackDelay({
      feedback: 0.6,
      delayTime: delay2Time,
      maxDelay: delay2Time,
      wet: 0.5,
    });

    reversePiano.chain(delay1, delay2, destination);

    violinNotes.forEach(note => {
      Tone.Transport.scheduleRepeat(
        () => violins.triggerAttack(note, '+1'),
        window.generativeMusic.rng() * 120 + 60,
        `+${window.generativeMusic.rng() * 15 + 15}`
      );
    });

    const intervals = pianoNotes.map(() => window.generativeMusic.rng() * 30 + 30);
    const minInterval = Math.min(...intervals);
    pianoNotes.forEach((note, i) => {
      const intervalTime = intervals[i];
      Tone.Transport.scheduleRepeat(
        () => reversePiano.triggerAttack(note, '+1'),
        intervalTime,
        intervalTime - minInterval
      );
    });

    return () => {
      reversePiano.releaseAll(0);
      violins.releaseAll(0);
      [delay1, delay2].forEach(node => node.dispose());
    };
  };

  const deactivate = () => {
    [violins, reversePiano].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['apoapsis'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-buttafingers/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  createPrerenderableBuffers,
  createPitchShiftedSampler,
} from '@generative-music/utilities';
import { sampleNames } from '../buttafingers.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const NOTES = ['C4', 'E4', 'F4', 'G4'];
const PITCH_CHANGES = [-36, -24];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const [wines, claves] = await Promise.all([
    Promise.all(
      NOTES.reduce(
        samplers =>
          samplers.concat(
            PITCH_CHANGES.map(pitchShift =>
              createPitchShiftedSampler({
                pitchShift,
                samplesByNote: samples['vcsl-wine-glasses-slow'],
                attack: 3,
                release: 3,
              })
            )
          ),
        []
      )
    ),
    createPrerenderableBuffers({
      samples,
      sampleLibrary,
      sourceInstrumentName: 'vcsl-claves',
      renderedInstrumentName: 'buttafingers__vcsl-claves',
      additionalRenderLength: 0,
      getDestination: () =>
        Promise.resolve(new Tone.Freeverb({ roomSize: 0.6 }).toDestination()),
      onProgress,
    }),
  ]);
  const disposableNodes = [];
  const disposeNode = node => {
    node.dispose();
    const i = disposableNodes.indexOf(node);
    if (i >= 0) {
      disposableNodes.splice(i, 1);
    }
  };
  const compressor = new Tone.Compressor();
  const filter = new Tone.Filter(200, 'lowpass', -48);
  filter.connect(compressor);

  const claveSounds =
    samples['vcsl-claves'] || samples['buttafingers__vcsl-claves'];

  const claveVol = new Tone.Volume(-15);

  const ballBounceClave = () => {
    const panner = new Tone.Panner(window.generativeMusic.rng() * 2 - 1).connect(claveVol);
    disposableNodes.push(panner);
    const buffer = claves.get(Math.floor(window.generativeMusic.rng() * claveSounds.length));
    let time = window.generativeMusic.rng() + 1;
    const deltaMultiplier = window.generativeMusic.rng() * 0.1 + 0.75;
    const playbackRate = window.generativeMusic.rng() + 0.5;
    for (
      let delayDelta = 1;
      delayDelta >= (1 - deltaMultiplier - 0.15) / 10;
      delayDelta *= deltaMultiplier, time += delayDelta
    ) {
      const source = new Tone.ToneBufferSource(buffer)
        .set({
          playbackRate,
          onended: () => {
            disposeNode(source);
          },
        })
        .connect(panner);
      disposableNodes.push(source);
      source.start(`+${time}`);
    }
    Tone.Transport.scheduleOnce(() => {
      disposeNode(panner);
    }, `+60`);
    Tone.Transport.scheduleOnce(() => {
      ballBounceClave();
    }, `+${window.generativeMusic.rng() * 10 + 10}`);
  };

  const schedule = ({ destination }) => {
    compressor.connect(destination);
    const delay = new Tone.FeedbackDelay({
      delayTime: 3,
      feedback: 0.3,
      wet: 0.2,
    });

    claveVol.connect(delay);

    const firstIndex = Math.floor(window.generativeMusic.rng() * wines.length);

    wines.forEach((wine, i) => {
      const gain = new Tone.Gain().connect(filter);
      const lfo = new Tone.LFO({
        frequency: window.generativeMusic.rng() / 100,
        phase: firstIndex === i ? 270 : window.generativeMusic.rng() * 360,
      });
      lfo.connect(gain.gain).start();
      wine.connect(gain);
      disposableNodes.push(gain, lfo);
      const playNote = () => {
        wine.triggerAttack(NOTES[Math.floor(i / 2)], '+1');
        Tone.Transport.scheduleOnce(() => {
          playNote();
        }, '+60');
      };
      if (i === firstIndex) {
        playNote();
      } else {
        Tone.Transport.scheduleOnce(() => {
          playNote();
        }, `+${window.generativeMusic.rng() * 60}`);
      }
    });

    Tone.Transport.scheduleOnce(() => {
      ballBounceClave(delay);
    }, `+${window.generativeMusic.rng() * 10 + 10}`);

    delay.connect(destination);

    return () => {
      wines.forEach(sampler => {
        sampler.releaseAll(0);
      });
      disposableNodes.forEach(disposeNode);
      delay.dispose();
    };
  };

  const deactivate = () => {
    disposableNodes
      .concat(wines)
      .concat([claves, compressor, filter])
      .forEach(disposeNode);
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['buttafingers'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>test-player/webpack.config.js<|end_filename|>
'use strict';

const { argv } = require('process');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { EnvironmentPlugin } = require('webpack');

if (argv.length < 4 || argv[2] !== '--pieceId') {
  throw new Error('Please provide a piece id with --pieceId');
}

const pieceId = argv[3];

const piecePath = path.resolve(
  __dirname,
  `../packages/piece-${pieceId}/src/piece.js`
);

console.log(`Testing ${piecePath}`);

const config = {
  resolve: {
    alias: {
      piece$: piecePath,
      tone$: path.resolve(__dirname, `./node_modules/tone`),
      '@generative-music/utilities$': path.resolve(
        __dirname,
        '../packages/utilities/src/index.js'
      ),
    },
  },
  module: {
    rules: [
      {
        test: path.resolve(
          './node_modules/@generative-music/web-provider/worker/save-worker.esm.js'
        ),
        use: 'worker-loader',
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin(),
    new EnvironmentPlugin({ SAMPLE_FILE_HOST: '//localhost:6969' }),
  ],
};

module.exports = config;


<|start_filename|>normalize/temp.js<|end_filename|>
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.9/Tone.js';
script.onload = () => {
  const video = document.querySelector('video');
  if (!video) {
    console.log('no video element!');
  }
  const mediaElNode = Tone.getContext().createMediaElementSource(video);
  const meter = new Tone.Meter().toDestination();
  Tone.connect(mediaElNode, meter);
  let maxDb = -Infinity;
  setInterval(() => {
    const value = meter.getValue();
    if (value > maxDb) {
      console.log(value);
      maxDb = value;
    }
  }, 10);
};

document.body.append(script);


<|start_filename|>packages/utilities/src/index.js<|end_filename|>
import createBuffer from './create-buffer';
import createBuffers from './create-buffers';
import createSampler from './create-sampler';
import createPrerenderableSampler from './create-prerenderable-sampler';
import wrapActivate from './wrap-activate';
import getRandomNumberBetween from './get-random-number-between';
import getRandomElement from './get-random-element';
import toss from './toss';
import renderBuffer from './render-buffer';
import createPrerenderableBufferArray from './create-prerenderable-buffer-array';
import getClosestNote from './get-closest-note';
import sampleNote from './sample-note';
import createPrerenderableBuffers from './create-prerenderable-buffers';
import createPitchShiftedSampler from './create-pitch-shifted-sampler';
import shuffleArray from './shuffle-array';
import createPrerenderableSampledBuffers from './create-prerenderable-sampled-buffers';
import createReverseSampler from './create-reverse-sampler';
import createPrerenderableInstrument from './create-prerenderable-instrument';
import createPrerenderedBuffer from './create-prerendered-buffer';

export * from './theory/index';
export {
  createBuffer,
  createBuffers,
  createSampler,
  createPrerenderableSampler,
  wrapActivate,
  getRandomNumberBetween,
  getRandomElement,
  toss,
  renderBuffer,
  createPrerenderableBufferArray,
  getClosestNote,
  sampleNote,
  createPrerenderableBuffers,
  createPitchShiftedSampler,
  shuffleArray,
  createPrerenderableSampledBuffers,
  createReverseSampler,
  createPrerenderableInstrument,
  createPrerenderedBuffer,
};


<|start_filename|>packages/utilities/src/create-prerenderable-sampled-buffer.js<|end_filename|>
import sampleNote from './sample-note';
import createBuffer from './create-buffer';
import renderBuffer from './render-buffer';

const createPrerenderableSampledBuffer = async ({
  note,
  samplesByNote,
  getDestination,
  additionalRenderLength,
  bufferSourceOptions = {},
  pitchShift = 0,
  reverse = false,
}) => {
  const { playbackRate, sampledNote } = sampleNote({
    note,
    pitchShift,
    sampledNotes: Object.keys(samplesByNote),
  });
  const noteBuffer = await createBuffer(samplesByNote[sampledNote]);
  noteBuffer.reverse = reverse;
  const renderedBuffer = await renderBuffer({
    getDestination,
    buffer: noteBuffer,
    duration: noteBuffer.duration / playbackRate + additionalRenderLength,
    bufferSourceOptions: Object.assign({}, bufferSourceOptions, {
      playbackRate,
    }),
  });
  noteBuffer.dispose();
  return renderedBuffer;
};

export default createPrerenderableSampledBuffer;


<|start_filename|>packages/utilities/src/theory/pitch-class-indicies-by-value.js<|end_filename|>
import pitchClasses from './pitch-classes';

const pitchClassIndiciesByValue = pitchClasses.reduce(
  (byIndex, pitchClass, index) => {
    byIndex[pitchClass] = index;
    return byIndex;
  },
  {}
);

export default pitchClassIndiciesByValue;


<|start_filename|>packages/piece-apoapsis/apoapsis.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Apoapsis",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "apoapsis",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "calm",
    "electronic",
    "dream"
  ],
  "releaseDate": "2019-02-01T00:00:00.000Z",
  "sampleNames": [
    [
      "apoapsis__vsco2-violins-susvib",
      "vsco2-violins-susvib"
    ],
    [
      "apoapsis__vsco2-piano-mf",
      "vsco2-piano-mf"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/apoapsis-excerpts"
}

<|start_filename|>packages/piece-drones-2/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffers,
  sampleNote,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../drones-2.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const NOTES = ['C4', 'G4', 'C5', 'G5', 'E5'];

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const samplesByNote = samples['vsco2-violins-susvib'];
  const sampledNotes = Object.keys(samplesByNote);

  const buffers = await createBuffers(samplesByNote);

  const filter = new Tone.Filter(6000, 'lowpass', -48);
  const activeSources = [];

  const drone = (note, droneDestination, pitchShift = 0, reverse = false) => {
    const { sampledNote, playbackRate } = sampleNote({
      note,
      sampledNotes,
      pitchShift,
    });
    const buffer = buffers.get(sampledNote);
    const source = new Tone.BufferSource(buffer)
      .set({
        reverse,
        playbackRate,
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(droneDestination);
    source.start('+1');
  };

  const schedule = ({ destination }) => {
    filter.connect(destination);
    const autoFilter = new Tone.AutoFilter(window.generativeMusic.rng() / 10, 150, 4)
      .connect(filter)
      .start();

    const lfoMin = window.generativeMusic.rng() / 100;
    const lfoMax = lfoMin * 10;

    const frequencyLfo = new Tone.LFO({ min: lfoMin, max: lfoMax });

    frequencyLfo.connect(autoFilter.frequency);
    frequencyLfo.start();

    NOTES.forEach(note => {
      const playDrone = () => {
        drone(note, autoFilter, -36);
        Tone.Transport.scheduleOnce(() => {
          playDrone();
        }, `+${window.generativeMusic.rng() * 20 + 40}`);
      };
      playDrone();
    });

    const startDrone = (note, droneDestination, delay) => {
      const playDrone = () => {
        drone(note, droneDestination, -24, true);
        Tone.Transport.scheduleOnce(() => {
          playDrone();
        }, `+${window.generativeMusic.rng() * 45 + 45}`);
      };
      Tone.Transport.scheduleOnce(() => {
        playDrone();
      }, `+${delay}`);
    };
    const firstDroneDelay = window.generativeMusic.rng() * 15 + 15;
    const secondDroneDelay = firstDroneDelay + window.generativeMusic.rng() * 30;
    startDrone('D5', filter, firstDroneDelay);
    startDrone('E5', filter, secondDroneDelay);
    startDrone('C5', filter, 60 + window.generativeMusic.rng() * 60);

    return () => {
      activeSources.forEach(source => source.dispose());
      autoFilter.dispose();
      frequencyLfo.dispose();
    };
  };

  const deactivate = () => {
    filter.dispose();
  };
  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['drones-2'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-little-bells/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  minor7th,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../little-bells.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const PITCH_CLASSES = ['F', 'G', 'G#', 'A', 'A#', 'B'];
const BASE_P_TO_PLAY = 0.1;
const MODULO_DIVISOR_ONE = 4;
const MODULO_DIVISOR_TWO = 2;
const LOWER_INTERVAL_TIME = 22;
const HIGHER_INTERVAL_TIME = 20;

const makeChordInterval = instrument => (
  tonic,
  interval,
  shouldPlayImmediately = false
) => {
  let hasPlayed = false;
  Tone.Transport.scheduleRepeat(
    () => {
      const notes = minor7th(tonic);
      const numNotesToPlay = Math.floor(
        window.generativeMusic.rng() * (notes.length + 1)
      );
      let playedNotes = 0;
      let beat = 1;
      while (
        playedNotes < numNotesToPlay ||
        (shouldPlayImmediately && !hasPlayed)
      ) {
        const chanceToPlay =
          BASE_P_TO_PLAY +
          (beat % MODULO_DIVISOR_ONE === 1 ? BASE_P_TO_PLAY : 0) +
          (beat % MODULO_DIVISOR_TWO === 1 ? BASE_P_TO_PLAY : 0);
        if (
          window.generativeMusic.rng() < chanceToPlay ||
          (shouldPlayImmediately && !hasPlayed)
        ) {
          const noteIndex = Math.floor(
            window.generativeMusic.rng() * notes.length
          );
          const note = notes[noteIndex];
          notes.splice(noteIndex, 1);
          instrument.triggerAttack(note, `+${beat}`);
          playedNotes += 1;
          hasPlayed = true;
        }
        beat += 1;
      }
    },
    interval,
    Tone.now()
  );
};

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const getGlockDestination = () =>
    Promise.resolve(
      new Tone.Freeverb({ roomSize: 0.9, dampening: 2000 }).toDestination()
    );

  const notes = Array.from(
    new Set(
      toss(PITCH_CLASSES, [4, 5])
        .sort()
        .map(minor7th)
        .flat()
        .map(note => Tone.Midi(note).toMidi())
    )
  )
    .sort()
    .filter((_, i) => i % 3 === 0)
    .map(midi => Tone.Midi(midi).toNote());

  const glock = await createPrerenderableSampler({
    notes,
    samples,
    sampleLibrary,
    onProgress,
    sourceInstrumentName: 'vsco2-glock',
    renderedInstrumentName: 'little-bells__vsco2-glock',
    additionalRenderLength: 1,
    getDestination: getGlockDestination,
  });

  const schedule = ({ destination }) => {
    const delay = new Tone.FeedbackDelay({
      delayTime: 8,
      maxDelay: 8,
      feedback: 0.7,
      wet: 0.5,
    });

    glock.chain(delay, destination);

    const chordInterval = makeChordInterval(glock);
    const pitchClass =
      PITCH_CLASSES[
        Math.floor(window.generativeMusic.rng() * PITCH_CLASSES.length)
      ];
    const p = window.generativeMusic.rng();
    chordInterval(`${pitchClass}4`, LOWER_INTERVAL_TIME, p >= 0.33);
    chordInterval(`${pitchClass}5`, HIGHER_INTERVAL_TIME, p < 0.66);

    return () => {
      delay.dispose();
    };
  };

  const deactivate = () => {
    glock.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['little-bells'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>normalize/renderer/renderer.js<|end_filename|>
'use strict';

const path = require('path');
const { ipcRenderer } = require('electron');
const Tone = require('tone');
const createLibrary = require('@generative-music/fs-library');
const createProvider = require('@generative-music/fs-provider');
const getSampleIndex = require('@generative-music/samples-alex-bainter');

const INITIAL_GAIN = 1;
const FIRST_GAIN_DELTA = 1;
const TRACK_TIME = 60;

const sampleFileHost = path.join(
  __dirname,
  '../../../samples-alex-bainter/dist'
);

// 1. gain = 1, delta = 1;
// 2. Record for 60 seconds and track db.
// 3. If db is greater than -1, stop recording, set delta = delta / 2, reduce gain by delta, and go back to step 2.
// 4. If db is less than -2, increase gain by delta and go back to step 2.
// 5. Otherwise, current gain value is the adjustment needed.

const sampleIndex = getSampleIndex({ host: sampleFileHost, format: 'ogg' });

const sampleLibrary = createLibrary({
  provider: createProvider(),
  sampleIndex,
});

ipcRenderer.once('begin', async (event, { pieceId }) => {
  const piecePath = path.join(__dirname, `../../packages/piece-${pieceId}`);
  require.cache[path.join(__dirname, '../../node_modules/tone/build/Tone.js')] =
    require.cache[require.resolve('tone')];

  const activate = require(piecePath);
  const meter = new Tone.Meter();
  let gain = INITIAL_GAIN;
  let nextGainDelta = FIRST_GAIN_DELTA;
  const gainNode = new Tone.Gain(gain).connect(meter);

  const [deactivate, schedule] = await activate({
    sampleLibrary,
    context: Tone.getContext(),
    destination: gainNode,
  });

  let isRampingUp = true;

  const startMetering = () => {
    let maxDbForGain = -Infinity;
    let endEventId;
    const meterInterval = setInterval(() => {
      maxDbForGain = Math.max(maxDbForGain, meter.getValue());
      if (maxDbForGain < -1) {
        return;
      }
      clearInterval(meterInterval);
      Tone.Transport.clear(endEventId);
      if (isRampingUp) {
        nextGainDelta /= 2;
      }
      isRampingUp = false;
      gainNode.gain.value -= nextGainDelta;
      console.log(
        `${maxDbForGain}db is too loud, reducing gain to ${gainNode.gain.value}`
      );
      setTimeout(() => {
        startMetering();
      }, 10);
    }, 10);

    endEventId = Tone.Transport.scheduleOnce(() => {
      clearInterval(meterInterval);
      if (maxDbForGain > -2) {
        console.log(
          `suggested gain: ${
            gainNode.gain.value
          } for a maxDb of ${maxDbForGain}`
        );
        Tone.Transport.stop();
        end();
        deactivate();
        ipcRenderer.send('complete', gainNode.gain.value);
        return;
      }
      if (!isRampingUp) {
        nextGainDelta /= 2;
      }
      isRampingUp = true;
      gainNode.gain.value += nextGainDelta;
      console.log(
        `${maxDbForGain}db is too quiet, increasing gain to ${
          gainNode.gain.value
        }`
      );
      setTimeout(() => {
        startMetering();
      }, 10);
    }, Tone.now() + TRACK_TIME);
  };

  const end = schedule();
  console.log(
    `starting volume check of ${pieceId} with gain of ${gainNode.gain.value}`
  );
  startMetering();

  Tone.Transport.start();
});

ipcRenderer.send('ready');


<|start_filename|>packages/piece-enough/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  minor7th,
  createPitchShiftedSampler,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../enough.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const corAnglaisSamplesByNote = samples['sso-cor-anglais'];
  const corAnglais = await createPitchShiftedSampler({
    samplesByNote: corAnglaisSamplesByNote,
    pitchShift: -24,
    attack: 5,
    release: 5,
    curve: 'linear',
  });
  const delayVolume = new Tone.Volume(-28);
  const compressor = new Tone.Compressor();

  const notes = toss(['A#'], [3, 4])
    .map(minor7th)
    .flat();

  const playChord = (first = false) => {
    let chord = notes
      .filter(() => window.generativeMusic.rng() < 0.5)
      .slice(0, 4);
    while (first && chord.length === 0) {
      chord = notes
        .filter(() => window.generativeMusic.rng() < 0.5)
        .slice(0, 4);
    }
    const immediateNoteIndex = first
      ? Math.floor(window.generativeMusic.rng() * chord.length)
      : -1;
    chord.forEach((note, i) =>
      corAnglais.triggerAttack(
        note,
        `+${immediateNoteIndex === i ? 0 : window.generativeMusic.rng() * 2}`
      )
    );
    Tone.Transport.scheduleOnce(() => {
      playChord();
    }, `+${window.generativeMusic.rng() * 5 + 12}`);
  };

  const schedule = ({ destination }) => {
    const delay = new Tone.FeedbackDelay({
      feedback: 0.5,
      delayTime: 10,
      maxDelay: 10,
    }).connect(delayVolume);
    corAnglais.connect(compressor);
    compressor.connect(delay);
    compressor.connect(destination);
    delayVolume.connect(destination);

    playChord(true);

    return () => {
      corAnglais.releaseAll(0);
      delay.dispose();
    };
  };

  const deactivate = () => {
    [corAnglais, delayVolume, compressor].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['enough'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-yesterday/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../yesterday.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const randomIntBetween = (min, max) =>
  Math.floor(min + window.generativeMusic.rng() * (max - min));

const scale = ['C3', 'D3', 'E3', 'F3', 'G3', 'A3', 'B3', 'C4'];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const sax = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    onProgress,
    notes: scale.filter((_, i) => i % 2 === 0),
    sourceInstrumentName: 'vcsl-tenor-sax-vib',
    renderedInstrumentName: 'yesterday__vcsl-tenor-sax-vib',
    getDestination: () =>
      new Tone.Reverb(20)
        .set({ wet: 0.9 })
        .toDestination()
        .generate(),
  });

  sax.set({ attack: 2, curve: 'linear' });

  const playNote = (note, time = 0) => {
    sax.triggerAttack(note, `+${1 + time}`);
  };

  const getMelody = () => {
    const melody = [];
    const length = window.generativeMusic.rng() < 0.9 ? 4 : 3;
    let currentIndex =
      window.generativeMusic.rng() < 0.5
        ? randomIntBetween(3, scale.length)
        : scale.length - 1;
    for (let i = 0; i < length; i += 1) {
      melody.push(scale[currentIndex]);
      if (i === 0) {
        currentIndex = randomIntBetween(1, currentIndex - 1);
      } else if (i === 1) {
        currentIndex = randomIntBetween(
          currentIndex + 1,
          scale.indexOf(melody[0])
        );
      } else if (i === 2) {
        currentIndex = randomIntBetween(0, scale.indexOf(melody[1]));
      }
    }
    return melody;
  };

  const playMelody = () => {
    const melody = getMelody();
    const baseTime = window.generativeMusic.rng() * 2 + 1;
    melody.forEach((note, i) => {
      playNote(note, i >= 2 ? i * baseTime + baseTime : i * baseTime);
    });

    playNote(`${melody[melody.length - 1].charAt(0)}${1}`);

    Tone.Transport.scheduleOnce(
      () => playMelody(),
      `+${baseTime * 8 + window.generativeMusic.rng()}`
    );
  };

  const schedule = ({ destination }) => {
    const autoFilter = new Tone.AutoFilter(
      window.generativeMusic.rng() / 100,
      1500,
      2
    ).connect(destination);
    const delay = new Tone.FeedbackDelay(0.5, 0.85).connect(autoFilter);
    sax.connect(delay);
    autoFilter.start();
    playMelody();
    return () => {
      sax.releaseAll(0);
      autoFilter.dispose();
      delay.dispose();
    };
  };

  const deactivate = () => {
    sax.dispose();
  };
  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['yesterday'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-zed/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  toss,
  createPrerenderableInstrument,
  createPrerenderedBuffer,
} from '@generative-music/utilities';
import { sampleNames } from '../piece.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const PITCH_CLASSES = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
const OCTAVES = [3, 4];
const NOTES = toss(PITCH_CLASSES, OCTAVES);

const createSynth = async context => {
  const synth = new Tone.DuoSynth({
    voice0: {
      oscillator: {
        type: 'square',
      },
      envelope: {
        attack: 5,
        release: 12,
      },
      filterEnvelope: {
        attack: 5,
        release: 12,
      },
    },
    voice1: {
      oscillator: {
        type: 'sawtooth',
      },
      envelope: {
        attack: 5,
        release: 12,
      },
      filterEnvelope: {
        attack: 5,
        release: 12,
      },
    },
    harmonicity: 0,
    vibratoRate: 0.45,
    vibratoAmount: 0.45,
    volume: -20,
  });
  const reverb = await new Tone.Reverb({ decay: 20 })
    .set({ wet: 0.5 })
    .connect(context.destination)
    .generate();
  synth.connect(reverb);
  return {
    instrument: synth,
    dispose: () => {
      synth.dispose();
      reverb.dispose();
    },
  };
};

const createNoise = async context => {
  const reverb = await new Tone.Reverb({ decay: 20 })
    .set({ wet: 0.35 })
    .connect(context.destination)
    .generate();
  const noiseSynthFxGain = new Tone.Gain().connect(reverb);
  const noiseSynth = new Tone.NoiseSynth({
    envelope: { sustain: 1, attack: 0 },
    volume: -40,
  }).connect(noiseSynthFxGain);

  const noiseSynthFxGainLfo = new Tone.LFO({ type: 'square' })
    .set({ phase: 270 })
    .connect(noiseSynthFxGain.gain)
    .start();

  const noiseSynthFxGainLfoFrequencyLfo = new Tone.LFO({
    min: 2,
    max: 20,
    frequency: 0.075,
  })
    .set({ phase: 90 })
    .connect(noiseSynthFxGainLfo.frequency)
    .start();

  return {
    start: () => noiseSynth.triggerAttack(),
    dispose: () => {
      [
        reverb,
        noiseSynthFxGain,
        noiseSynth,
        noiseSynthFxGainLfo,
        noiseSynthFxGainLfoFrequencyLfo,
      ].forEach(node => node.dispose());
    },
  };
};

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const padSynth = await createPrerenderableInstrument({
    sampleLibrary,
    samples,
    createInstrument: createSynth,
    notes: NOTES,
    noteDuration: 20,
    renderedInstrumentName: 'zed__pad',
    onProgress: val => onProgress(val * 0.95),
  });

  padSynth.set({
    attack: 5,
    release: 12,
  });

  if (!samples['zed__noise']) {
    const fullNoiseSynthBuffer = await createPrerenderedBuffer({
      createSource: createNoise,
      duration: 2 / 0.075,
    });
    const noiseSynthBuffer = fullNoiseSynthBuffer.slice(1);
    fullNoiseSynthBuffer.dispose();
    samples['zed__noise'] = [noiseSynthBuffer];
    sampleLibrary.save([['zed__noise', [noiseSynthBuffer]]]);
    onProgress(1);
  }

  const padFilter = new Tone.Filter({ type: 'lowpass' });
  padSynth.connect(padFilter);
  const noiseFilter = new Tone.Filter({ type: 'lowpass' });
  const noiseSynthMasterGain = new Tone.Gain().connect(noiseFilter);

  const noisePlayer = new Tone.Player(samples['zed__noise'][0])
    .set({ loop: true })
    .connect(noiseSynthMasterGain);

  const schedule = ({ destination }) => {
    padFilter.connect(destination);
    noiseFilter.connect(destination);
    const noiseSynthMasterGainLfo = new Tone.LFO({
      frequency: window.generativeMusic.rng() * 0.05 + 0.05,
    })
      .set({ phase: 90 })
      .connect(noiseSynthMasterGain.gain)
      .start();

    const noiseSynthMasterGainLfoFrequencyLfo = new Tone.LFO({
      min: 0.01,
      max: 0.05,
      frequency: window.generativeMusic.rng() * 0.05 + 0.05,
    })
      .connect(noiseSynthMasterGainLfo.frequency)
      .start();

    const padFilterFreqLfo = new Tone.LFO({
      min: 100,
      max: 250,
      frequency: window.generativeMusic.rng() * 0.05 + 0.05,
    })
      .connect(padFilter.frequency)
      .start();

    const noiseFilterFreqLfo = new Tone.LFO({
      min: 5000,
      max: 10000,
      frequency: window.generativeMusic.rng() * 0.05 + 0.05,
    })
      .connect(noiseFilter.frequency)
      .start();

    noisePlayer.start();

    const playRandomChord = () => {
      const root = Math.floor(window.generativeMusic.rng() * (NOTES.length - 5));
      const chord = [root, root + 2, root + 5];
      const notes = chord.map(index => NOTES[index]);
      const baseDuration = window.generativeMusic.rng() * 3 + 4;
      notes.forEach(note => {
        padSynth.triggerAttackRelease(
          note,
          baseDuration + window.generativeMusic.rng() - 0.5,
          `+${window.generativeMusic.rng()}`
        );
      });

      Tone.Transport.scheduleOnce(() => {
        playRandomChord();
      }, `+${window.generativeMusic.rng() * 3 + 4 + window.generativeMusic.rng() * 3 + 4}`);
    };

    playRandomChord();

    return () => {
      padSynth.releaseAll(0);
      noisePlayer.stop(0);
      [
        noiseSynthMasterGainLfo,
        noiseSynthMasterGainLfoFrequencyLfo,
        padFilterFreqLfo,
        noiseFilterFreqLfo,
      ].forEach(lfo => lfo.dispose());
    };
  };

  const deactivate = () => {
    [
      padSynth,
      noisePlayer,
      padFilter,
      noiseFilter,
      noiseSynthMasterGain,
    ].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['zed'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-pulse-code-modulation/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  toss,
  getRandomElement,
  getPitchClass,
} from '@generative-music/utilities';
import getSimilarNotes from './get-similar-notes';
import { sampleNames } from '../pulse-code-modulation.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const DRONE_OCTAVES = [4, 5];
const PIANO_OCTAVES = [4, 5, 6];
const GUITAR_OCTAVES = [2, 3];
const PITCH_CLASSES = [
  'C',
  'C#',
  'D',
  'D#',
  'E',
  'F',
  'F#',
  'G',
  'G#',
  'A',
  'A#',
  'B',
];
const DRONE_NOTES = toss(PITCH_CLASSES, DRONE_OCTAVES);

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const getReverb = () =>
    new Tone.Reverb(15)
      .set({ wet: 0.9 })
      .toDestination()
      .generate();

  const renderedNotes = DRONE_NOTES.filter((_, i) => i % 3 === 0);
  const droneSamplers = await Promise.all(
    DRONE_NOTES.map(() =>
      createPrerenderableSampler({
        samples,
        sampleLibrary,
        notes: renderedNotes,
        sourceInstrumentName: 'vsco2-violins-susvib',
        renderedInstrumentName: 'pulse-code-modulation__vsco2-violins-susvib',
        getDestination: getReverb,
        onProgress: val => onProgress(val * 0.33),
        pitchShift: -24,
      })
    )
  );
  const guitarSampler = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: toss(PITCH_CLASSES.filter((_, i) => i % 3 === 0), GUITAR_OCTAVES),
    sourceInstrumentName: 'acoustic-guitar',
    renderedInstrumentName: 'pulse-code-modulation__acoustic-guitar',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.33 + 0.33),
  });
  guitarSampler.set({ attack: 3, curve: 'linear' });
  const pianoSampler = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: toss(PITCH_CLASSES.filter((_, i) => i % 3 === 0), PIANO_OCTAVES),
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'pulse-code-modulation__vsco2-piano-mf',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.33 + 0.66),
  });

  const filter = new Tone.Filter(window.generativeMusic.rng() * 300 + 300);

  const droneGainsByNote = droneSamplers.reduce((dronesHash, sampler, i) => {
    const note = DRONE_NOTES[i];
    const gain = new Tone.Gain(0).connect(filter);
    sampler.connect(gain);
    dronesHash[note] = gain;
    return dronesHash;
  }, {});

  const playChord = (notes = getSimilarNotes([], DRONE_OCTAVES)) => {
    const time = window.generativeMusic.rng() * 30 + 30;
    notes.forEach(note => {
      const { gain } = droneGainsByNote[note];
      gain.cancelScheduledValues(Tone.now());
      gain.setValueAtTime(gain.value, Tone.now());
      gain.linearRampToValueAtTime(1, `+${time / 2}`);
    });
    const nextNotes = getSimilarNotes(
      notes.filter(() => window.generativeMusic.rng() < 0.5),
      DRONE_OCTAVES
    );
    const notesToMute = notes.filter(note => !nextNotes.includes(note));
    Tone.Transport.scheduleOnce(() => {
      notesToMute.forEach(note => {
        const { gain } = droneGainsByNote[note];
        gain.cancelScheduledValues(Tone.now());
        gain.setValueAtTime(gain.value, Tone.now());
        gain.linearRampToValueAtTime(0, `+${time / 2}`);
      });
      if (window.generativeMusic.rng() < 0.75) {
        let primarySampler;
        let primaryOctaves;
        let secondarySampler;
        let secondaryOctaves;
        if (window.generativeMusic.rng() < 0.5) {
          primarySampler = pianoSampler;
          primaryOctaves = PIANO_OCTAVES;
          secondarySampler = guitarSampler;
          secondaryOctaves = GUITAR_OCTAVES;
        } else {
          primarySampler = guitarSampler;
          primaryOctaves = GUITAR_OCTAVES;
          secondarySampler = pianoSampler;
          secondaryOctaves = PIANO_OCTAVES;
        }
        const firstPc = getPitchClass(getRandomElement(notes));
        const noteTime = 1 + window.generativeMusic.rng();
        primarySampler.triggerAttack(
          `${firstPc}${getRandomElement(primaryOctaves)}`,
          `+${noteTime}`
        );
        if (window.generativeMusic.rng() < 0.5) {
          secondarySampler.triggerAttack(
            `${firstPc}${getRandomElement(secondaryOctaves)}`,
            `+${noteTime * 3}`
          );
        }
        if (window.generativeMusic.rng() < 0.5) {
          const otherNoteTime = 3 + window.generativeMusic.rng() * 2;
          const secondPc = getPitchClass(
            getRandomElement(
              notes.filter(note => getPitchClass(note) !== firstPc)
            )
          );
          primarySampler.triggerAttack(
            `${secondPc}${getRandomElement(primaryOctaves)}`,
            `+${otherNoteTime}`
          );

          if (window.generativeMusic.rng() < 0.5) {
            secondarySampler.triggerAttack(
              `${secondPc}${getRandomElement(secondaryOctaves)}`,
              `+${otherNoteTime * 3}`
            );
          }
        }
      }
      Tone.Transport.scheduleOnce(() => {
        playChord(nextNotes);
      }, `+${time / 4}`);
    }, `+${time / 2}`);
  };

  const changeFilterFrequency = () => {
    const time = window.generativeMusic.rng() * 30 + 30;
    const frequency = window.generativeMusic.rng() * 300 + 300;
    filter.frequency.cancelScheduledValues(Tone.now());
    filter.frequency.setValueAtTime(filter.frequency.value, Tone.now());
    filter.frequency.linearRampToValueAtTime(frequency, `+${time}`);
    Tone.Transport.scheduleOnce(() => {
      changeFilterFrequency();
    }, `+${time}`);
  };

  const schedule = ({ destination }) => {
    filter.connect(destination);
    pianoSampler.connect(destination);
    guitarSampler.connect(destination);
    droneSamplers.forEach((sampler, i) => {
      const note = DRONE_NOTES[i];
      const play = () => {
        sampler.triggerAttack(note, '+1');
        Tone.Transport.scheduleOnce(() => {
          play();
        }, `+${30 - window.generativeMusic.rng() * 5}`);
      };
      play();
    });

    playChord();
    changeFilterFrequency();

    return () => {
      droneSamplers.concat([guitarSampler, pianoSampler]).forEach(sampler => {
        sampler.releaseAll(0);
      });
      Object.values(droneGainsByNote).forEach(({ gain }) => {
        gain.cancelScheduledValues(Tone.now());
        gain.setValueAtTime(0, Tone.now());
      });
    };
  };
  const deactivate = () => {
    droneSamplers
      .concat(Object.values(droneGainsByNote))
      .concat([guitarSampler, pianoSampler, filter])
      .forEach(node => {
        node.dispose();
      });
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['pulse-code-modulation'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-last-transit/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffer,
  renderBuffer,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../last-transit.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  let reverbBuffers = samples['last-transit__idling-truck'];
  if (!reverbBuffers) {
    const buffer = await createBuffer(samples['idling-truck'][0]);
    const bufferWithReverb = await renderBuffer({
      buffer,
      getDestination: () =>
        new Tone.Reverb(5)
          .set({ wet: 0.5 })
          .toDestination()
          .generate(),
      duration: buffer.duration,
    });
    buffer.dispose();
    reverbBuffers = [bufferWithReverb];
    sampleLibrary.save([['last-transit__idling-truck', reverbBuffers]]);
  }

  const [bufferWithReverb] = reverbBuffers;
  const activeSources = [];
  const vol = new Tone.Volume(10);

  const play = ({ sourceDestination, playbackRateLfo }) => {
    const source = new Tone.BufferSource(bufferWithReverb)
      .set({
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(sourceDestination);
    activeSources.push(source);
    playbackRateLfo.connect(source.playbackRate);
    source.start();
    Tone.Transport.scheduleOnce(() => {
      play({ sourceDestination, playbackRateLfo });
    }, `+${bufferWithReverb.duration / 0.25 - window.generativeMusic.rng()}`);
  };

  const schedule = ({ destination }) => {
    vol.connect(destination);
    const filter = new Tone.AutoFilter(window.generativeMusic.rng() / 30).connect(vol);
    filter.start();
    const lfo = new Tone.LFO(window.generativeMusic.rng() / 100, 0.05, 0.25);
    lfo.start();
    play({ sourceDestination: filter, playbackRateLfo: lfo });

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
      filter.dispose();
      lfo.dispose();
    };
  };

  const deactivate = () => {
    [vol, ...activeSources].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['last-transit'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/shuffle-array.js<|end_filename|>
// https://stackoverflow.com/a/2450976
const shuffle = array => {
  const newArray = array.slice();
  let currentIndex = array.length;
  let temporaryValue;
  let randomIndex;

  // While there remain elements to shuffle...
  while (currentIndex !== 0) {
    // Pick a remaining element...
    randomIndex = Math.floor(window.generativeMusic.rng() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = newArray[currentIndex];
    newArray[currentIndex] = newArray[randomIndex];
    newArray[randomIndex] = temporaryValue;
  }

  return newArray;
};

export default shuffle;


<|start_filename|>packages/piece-expand-collapse/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  toss,
  sampleNote,
  wrapActivate,
  createPrerenderableBuffers,
  getRandomElement,
} from '@generative-music/utilities';
import { sampleNames } from '../expand-collapse.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const createReversedBuffers = (buffers, samplesByNote) =>
  new Tone.ToneAudioBuffers(
    Reflect.ownKeys(samplesByNote).reduce((reverseBuffers, note) => {
      reverseBuffers[note] = buffers.get(note).slice(0);
      reverseBuffers[note].reverse = true;
      return reverseBuffers;
    }, {})
  );

const PITCH_CLASSES = ['C', 'E', 'G'];
const OCTAVES = [3, 4, 5, 6];
const NOTES = toss(PITCH_CLASSES, OCTAVES);

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const pianoSamples =
    samples['expand-collapse__vsco2-piano-mf'] || samples['vsco2-piano-mf'];

  const pianoBuffers = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    onProgress,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'expand-collapse__vsco2-piano-mf',
    getDestination: () =>
      new Tone.Reverb({ decay: 4, wet: 0.5 }).toDestination().generate(),
  });

  const reversedBuffers = createReversedBuffers(pianoBuffers, pianoSamples);
  const activeSources = [];

  const handleBufferSourceEnded = bufferSource => {
    const i = activeSources.indexOf(bufferSource);
    if (i >= 0) {
      activeSources.splice(i, 1);
    }
  };

  const sampledNotes = Object.keys(pianoSamples);

  const play = (dest, first = false) => {
    const roll = window.generativeMusic.rng();
    const time = 5 + roll * 15;
    const p = 0.4 + roll * 0.5;
    let notes = NOTES.filter(() => window.generativeMusic.rng() < p);
    while (first && notes.length === 0) {
      notes = NOTES.filter(() => window.generativeMusic.rng() < p);
    }
    const firstNote = first && getRandomElement(notes);
    notes.forEach(note => {
      const { sampledNote, playbackRate } = sampleNote({
        note,
        sampledNotes,
      });
      const buffer = pianoBuffers.get(sampledNote);
      const source = new Tone.ToneBufferSource(buffer)
        .set({ playbackRate, onended: () => handleBufferSourceEnded(source) })
        .connect(dest);
      const startTime = firstNote === note ? 1 : window.generativeMusic.rng() * time;
      const reverseBuffer = reversedBuffers.get(sampledNote);
      const reverseSource = new Tone.BufferSource(reverseBuffer)
        .set({
          playbackRate,
          onended: () => handleBufferSourceEnded(reverseSource),
        })
        .connect(dest);
      activeSources.push(source, reverseSource);
      source.start(`+${startTime}`, 0, time * 2 - startTime);

      if (reverseBuffer.duration / playbackRate > time) {
        reverseSource.start(
          `+${time + window.generativeMusic.rng() / 10}`,
          reverseBuffer.duration - time * playbackRate
        );
      } else {
        reverseSource.start(
          `+${time * 2 -
            reverseBuffer.duration / playbackRate +
            window.generativeMusic.rng() / 10}`
        );
      }
    });

    Tone.Transport.scheduleOnce(() => {
      play(dest);
    }, `+${time * 2 + window.generativeMusic.rng() + 1}`);
  };

  const schedule = ({ destination }) => {
    const feedbackDelay = new Tone.FeedbackDelay({
      delayTime: 1,
      feedback: 0.3,
      wet: 0.1,
    }).connect(destination);
    play(feedbackDelay, true);

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
      feedbackDelay.dispose();
    };
  };

  const deactivate = () => {
    pianoBuffers.dispose();
    reversedBuffers.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['expand-collapse'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-trees/trees.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Trees",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "trees",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "acoustic"
  ],
  "releaseDate": "2019-01-17T00:00:00.000Z",
  "sampleNames": [
    [
      "trees__vsco2-piano-mf",
      "vsco2-piano-mf"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/trees-excerpts"
}

<|start_filename|>packages/piece-documentary-films/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  toss,
  sortNotes,
} from '@generative-music/utilities';
import { sampleNames } from '../documentary-films.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const phrases = [
  ['A#', 'F', 'G#', 'C#'],
  ['A#', 'F', 'D'],
  ['D', 'D#', 'F'],
  ['C', 'D#', 'D'],
  ['A#', 'F', 'G', 'D'],
];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const renderedPitchClasses = sortNotes(
    Array.from(new Set(phrases.flat()))
  ).filter((_, i) => i % 2 === 0);

  const getReverb = () => new Tone.Reverb(45).toDestination().generate();

  const trumpet = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-trumpet-sus-mf',
    renderedInstrumentName: 'documentary-films__vsco2-trumpet-sus-mf',
    notes: toss(renderedPitchClasses, [2, 3]),
    onProgress: val => onProgress(val * 0.7),
    getDestination: getReverb,
  });

  const trombone = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-trombone-sus-mf',
    renderedInstrumentName: 'documentary-films__vsco2-trombone-sus-mf',
    notes: toss(renderedPitchClasses, [1]),
    onProgress: val => onProgress(val * 0.25 + 0.7),
    getDestination: getReverb,
  });

  const tuba = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-tuba-sus-mf',
    renderedInstrumentName: 'documentary-films__vsco2-tuba-sus-mf',
    notes: ['A#0'],
    onProgress: val => onProgress(val * 0.05 + 0.95),
    getDestination: getReverb,
  });

  tuba.set({ attack: 0.5, curve: 'linear' });

  const droneTuba = note => {
    tuba.triggerAttack(note, '+1');

    Tone.Transport.scheduleOnce(() => {
      droneTuba(note);
    }, `+${window.generativeMusic.rng() * 3 + 2}`);
  };

  const trumpetPhrase = () => {
    const trumpetOct = Math.floor(window.generativeMusic.rng() * 2) + 2;
    const tromboneOct = 1;
    const trumpetMultiplier = window.generativeMusic.rng() * 10 + 5;
    const tromboneMultiplier = window.generativeMusic.rng() * 10 + 5;
    const tromboneDelay = window.generativeMusic.rng() * 15 + 15;
    const phrase = phrases[Math.floor(window.generativeMusic.rng() * phrases.length)];
    const sliceLength = Math.floor(
      Math.pow(window.generativeMusic.rng(), 0.1) * phrase.length
    );
    phrase.slice(0, sliceLength).forEach((pc, i) => {
      trumpet.triggerAttack(
        `${pc}${trumpetOct}`,
        `+${1 + i * trumpetMultiplier}`
      );
      trombone.triggerAttack(
        `${pc}${tromboneOct}`,
        `${1 + i * tromboneMultiplier + tromboneDelay}`
      );
    });

    Tone.Transport.scheduleOnce(() => {
      trumpetPhrase();
    }, `+${sliceLength * trumpetMultiplier + 1 + window.generativeMusic.rng() * 20}`);
  };

  const schedule = ({ destination }) => {
    const delay = new Tone.FeedbackDelay(0.2, 0.6).connect(destination);
    const trumpetFilter = new Tone.AutoFilter(
      window.generativeMusic.rng() / 100 + 0.01
    ).connect(delay);
    trumpetFilter.start();
    trumpet.connect(trumpetFilter);
    const tromboneFilter = new Tone.AutoFilter(
      window.generativeMusic.rng() / 100 + 0.01
    ).connect(delay);
    tromboneFilter.start();
    trombone.connect(tromboneFilter);
    const tubaFilter = new Tone.AutoFilter(window.generativeMusic.rng() / 100 + 0.01).connect(
      destination
    );
    tubaFilter.start();
    tuba.connect(tubaFilter);

    droneTuba('A#0');
    trumpetPhrase();

    return () => {
      trumpet.releaseAll(0);
      trombone.releaseAll(0);
      tuba.releaseAll(0);

      delay.dispose();
      trumpetFilter.dispose();
      tromboneFilter.dispose();
      tubaFilter.dispose();
    };
  };

  const deactivate = () => {
    [trumpet, trombone, tuba].forEach(node => node.dispose());
  };
  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['documentary-films'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-agua-ravine/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  getRandomElement,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../agua-ravine.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const phrase1 = [
  ['G4', 'B4', 'E5'],
  ['F4', 'A4', 'D5'],
  ['E4', 'G4', 'C5'],
  ['D4', 'F4', 'B4'],
  ['C4', 'E4', 'A4'],
  ['B3', 'D4', 'G4'],
  ['A3', 'C4', 'F4'],
  ['G3', 'B3', 'E4'],
];
const phrase2 = [['C2'], ['C3'], ['G2']];
const phrase3 = [['C4'], ['G4'], ['C5'], ['C4', 'G4', 'B4']];
const phrase4 = [['C3'], ['G3'], ['C4'], ['F3'], ['E3']];
const phrase5 = [['C2'], ['A2'], ['C3']];
const phrase6 = [['D3'], ['G3'], ['D4'], ['A4'], ['C5']];
const phrase7 = [['D4', 'G4', 'C5'], ['C4', 'G4', 'C5']];

const phrases = [phrase1, phrase2, phrase3, phrase4, phrase5, phrase6, phrase7];

const vibeNotes = ['C3', 'C4', 'C5', 'G3', 'G4', 'G5'];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const piano = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: Array.from(new Set(phrases.flat(2))).filter(
      ([pc]) => pc === 'C' || pc === 'E' || pc === 'G' || pc === 'A'
    ),
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'agua-ravine__vsco2-piano-mf',
    onProgress: val => onProgress(val * 0.5),
    getDestination: () =>
      new Tone.Reverb(15)
        .set({ wet: 0.5 })
        .toDestination()
        .generate(),
  });
  const vibes = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: vibeNotes,
    sourceInstrumentName: 'vcsl-vibraphone-soft-mallets-mp',
    renderedInstrumentName: 'agua-ravine__vcsl-vibraphone-soft-mallets-mp',
    onProgress: val => onProgress(val * 0.5 + 0.5),
    getDestination: () =>
      new Tone.Reverb(30)
        .set({ wet: 0.8 })
        .toDestination()
        .generate(),
  });

  const playPhrase = (phrase = getRandomElement(phrases)) => {
    const noteTime = (window.generativeMusic.rng() * 2 + 3) / phrase.length;
    (window.generativeMusic.rng() < 0.25 ? phrase.slice(0).reverse() : phrase)
      .slice(0, Math.ceil(window.generativeMusic.rng() * phrase.length - 1))
      .forEach((notes, i) => {
        notes.forEach(note => {
          piano.triggerAttack(
            note,
            `+${(1 + i + window.generativeMusic.rng() / 20 - 0.025) * noteTime +
              window.generativeMusic.rng() / 10 -
              0.05}`
          );
        });
      });

    Tone.Transport.scheduleOnce(() => {
      playPhrase();
    }, `+${window.generativeMusic.rng() * 5 + 3}`);
  };

  const playVibes = () => {
    Tone.Transport.scheduleOnce(() => {
      const vibeNote1 = getRandomElement(vibeNotes);
      const vibeNote2 = getRandomElement(
        vibeNotes.filter(note => note !== vibeNote1)
      );
      [vibeNote1, vibeNote2].forEach(note => {
        vibes.triggerAttack(note, `+${1 + window.generativeMusic.rng() / 10 - 0.05}`);
      });
      playVibes();
    }, `+${window.generativeMusic.rng() * 40 + 20}`);
  };

  const schedule = ({ destination }) => {
    vibes.connect(destination);
    piano.connect(destination);
    playPhrase();
    playVibes();
    return () => {
      piano.releaseAll(0);
      vibes.releaseAll(0);
    };
  };

  const deactivate = () => {
    [piano, vibes].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['agua-ravine'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-little-bells/little-bells.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Little Bells",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "little-bells",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "acoustic"
  ],
  "releaseDate": "2018-11-22T00:00:00.000Z",
  "sampleNames": [
    [
      "little-bells__vsco2-glock",
      "vsco2-glock"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/little-bells-excerpts"
}

<|start_filename|>packages/utilities/src/render-buffer.js<|end_filename|>
import { ToneBufferSource } from 'tone';
import createPrerenderedBuffer from './create-prerendered-buffer';

const renderBuffer = ({
  buffer,
  getDestination,
  duration,
  bufferSourceOptions,
}) => {
  const createSource = async () => {
    const destination = await getDestination();
    const bufferSource = new ToneBufferSource(
      Object.assign({}, bufferSourceOptions, { url: buffer })
    );
    bufferSource.connect(destination);
    const start = () => {
      bufferSource.start();
    };
    const dispose = () => {
      bufferSource.dispose();
      destination.dispose();
    };
    return { start, dispose };
  };
  return createPrerenderedBuffer({ createSource, duration });
};

export default renderBuffer;


<|start_filename|>packages/piece-observable-streams/src/operators/octaved.js<|end_filename|>
import { of, from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { getOctave, getPitchClass } from '@generative-music/utilities';

const octaved = ({ p, octaveChange, notes }) => source => {
  const noteSet = new Set(notes);
  return source.pipe(
    mergeMap(note =>
      noteSet.has(note) && window.generativeMusic.rng() < p
        ? from([
            note,
            `${getPitchClass(note)}${getOctave(note) + octaveChange}`,
          ])
        : of(note)
    )
  );
};

export default octaved;


<|start_filename|>packages/utilities/src/theory/intervals.js<|end_filename|>
export const P1 = 0;
export const d2 = 0;
export const m2 = 1;
export const A1 = 1;
export const M2 = 2;
export const d3 = 2;
export const m3 = 3;
export const A2 = 3;
export const M3 = 4;
export const d4 = 4;
export const P4 = 5;
export const A3 = 5;
export const d5 = 6;
export const A4 = 6;
export const P5 = 7;
export const d6 = 7;
export const m6 = 8;
export const A5 = 8;
export const M6 = 9;
export const d7 = 9;
export const m7 = 10;
export const A6 = 10;
export const M7 = 11;
export const d8 = 11;
export const P8 = 12;
export const A7 = 12;


<|start_filename|>packages/piece-lemniscate/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createSampler,
  wrapActivate,
  minor7th,
} from '@generative-music/utilities';
import combineNotesWithOctaves from './combine-notes-with-octaves';
import { sampleNames } from '../lemniscate.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const TONIC = 'A#';
const OCTAVES = [2, 3, 4, 5, 6];

const notes = combineNotesWithOctaves([TONIC], OCTAVES).reduce(
  (allNotes, note) => allNotes.concat(minor7th(note)),
  []
);

const INSTRUMENT_NAME = 'vsco2-piano-mf';
const MIN_REPEAT_S = 30;
const MAX_REPEAT_S = 80;

const generateTiming = (instruments, getPlayProbability) => {
  notes.forEach(note => {
    const interval =
      window.generativeMusic.rng() * (MAX_REPEAT_S - MIN_REPEAT_S) + MIN_REPEAT_S;
    const delay = interval - MIN_REPEAT_S;
    Tone.Transport.scheduleRepeat(
      () => {
        const random = window.generativeMusic.rng();
        const probability = getPlayProbability();
        if (random <= probability) {
          instruments.forEach(instrument =>
            instrument.triggerAttack(note, '+1')
          );
        }
      },
      interval,
      delay
    );
  });
};

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const rightPanner = new Tone.Panner();
  const leftPanner = new Tone.Panner();
  const pianoSamples = samples[INSTRUMENT_NAME];
  const instruments = await Promise.all([
    createSampler(pianoSamples),
    createSampler(pianoSamples),
  ]);

  const schedule = ({ destination }) => {
    [rightPanner, leftPanner].forEach(panner => {
      panner.connect(destination);
    });
    const primaryControlLfo = new Tone.LFO(1 / 480).set({ phase: 270 });

    const negate = new Tone.Negate();
    const rightPanSignal = new Tone.Add(1);

    primaryControlLfo.chain(negate, rightPanSignal);
    rightPanSignal.connect(rightPanner.pan);

    const leftPanSignal = new Tone.Negate();
    rightPanSignal.connect(leftPanSignal);
    leftPanSignal.connect(leftPanner.pan);

    const [firstInstrument, secondInstrument] = instruments;
    firstInstrument.chain(rightPanner);
    secondInstrument.chain(leftPanner);

    const lfoMeter = new Tone.Meter({ normalRange: true });
    primaryControlLfo.connect(lfoMeter);

    generateTiming(
      [firstInstrument, secondInstrument],
      () => lfoMeter.getValue(),
      'both'
    );
    generateTiming([firstInstrument], () => 1 - lfoMeter.getValue());
    generateTiming([secondInstrument], () => 1 - lfoMeter.getValue());

    primaryControlLfo.start();

    return () => {
      [
        primaryControlLfo,
        negate,
        rightPanSignal,
        leftPanSignal,
        lfoMeter,
      ].forEach(node => node.dispose());
      [firstInstrument, secondInstrument].forEach(sampler =>
        sampler.releaseAll(0)
      );
    };
  };

  const deactivate = () => {
    instruments
      .concat([leftPanner, rightPanner])
      .forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['lemniscate'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/create-prerenderable-instrument.js<|end_filename|>
import createPrerenderedBuffer from './create-prerendered-buffer';
import createSampler from './create-sampler';
import noop from './utilities/noop';

const createPrerenderedInstrument = async ({
  createInstrument,
  notes,
  noteDuration,
  sampleLibrary,
  samples,
  renderedInstrumentName,
  onProgress = noop,
}) => {
  if (samples[renderedInstrumentName]) {
    return createSampler(samples[renderedInstrumentName]);
  }

  let renderedCount = 0;
  const noteBuffers = await Promise.all(
    notes.map(async note => {
      const createSourceForNote = async context => {
        const { instrument, dispose } = await Promise.resolve(
          createInstrument(context)
        );
        const start = () => {
          instrument.triggerAttackRelease(note, noteDuration);
        };
        return { start, dispose };
      };
      const renderedBuffer = await createPrerenderedBuffer({
        createSource: createSourceForNote,
        duration: noteDuration,
      });
      renderedCount += 1;
      onProgress(renderedCount / notes.length);
      return renderedBuffer;
    })
  );
  const noteBuffersByNote = noteBuffers.reduce((byNote, buffer, i) => {
    const note = notes[i];
    byNote[note] = buffer;
    return byNote;
  }, {});
  sampleLibrary.save([[renderedInstrumentName, noteBuffersByNote]]);
  return createSampler(noteBuffersByNote);
};

export default createPrerenderedInstrument;


<|start_filename|>packages/piece-stream-of-consciousness/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffers,
  createPrerenderableSampler,
  createPrerenderableBuffers,
  createPrerenderableSampledBuffers,
  wrapActivate,
  toss,
  getPitchClass,
  getOctave,
  getRandomElement,
  sampleNote,
} from '@generative-music/utilities';
import { sampleNames } from '../stream-of-consciousness.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const chords = [
  ['C3', 'E3', 'G3', 'B3'],
  ['C3', 'Eb3', 'G3', 'B3'],
  ['B2', 'D3', 'G3', 'B3'],
  ['A2', 'C#3', 'G3', 'A3'],
];

const nextChordMap = new Map([
  [0, [0, 1, 3]],
  [1, [0, 2, 3]],
  [2, [0, 3]],
  [3, [0, 2]],
]);

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const stirBuffers = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'snare-brush-stir',
    renderedInstrumentName: 'stream-of-consciousness__snare-brush-stir',
    getDestination: () =>
      new Tone.Reverb(15)
        .set({ wet: 0.6 })
        .toDestination()
        .generate(),
    onProgress: val => onProgress(val / 3),
  });

  const hitBuffers = await createBuffers(samples['snare-brush-hit-p']);
  const rideBuffers = await createBuffers(samples['ride-brush-p']);
  const renderedPitchClasses = ['C', 'E', 'G'];
  const renderedReversePianoNotes = toss(renderedPitchClasses, [3, 4]);

  const reversePianoBuffers = await createPrerenderableSampledBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'stream-of-consciousness__vsco2-piano-mf-reverse',
    notes: renderedReversePianoNotes,
    reverse: true,
    getDestination: () =>
      new Tone.Reverb(30)
        .set({ wet: 0.75 })
        .toDestination()
        .generate(),
    onProgress: val => onProgress((val + 1) / 3),
  });

  const lowPiano = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'stream-of-consciousness__vsco2-piano-mf-low',
    notes: toss(renderedPitchClasses, [5, 6, 7]),
    getDestination: () =>
      new Tone.Reverb(30)
        .set({ wet: 0.75 })
        .toDestination()
        .generate(),
    pitchShift: -24,
    onProgress: val => onProgress((val + 2) / 3),
  });

  const activeSources = [];
  const stirVol = new Tone.Volume(-5);
  const stirBuffer = stirBuffers.get(0);
  const stir = () => {
    const source = new Tone.ToneBufferSource(stirBuffer)
      .set({
        curve: 'linear',
        fadeIn: 5,
        fadeOut: 5,
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(stirVol);
    activeSources.push(source);
    source.start('+1', 0, stirBuffer.duration - 5);
    Tone.Transport.scheduleOnce(() => {
      stir();
    }, `+${stirBuffer.duration - 10 - window.generativeMusic.rng() * 5}`);
  };

  const hitVol = new Tone.Volume(-4);

  const randomHit = (time = '+1') => {
    const randomBuffer = hitBuffers.get(
      Math.floor(window.generativeMusic.rng() * samples['snare-brush-hit-p'].length)
    );
    const hitSource = new Tone.ToneBufferSource(randomBuffer)
      .set({
        playbackRate: window.generativeMusic.rng() * 0.1 + 0.95,
        onended: () => {
          const i = activeSources.indexOf(hitSource);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(hitVol);
    activeSources.push(hitSource);
    hitSource.start(time);
  };

  const randomRide = (time = '+1') => {
    const randomBuffer = rideBuffers.get(
      Math.floor(window.generativeMusic.rng() * samples['ride-brush-p'].length)
    );
    const source = new Tone.ToneBufferSource(randomBuffer)
      .set({
        playbackRate: window.generativeMusic.rng() * 0.1 + 0.95,
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(hitVol);
    activeSources.push(source);
    source.start(time);
  };

  const synth = new Tone.Synth({ oscillator: { type: 'sine' } }).set({
    volume: -7,
  });

  const playReversePiano = (note, time, duration, dest) => {
    const { sampledNote, playbackRate } = sampleNote({
      sampledNotes: renderedReversePianoNotes,
      note,
    });

    const buffer = reversePianoBuffers.get(sampledNote);
    const source = new Tone.ToneBufferSource(buffer).set({
      playbackRate,
      onended: () => {
        const i = activeSources.indexOf(source);
        if (i >= 0) {
          activeSources.splice(i, 1);
        }
      },
    });
    activeSources.push(source);
    source.connect(dest);
    const bufferDuration = buffer.duration;
    const adjustedDuration = duration * playbackRate;
    if (bufferDuration > adjustedDuration) {
      const offset = bufferDuration - adjustedDuration;
      source.start(time, offset);
    } else {
      const waitTime = duration - bufferDuration / playbackRate;
      Tone.Transport.scheduleOnce(() => {
        source.start(time);
      }, `+${waitTime}`);
    }
  };

  let chordI = 0;
  let measure = 0;
  const playMeasure = (
    reversePianoDestination,
    bpm = window.generativeMusic.rng() * 40 + 60,
    up = window.generativeMusic.rng() < 0.5
  ) => {
    const beats = 8;
    const spb = (1 / bpm) * 60;
    const chord = chords[chordI];
    const chordCopy = chord.slice(0);
    const arpeggio = [];
    while (chordCopy.length) {
      const i = Math.floor(window.generativeMusic.rng() * chordCopy.length);
      const note = chordCopy[i];
      chordCopy.splice(i, 1);
      arpeggio.push(note);
    }
    for (let i = 0; i < beats; i += 1) {
      const time = 1 + i * spb + (window.generativeMusic.rng() * 0.005 - 0.0025);
      if (i === 0) {
        synth.triggerAttackRelease('C2', 0.05, `+${time}`);
        chord
          .filter(() => window.generativeMusic.rng() < 0.9)
          .forEach(note => {
            playReversePiano(
              note,
              `+${time}`,
              spb * 2 + window.generativeMusic.rng() * 0.5,
              reversePianoDestination
            );
          });
        if (window.generativeMusic.rng() < 0.25) {
          chord
            .filter(() => window.generativeMusic.rng() < 0.9)
            .forEach(note => {
              const pc = getPitchClass(note);
              const oct = getOctave(note);
              playReversePiano(
                `${pc}${oct + 1}`,
                `+${time}`,
                spb * 2 + window.generativeMusic.rng() * 0.5,
                reversePianoDestination
              );
            });
        }
        const nextChordIndicies = nextChordMap.get(chordI);
        chordI = getRandomElement(nextChordIndicies);
      } else if (i === 2 || i === 6) {
        randomHit(`+${time}`);
      } else if ((i === 3 || i === 7) && window.generativeMusic.rng() < 0.3) {
        randomHit(`+${time + spb / 2}`);
      }
      if (i === 7 && window.generativeMusic.rng() < 0.1) {
        synth.triggerAttackRelease('C2', 0.05, `+${time + spb / 2}`);
      }
      randomRide(`+${time}`);
      const note = arpeggio[i % arpeggio.length];
      const pc = getPitchClass(note);
      const oct = getOctave(note);
      const pianoDelay = window.generativeMusic.rng() * 0.1 - 0.05;
      lowPiano.triggerAttack(`${pc}${oct + 3}`, `+${time + pianoDelay}`);
      lowPiano.triggerAttack(`${pc}${oct + 4}`, `+${time + pianoDelay}`);
    }

    if (measure === 2) {
      measure = 0;
    } else {
      measure += 1;
    }

    Tone.Transport.scheduleOnce(() => {
      if (bpm >= 100) {
        playMeasure(
          reversePianoDestination,
          bpm * (1 - window.generativeMusic.rng() * 0.001),
          false
        );
      } else if (bpm <= 60) {
        playMeasure(
          reversePianoDestination,
          bpm * (1 + window.generativeMusic.rng() * 0.001),
          true
        );
      } else if (up) {
        playMeasure(
          reversePianoDestination,
          bpm * (1 + window.generativeMusic.rng() * 0.001),
          true
        );
      } else {
        playMeasure(
          reversePianoDestination,
          bpm * (1 - window.generativeMusic.rng() * 0.001),
          false
        );
      }
    }, `+${spb * beats}`);
  };

  const schedule = ({ destination }) => {
    const percussionAutoFilter = new Tone.AutoFilter(
      window.generativeMusic.rng() / 500 + 0.005,
      150,
      6
    ).connect(destination);
    percussionAutoFilter.start();

    hitVol.connect(percussionAutoFilter);
    synth.connect(percussionAutoFilter);

    const pianoAutoFilter = new Tone.AutoFilter(
      window.generativeMusic.rng() / 500 + 0.005,
      150,
      6
    ).connect(destination);
    pianoAutoFilter.start();

    lowPiano.connect(pianoAutoFilter);

    const stirAutoFilter = new Tone.AutoFilter(
      window.generativeMusic.rng() / 500 + 0.005,
      150,
      6
    );
    stirAutoFilter.start();
    stirAutoFilter.connect(destination);
    stirVol.connect(stirAutoFilter);

    stir();
    playMeasure(pianoAutoFilter);

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
      lowPiano.releaseAll(0);
      [percussionAutoFilter, pianoAutoFilter, stirAutoFilter].forEach(node =>
        node.dispose()
      );
    };
  };

  const deactivate = () => {
    [
      stirBuffers,
      hitBuffers,
      rideBuffers,
      reversePianoBuffers,
      lowPiano,
      synth,
      stirVol,
      hitVol,
    ].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['stream-of-consciousness'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>scripts/add-bandcamp-urls.js<|end_filename|>
'use strict';

const fsp = require('fs').promises;
const glob = require('glob');
const bandcampScraper = require('bandcamp-scraper');

const getManifests = () =>
  new Promise((resolve, reject) => {
    glob('./packages/*/*.gfm.manifest.json', (err, files) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(files);
    });
  });

const getAlbumUrls = () =>
  new Promise((resolve, reject) => {
    bandcampScraper.getAlbumUrls(
      'http://alexbainter.bandcamp.com/',
      (err, urls) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(urls);
      }
    );
  });

Promise.all([getManifests(), getAlbumUrls()]).then(
  ([manifestPaths, albumUrls]) => {
    const unmatchedUrls = albumUrls.filter(
      url => !url.includes('corruption-loops')
    );
    const unmatchedIds = [];
    const newManifests = manifestPaths.map(relativePath => {
      const manifest = require(relativePath.replace('./', '../'));
      const { id } = manifest;
      const urlIndex = unmatchedUrls.findIndex(url =>
        url.startsWith(`http://alexbainter.bandcamp.com/album/${id}-excerpts`)
      );
      if (urlIndex === -1) {
        unmatchedIds.push(id);
        return [relativePath, Object.assign({}, manifest)];
      }
      const [url] = unmatchedUrls.splice(urlIndex, 1);
      return [relativePath, Object.assign({}, manifest, { bandcampUrl: url })];
    });
    console.log(unmatchedIds);
    return Promise.all(
      newManifests.map(([relativePath, manifest]) =>
        fsp.writeFile(relativePath, JSON.stringify(manifest, null, 2))
      )
    );
  }
);


<|start_filename|>packages/utilities/src/create-reverse-sampler.js<|end_filename|>
import { ToneAudioBuffer } from 'tone';
import createBuffers from './create-buffers';
import createSampler from './create-sampler';

const createReverseSampler = async (urlMap, opts = {}) => {
  const buffers = await createBuffers(urlMap);
  const keys = Object.keys(urlMap);
  const reverseBuffersByKey = keys.reduce(
    (byKey, key) => {
      const buffer = ToneAudioBuffer.fromArray(buffers.get(key).toArray());
      buffer.reverse = true;
      byKey[key] = buffer;
      return byKey;
    },
    Array.isArray(urlMap) ? [] : {}
  );
  buffers.dispose();
  return createSampler(reverseBuffersByKey, opts);
};

export default createReverseSampler;


<|start_filename|>packages/piece-substrate/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableSampler,
  wrapActivate,
  shuffleArray,
  getPitchClass,
  getOctave,
  minor7th,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../substrate.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const OCTAVES = [3, 4];
const getNotes = tonic =>
  OCTAVES.reduce(
    (notes, octave) => notes.concat(minor7th(`${tonic}${octave}`)),
    []
  );

const swapTwo = arr => {
  const copy = arr.slice(0);
  const index1 = Math.floor(window.generativeMusic.rng() * copy.length);
  let index2 = index1;
  while (index1 === index2) {
    index2 = Math.floor(window.generativeMusic.rng() * copy.length);
  }
  const tmp = copy[index1];
  copy[index1] = copy[index2];
  copy[index2] = tmp;
  return copy;
};

function* makeNoteGenerator(notes) {
  for (let i = 0; i < notes.length; i += 1) {
    yield notes[i];
  }
}

const PITCH_CLASSES = [
  'C',
  'C#',
  'D',
  'D#',
  'E',
  'F',
  'F#',
  'G',
  'G#',
  'A',
  'A#',
  'B',
];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const getReverb = () =>
    new Tone.Reverb(20)
      .set({ wet: 0.5 })
      .toDestination()
      .generate();

  const renderedMarimbaNotes = toss(PITCH_CLASSES, OCTAVES.concat([5]))
    .slice(0, -2)
    .filter((_, i) => i % 3 === 0);
  const marimba = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: renderedMarimbaNotes,
    sourceInstrumentName: 'vsco2-marimba',
    renderedInstrumentName: 'substrate__vsco2-marimba',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.5),
    pitchShift: -24,
  });
  const piano = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: renderedMarimbaNotes.map(
      note => `${getPitchClass(note)}${getOctave(note) + 1}`
    ),
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'substrate__vsco2-piano-mf',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.5 + 0.5),
    pitchShift: -12,
  });
  marimba.set({
    attack: 0.3,
    curve: 'linear',
  });

  piano.set({
    attack: 0.25,
    curve: 'linear',
  });

  const playAndScheduleNext = (noteGenerator, notes) => {
    const next = noteGenerator.next();
    if (!next.done) {
      marimba.triggerAttack(next.value, '+1');
      const pc = getPitchClass(next.value);
      const oct = getOctave(next.value);
      if (window.generativeMusic.rng() < 0.5) {
        const delay =
          window.generativeMusic.rng() < 0.5
            ? 1
            : window.generativeMusic.rng() * 2 + 1;
        piano.triggerAttack(`${pc}${oct + 1}`, `+${delay}`);
      }

      Tone.Transport.scheduleOnce(() => {
        playAndScheduleNext(noteGenerator, notes);
      }, `+${3 + window.generativeMusic.rng()}`);
    } else {
      Tone.Transport.scheduleOnce(() => {
        const newNotes = swapTwo(notes);
        const newNoteGenerator = makeNoteGenerator(newNotes);
        playAndScheduleNext(newNoteGenerator, newNotes);
      }, `+${window.generativeMusic.rng() + 4}`);
    }
  };

  const schedule = ({ destination }) => {
    marimba.connect(destination);
    piano.connect(destination);
    const tonic =
      PITCH_CLASSES[
        Math.floor(window.generativeMusic.rng() * PITCH_CLASSES.length)
      ];
    const notes = shuffleArray(getNotes(tonic));
    const noteGenerator = makeNoteGenerator(notes);

    playAndScheduleNext(noteGenerator, notes);

    return () => {
      marimba.releaseAll(0);
      piano.releaseAll(0);
    };
  };

  const deactivate = () => {
    marimba.dispose();
    piano.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['substrate'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>scripts/tag-count.js<|end_filename|>
'use strict';

const glob = require('glob');
const fsp = require('fs').promises;

const globPromise = pattern =>
  new Promise((resolve, reject) => {
    glob(pattern, (err, filenames) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(filenames);
    });
  });

globPromise('./packages/*/*.gfm.manifest.json').then(manifestFilenames =>
  Promise.all(
    manifestFilenames.map(manifestFilename =>
      fsp
        .readFile(manifestFilename, 'utf8')
        .then(content => JSON.parse(content))
    )
  ).then(manifests => {
    const tagCounts = manifests.reduce((map, { tags }) => {
      tags.forEach(tag => {
        const currentCount = map.get(tag) || 0;
        map.set(tag, currentCount + 1);
      });
      return map;
    }, new Map());
    console.log(Array.from(tagCounts).sort((a, b) => a[1] - b[1]));
  })
);


<|start_filename|>packages/piece-impact/src/arpeggiate-once.js<|end_filename|>
import { getRandomNumberBetween } from '@generative-music/utilities';

const arpeggiateOnce = ({ instrument, notes, withinTime, velocity = 1 }) => {
  notes.forEach(note => {
    const time = getRandomNumberBetween(0, withinTime);
    instrument.triggerAttack(note, `+${time}`, velocity);
  });
};

export default arpeggiateOnce;


<|start_filename|>packages/utilities/src/theory/get-pitch-class.js<|end_filename|>
const getPitchClass = (note = '') => {
  const match = note.match(/([abcdefg][#b]?)\d*/i);
  if (!match) {
    return null;
  }
  return match[1];
};

export default getPitchClass;


<|start_filename|>packages/piece-eyes-closed/eyes-closed.gfm.manifest.json<|end_filename|>
{
  "title": "Eyes Closed",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "eyes-closed",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "electronic",
    "calm",
    "dream"
  ],
  "releaseDate": "2019-10-27T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    "dan-tranh-gliss-ps",
    "vsco2-piano-mf"
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/eyes-closed-excerpts"
}

<|start_filename|>packages/piece-observable-streams/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import { Scale, Chord } from 'tonal';
import { of, from, Observable } from 'rxjs';
import { repeat, mergeMap } from 'rxjs/operators';
import {
  createPrerenderableSampler,
  wrapActivate,
  toss,
  getPitchClass,
  getOctave,
} from '@generative-music/utilities';
import { sampleNames } from '../observable-streams.gfm.manifest.json';
import octaved from './operators/octaved';
import shortTermThrottleByNote from './operators/short-term-throttle-by-note';
import gainAdjustments from '../../../normalize/gain.json';

const TONIC = 'C';
const SCALE = 'major';
const NOTES = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4'];
const COR_ANGALAIS_NOTES = toss(['C', 'E', 'G'], [4, 5]);

const CHORDS_BY_NOTE = Scale.chords(SCALE)
  .filter(name => name !== '5' && name !== '64')
  .reduce(
    (chords, name) => chords.concat([Chord.notes(`${TONIC}4${name}`)]),
    []
  )
  .reduce((map, chord) => {
    chord.forEach(note => {
      if (map.has(note)) {
        map.set(note, map.get(note).concat([chord]));
      } else {
        map.set(note, [chord]);
      }
    });
    return map;
  }, new Map());

const minDelay = 700;

const getDelayTimeInMS = () => window.generativeMusic.rng() * 10000 + minDelay;

const scheduledNote = (noteArg, timeArg) =>
  Observable.create(observer => {
    let note = noteArg;
    let time = timeArg;
    if (typeof note === 'undefined') {
      note = NOTES[Math.floor(window.generativeMusic.rng() * NOTES.length)];
    }
    if (typeof time === 'undefined') {
      time = getDelayTimeInMS() / 1000;
    }
    Tone.Transport.scheduleOnce(() => {
      observer.next(note);
      observer.complete();
    }, `+${time}`);
  });

const humanize = () => source =>
  source.pipe(mergeMap(note => scheduledNote(note, window.generativeMusic.rng() / 10)));

const delayed = () => source =>
  source.pipe(
    mergeMap(note => {
      if (window.generativeMusic.rng() < 0.2) {
        return scheduledNote(note);
      }
      return of(note);
    })
  );

const chord = p => source =>
  source.pipe(
    mergeMap(note => {
      if (CHORDS_BY_NOTE.has(note) && window.generativeMusic.rng() < p) {
        const chordsWithNote = CHORDS_BY_NOTE.get(note);
        return from(
          chordsWithNote[Math.floor(window.generativeMusic.rng() * chordsWithNote.length)]
        );
      }
      return of(note);
    })
  );

const renderedPitchClassSet = new Set(['C', 'E', 'G', 'B']);
const renderedPianoNotes = Array.from(
  new Set(
    Array.from(CHORDS_BY_NOTE)
      .map(([, chords]) => chords)
      .flat(2)
      .concat(NOTES)
      .concat(
        NOTES.map(note =>
          [1, -1, -2].map(octChange => {
            const pc = getPitchClass(note);
            const oct = getOctave(note) + octChange;
            return `${pc}${oct}`;
          })
        ).flat()
      )
  )
).filter(note => renderedPitchClassSet.has(note[0]));

const renderedViolinNotes = renderedPianoNotes.filter(([, oct]) => oct > 3);

const notes$ = scheduledNote().pipe(
  repeat(),
  octaved({ p: 0.2, octaveChange: 1, notes: NOTES }),
  octaved({ p: 0.2, octaveChange: -1, notes: NOTES }),
  octaved({ p: 0.4, octaveChange: -2, notes: NOTES }),
  delayed(),
  shortTermThrottleByNote(3),
  chord(0.2),
  humanize()
);

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const violinVol = new Tone.Volume(-15);
  const corAnglaisVol = new Tone.Volume(-40);

  const piano = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: renderedPianoNotes,
    sourceInstrumentName: 'vsco2-piano-mf',
    renderedInstrumentName: 'observable-streams__vsco2-piano-mf',
    additionalRenderLength: 3,
    getDestination: () =>
      Promise.resolve(
        new Tone.Freeverb({ roomSize: 0.5, wet: 0.6 }).toDestination()
      ),
    onProgress: val => onProgress(val * 0.33),
  });

  const violin = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: renderedViolinNotes,
    sourceInstrumentName: 'vsco2-violin-arcvib',
    renderedInstrumentName: 'observable-streams__vsco2-violin-arcvib',
    additionalRenderLength: 0,
    getDestination: () =>
      Promise.resolve(
        new Tone.Freeverb({ roomSize: 0.9, wet: 1 }).toDestination()
      ),
    onProgress: val => onProgress(0.33 + val * 0.33),
    bufferSourceOptions: {
      fadeOut: 4,
      curve: 'linear',
    },
  });

  const corAnglais = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: COR_ANGALAIS_NOTES,
    sourceInstrumentName: 'sso-cor-anglais',
    renderedInstrumentName: 'observable-streams__sso-cor-anglais',
    additionalRenderLength: 3,
    getDestination: () =>
      Promise.resolve(
        new Tone.Freeverb({ roomSize: 0.9, wet: 1 }).toDestination()
      ),
    onProgress: val => onProgress(0.66 + val * 0.33),
  });

  violin.connect(violinVol);
  corAnglais.connect(corAnglaisVol);

  const schedule = ({ destination }) => {
    piano.connect(destination);
    const violinDelay = new Tone.FeedbackDelay({
      feedback: 0.75,
      delayTime: 0.08,
      wet: 0.5,
    });

    const corAnglaisDelay1 = new Tone.FeedbackDelay({
      feedback: 0.75,
      delayTime: 0.5,
      wet: 0.5,
    });
    const corAnglaisDelay2 = new Tone.FeedbackDelay({
      feedback: 0.7,
      delayTime: 5,
      maxDelay: 5,
      wet: 0.5,
    });

    violinVol.chain(violinDelay, destination);
    corAnglaisVol.chain(corAnglaisDelay1, corAnglaisDelay2, destination);

    const intervals = COR_ANGALAIS_NOTES.map(() => window.generativeMusic.rng() * 10 + 10);
    const minInterval = Math.min(...intervals);
    COR_ANGALAIS_NOTES.forEach((note, i) => {
      const interval = intervals[i];
      Tone.Transport.scheduleRepeat(
        () => corAnglais.triggerAttack(note, '+1'),
        interval,
        interval - minInterval
      );
    });
    let lastViolinTimeS = Tone.now();
    const noteSubscription = notes$.subscribe(note => {
      if (
        window.generativeMusic.rng() < 0.1 &&
        getOctave(note) > 3 &&
        Tone.now() - lastViolinTimeS > 20
      ) {
        lastViolinTimeS = Tone.now();
        violin.triggerAttack(note, '+1');
      } else {
        piano.triggerAttack(note, '+1');
      }
    });

    return () => {
      [violinDelay, corAnglaisDelay1, corAnglaisDelay2].forEach(node => {
        node.dispose();
      });
      [violin, corAnglais, piano].forEach(sampler => {
        sampler.releaseAll(0);
      });
      noteSubscription.unsubscribe();
    };
  };

  const deactivate = () => {
    [piano, violin, corAnglais].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['observable-streams'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/create-prerendered-buffer.js<|end_filename|>
import { Offline } from 'tone';

const _createPrerenderedBuffer = async ({ createSource, duration }) => {
  let disposeSource;
  const renderedBufer = await Offline(async offlineContext => {
    const { start, dispose } = await Promise.resolve(
      createSource(offlineContext)
    );
    disposeSource = dispose;
    start();
  }, duration);
  disposeSource();
  return renderedBufer;
};

const queue = [];
const createPrerenderedBuffer = options =>
  new Promise(resolve => {
    const renderFn = async () => {
      const renderedBuffer = await _createPrerenderedBuffer(options);
      const index = queue.indexOf(renderFn);
      queue.splice(index, 1);
      resolve(renderedBuffer);
      if (queue.length > 0) {
        queue[0]();
      }
    };
    queue.push(renderFn);
    if (queue.length === 1) {
      renderFn();
    }
  });

export default createPrerenderedBuffer;


<|start_filename|>packages/piece-420hz-gamma-waves-for-big-brain/420hz-gamma-waves-for-big-brain.gfm.manifest.json<|end_filename|>
{
  "title": "420hz Gamma Waves for Big Brain",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "420hz-gamma-waves-for-big-brain",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "dream",
    "electronic",
    "drone"
  ],
  "releaseDate": "2020-01-21T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    "waves",
    "vsco2-piano-mf",
    "sso-chorus-male"
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/420hz-gamma-waves-for-big-brain-excerpts"
}

<|start_filename|>packages/utilities/src/create-sampler.js<|end_filename|>
import * as Tone from 'tone';

const createSampler = (urlMap, opts = {}) => {
  const urls = Array.isArray(urlMap) ? urlMap : Object.values(urlMap);
  if (
    urls.every(
      url => url instanceof AudioBuffer || url instanceof Tone.ToneAudioBuffer
    )
  ) {
    return Promise.resolve(new Tone.Sampler(urlMap, opts));
  }
  return new Promise(resolve => {
    const sampler = new Tone.Sampler(
      urlMap,
      Object.assign({}, opts, {
        onload: () => resolve(sampler),
      })
    );
  });
};

export default createSampler;


<|start_filename|>packages/utilities/src/toss.js<|end_filename|>
const toss = (pitchClasses = [], octaves = []) =>
  octaves.reduce(
    (notes, octave) => notes.concat(pitchClasses.map(pc => `${pc}${octave}`)),
    []
  );

export default toss;


<|start_filename|>packages/piece-bhairav/bhairav.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Bhairav",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "bhairav",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "acoustic"
  ],
  "releaseDate": "2019-07-28T00:00:00.000Z",
  "sampleNames": [
    [
      "bhairav__vsco2-piano-mf",
      "vsco2-piano-mf"
    ],
    [
      "bhairav__vsco2-cellos-susvib-mp",
      "vsco2-cellos-susvib-mp"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/bhairav-excerpts"
}

<|start_filename|>packages/piece-western-medicine/western-medicine.gfm.manifest.json<|end_filename|>
{
  "title": "Western Medicine",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "western-medicine",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "electronic"
  ],
  "releaseDate": "2019-09-30T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "western-medicine__guitar-harmonics",
      "guitar-harmonics"
    ],
    [
      "western-medicine__vsco2-marimba",
      "vsco2-marimba"
    ],
    [
      "western-medicine__vsco2-harp",
      "vsco2-harp"
    ],
    [
      "western-medicine__vsco2-piano-mf",
      "vsco2-piano-mf"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/western-medicine-excerpts"
}

<|start_filename|>packages/piece-eyes-closed/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffer,
  createPitchShiftedSampler,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../eyes-closed.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const PHRASE = [['G#5', 1], ['F#5', 2], ['D#5', 3.5], ['C#5', 4], ['D#5', 4.5]];
const CHORD = ['G#3', 'G#4'];

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const danTranh = await createBuffer(samples['dan-tranh-gliss-ps'][0]);
  const piano = await createPitchShiftedSampler({
    samplesByNote: samples['vsco2-piano-mf'],
    pitchShift: -24,
  });

  const activeSources = [];

  const playDanTranh = dest => {
    const offset = Math.pow(window.generativeMusic.rng(), 3) * 120;
    const duration = window.generativeMusic.rng() * 60 + 60;
    const source = new Tone.BufferSource(danTranh)
      .set({
        fadeIn: 5,
        fadeOut: 5,
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i >= 0) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(dest);
    activeSources.push(source);
    source.start('+1', offset, duration);
    Tone.Transport.scheduleOnce(() => {
      playDanTranh(dest);
    }, `+${1 + duration - 5}`);
  };

  const schedulePhrase = () => {
    Tone.Transport.scheduleOnce(() => {
      const multiplier = Math.pow(window.generativeMusic.rng(), 2);
      PHRASE.slice(0, Math.ceil(window.generativeMusic.rng() * PHRASE.length)).forEach(
        ([note, time], i) => {
          piano.triggerAttack(
            note,
            `+${time * (1 + multiplier) + i * multiplier}`
          );
        }
      );
      schedulePhrase();
    }, `+${window.generativeMusic.rng() * 60 + 30}`);
  };

  const scheduleChord = () => {
    Tone.Transport.scheduleOnce(() => {
      CHORD.forEach(note => {
        piano.triggerAttack(note, `+${1 + window.generativeMusic.rng() / 10 - 0.05}`);
      });
      scheduleChord();
    }, `+${window.generativeMusic.rng() * 60 + 30}`);
  };

  const schedule = ({ destination }) => {
    const danTranhFilter = new Tone.AutoFilter(window.generativeMusic.rng() / 100 + 0.01, 200);
    const pianoFilter = new Tone.AutoFilter(window.generativeMusic.rng() / 100 + 0.01, 400);

    [danTranhFilter, pianoFilter].forEach(filter => {
      filter.connect(destination);
      filter.start();
    });

    piano.connect(pianoFilter);
    playDanTranh(danTranhFilter);
    schedulePhrase();
    scheduleChord();

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
      piano.releaseAll(0);
      danTranhFilter.dispose();
      pianoFilter.dispose();
    };
  };

  const deactivate = () => {
    [danTranh, piano].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['eyes-closed'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/theory/simplify-note.js<|end_filename|>
import transpose from './transpose';

export default transpose(0);


<|start_filename|>packages/piece-at-sunrise/at-sunrise.gfm.manifest.json<|end_filename|>
{
  "title": "At Sunrise",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "at-sunrise",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "calm",
    "dream",
    "electronic"
  ],
  "releaseDate": "2019-08-25T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "at-sunrise__vcsl-vibraphone-soft-mallets-mp",
      "vcsl-vibraphone-soft-mallets-mp"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/at-sunrise-excerpts"
}

<|start_filename|>packages/piece-townsend/townsend.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Townsend",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "townsend",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "calm",
    "acoustic"
  ],
  "releaseDate": "2019-02-16T00:00:00.000Z",
  "sampleNames": [
    [
      "townsend__vsco2-flute-susvib",
      "vsco2-flute-susvib"
    ],
    [
      "townsend__acoustic-guitar-chords-cmaj",
      "acoustic-guitar-chords-cmaj"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/townsend-excerpts"
}

<|start_filename|>packages/utilities/src/wrap-activate.js<|end_filename|>
import { getContext, setContext, Gain, Compressor } from 'tone';
import makeActiveStage from './make-active-stage';

const wrapActivate = (activate, { gain = 1 } = {}) => async options => {
  if (getContext() !== options.context) {
    setContext(options.context);
  }
  window.generativeMusic = window.generativeMusic || {};
  window.generativeMusic.rng = window.generativeMusic.rng || Math.random;
  const [deactivate, schedule] = await activate(options);
  const compressorNode = new Compressor();
  const gainNode = new Gain(gain).connect(compressorNode);
  compressorNode.connect(options.destination);
  return makeActiveStage({
    deactivate,
    schedule,
    destination: gainNode,
  });
};

export default wrapActivate;


<|start_filename|>packages/utilities/src/make-active-stage.spec.js<|end_filename|>
import { Gain } from 'tone';
import makeActiveStage from './make-active-stage';
import noop from './utilities/noop';

const makeCallAwareFn = () => {
  const callAwareFn = () => {
    callAwareFn.called = true;
    callAwareFn.callCount = callAwareFn.callCount
      ? callAwareFn.callCount + 1
      : 1;
  };
  return callAwareFn;
};

const destination = new Gain();

describe('makeActiveStage', () => {
  it('should return an array of two functions, deactivate and schedule', () => {
    expect(makeActiveStage({ deactivate: noop, schedule: noop, destination }))
      .to.be.an.instanceOf(Array)
      .of.length(2);
  });
  describe('deactivate', () => {
    it('should call the deactivate parameter', () => {
      const deactivateParameter = makeCallAwareFn();
      const [deactivate] = makeActiveStage({
        deactivate: deactivateParameter,
        schedule: noop,
        destination,
      });
      deactivate();
      expect(deactivateParameter).to.have.property('called', true);
    });
    it('should not call the deactive parameter multiple times', () => {
      const deactivateParameter = makeCallAwareFn();
      const [deactivate] = makeActiveStage({
        deactivate: deactivateParameter,
        schedule: noop,
        destination,
      });
      deactivate();
      deactivate();
      expect(deactivateParameter).to.have.property('callCount', 1);
    });
    it('should call any uncalled end functions', () => {
      const end = makeCallAwareFn();
      const scheduleParameter = () => end;
      const [deactivate, schedule] = makeActiveStage({
        deactivate: noop,
        schedule: scheduleParameter,
        destination,
      });
      schedule();
      deactivate();
      expect(end).to.have.property('called', true);
    });
    it('should not call end functions which were already called', () => {
      const end = makeCallAwareFn();
      const scheduleParameter = () => end;
      const [deactivate, schedule] = makeActiveStage({
        deactivate: noop,
        schedule: scheduleParameter,
        destination,
      });
      const endFromSchedule = schedule();
      endFromSchedule();
      deactivate();
      expect(end).to.have.property('callCount', 1);
    });
    it('should not call end functions multiple times', () => {
      const end = makeCallAwareFn();
      const scheduleParameter = () => end;
      const [deactivate, schedule] = makeActiveStage({
        deactivate: noop,
        schedule: scheduleParameter,
        destination,
      });
      schedule();
      deactivate();
      deactivate();
      expect(end).to.have.property('callCount', 1);
    });
    it('should not attempt to call non-function results of scheduleParameter', () => {
      const scheduleParameter = () => 'NOT_A_FUNCTION';
      const [deactivate, schedule] = makeActiveStage({
        deactivate: noop,
        schedule: scheduleParameter,
        destination,
      });
      schedule();
      expect(() => deactivate()).not.to.throw();
    });
  });
  describe('schedule', () => {
    it('should call the scheduleParameter and return a function which calls the result', () => {
      const end = makeCallAwareFn();
      const scheduleResult = end;
      const scheduleParameter = () => scheduleResult;
      const [, schedule] = makeActiveStage({
        deactivate: noop,
        schedule: scheduleParameter,
        destination,
      });
      const endFromSchedule = schedule();
      endFromSchedule();
      expect(end).to.have.property('called', true);
    });
    it('should throw an error when called after deactivate was called', () => {
      const [deactivate, schedule] = makeActiveStage({
        deactivate: noop,
        schedule: noop,
        destination,
      });
      deactivate();
      expect(() => schedule()).to.throw();
    });
    it('should return a function even if scheduleParameter does not', () => {
      const scheduleParameter = () => 'NOT_A_FUNCTION';
      const [, schedule] = makeActiveStage({
        deactivate: noop,
        schedule: scheduleParameter,
        destination,
      });
      const result = schedule();
      expect(result).to.be.a('function');
    });
  });
});


<|start_filename|>packages/utilities/src/create-prerenderable-buffers.js<|end_filename|>
import createPrerenderableBufferArray from './create-prerenderable-buffer-array';
import createBuffers from './create-buffers';
import renderBuffer from './render-buffer';
import noop from './utilities/noop';

const createPrerenderableBuffers = async options => {
  const {
    samples,
    sourceInstrumentName,
    renderedInstrumentName,
    sampleLibrary,
    getDestination,
    additionalRenderLength = 0,
    onProgress = noop,
    bufferSourceOptions = {},
    keyFilter = () => true,
  } = options;
  if (samples[renderedInstrumentName]) {
    return createBuffers(samples[renderedInstrumentName]);
  }
  if (Array.isArray(samples[sourceInstrumentName])) {
    const bufferArray = await createPrerenderableBufferArray(options);
    return createBuffers(bufferArray);
  }
  const keys = Object.keys(samples[sourceInstrumentName]).filter(keyFilter);
  const values = keys.map(key => samples[sourceInstrumentName][key]);
  const renderedBuffers = await Promise.all(
    values.map(async (buffer, i) => {
      const renderedBuffer = await renderBuffer({
        buffer,
        getDestination,
        bufferSourceOptions,
        duration: buffer.duration + additionalRenderLength,
      });
      onProgress((i + 1) / values.length);
      return renderedBuffer;
    })
  );
  const renderedBuffersByKey = renderedBuffers.reduce(
    (o, renderedBuffer, i) => {
      const key = keys[i];
      o[key] = renderedBuffer;
      return o;
    },
    {}
  );
  sampleLibrary.save([[renderedInstrumentName, renderedBuffersByKey]]);
  return createBuffers(renderedBuffersByKey);
};

export default createPrerenderableBuffers;


<|start_filename|>packages/piece-beneath-waves/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableBuffers,
  wrapActivate,
  getRandomElement,
} from '@generative-music/utilities';
import { sampleNames } from '../beneath-waves.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const getReverb = () => new Tone.Reverb(15).toDestination().generate();

  const chorus = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    keyFilter: key => key === 'C5' || key === 'C6',
    sourceInstrumentName: 'sso-chorus-female',
    renderedInstrumentName: 'beneath-waves__sso-chorus-female',
    getDestination: getReverb,
    onProgress: val => onProgress(val / 2),
  });

  const corAnglais = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    keyFilter: key => key === 'F3' || key === 'F4',
    sourceInstrumentName: 'sso-cor-anglais',
    renderedInstrumentName: 'beneath-waves__sso-cor-anglais',
    getDestination: getReverb,
    onProgress: val => onProgress((val + 1) / 2),
  });

  const compressor = new Tone.Compressor();

  const synthGain = new Tone.Gain().connect(compressor);
  const lPan = new Tone.Panner(-1);
  const rPan = new Tone.Panner(1);
  const lSynth = new Tone.Synth({
    oscillator: { type: 'sine' },
  }).connect(lPan);
  const rSynth = new Tone.Synth({
    oscillator: { type: 'sine' },
  }).connect(rPan);

  const sub = () => {
    lSynth.triggerAttackRelease(38, 0.5, '+1');
    rSynth.triggerAttackRelease(38, 0.5, '+1.5');

    Tone.Transport.scheduleOnce(() => {
      sub();
    }, '+8');
  };

  const playbackRate = 0.15;
  const vol = new Tone.Volume(-10);

  const activeSources = [];

  const play = notes => {
    const note = getRandomElement(notes);
    const buf = chorus.get(note);
    const source = new Tone.BufferSource(buf)
      .set({
        playbackRate,
        fadeIn: 4,
        fadeOut: 4,
        curve: 'linear',
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i > -1) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(vol);
    source.start('+1', 0, buf.duration / playbackRate);

    activeSources.push(source);

    Tone.Transport.scheduleOnce(() => {
      play(notes);
    }, `+${buf.duration / playbackRate - (1 + window.generativeMusic.rng() * 5)}`);
  };

  const playCorAnglais = () => {
    const note = `F${Math.floor(window.generativeMusic.rng() * 2) + 3}`;
    const buf = corAnglais.get(note);
    const source = new Tone.BufferSource(buf)
      .set({
        playbackRate: 0.15,
        fadeIn: 4,
        fadeOut: 4,
        curve: 'linear',
        onended: () => {
          const i = activeSources.indexOf(source);
          if (i > -1) {
            activeSources.splice(i, 1);
          }
        },
      })
      .connect(vol);
    source.start('+1');
    activeSources.push(source);

    Tone.Transport.scheduleOnce(() => {
      playCorAnglais();
    }, `+${buf.duration * 10 + window.generativeMusic.rng() * buf.duration * 10}`);
  };

  const schedule = ({ destination }) => {
    compressor.connect(destination);
    const delay = new Tone.FeedbackDelay({
      feedback: 0.7,
      delayTime: 2,
      maxDelay: 2,
    }).connect(synthGain);

    rPan.connect(delay);
    lPan.connect(delay);

    const synthGainLfo = new Tone.LFO(window.generativeMusic.rng() / 100 + 0.01).set({
      phase: 90,
    });
    synthGainLfo.connect(synthGain.gain);
    synthGainLfo.start();

    const autoFilter = new Tone.AutoFilter(window.generativeMusic.rng() / 100 + 0.01, 75, 6);
    autoFilter.connect(compressor);
    autoFilter.start();
    vol.connect(autoFilter);

    play(['C5']);
    play(['C6']);
    sub();

    Tone.Transport.scheduleOnce(() => {
      playCorAnglais();
    }, `+${window.generativeMusic.rng() * 60}`);

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });

      lSynth.triggerRelease();
      rSynth.triggerRelease();

      delay.dispose();
      synthGainLfo.dispose();
      autoFilter.dispose();
    };
  };

  const deactivate = () => {
    [
      chorus,
      corAnglais,
      compressor,
      synthGain,
      lPan,
      rPan,
      lSynth,
      rSynth,
      vol,
      ...activeSources,
    ].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['beneath-waves'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-didgeridoobeats/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffers,
  createPrerenderableBuffers,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../didgeridoobeats.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const DRUM_LOOP_LENGTH_S = 75;
const BEAT_SIXTEETHS_COUNT = 32;

const getPattern = (quarterP, eighthP, sixteethP) => {
  const pattern = [];
  for (let i = 0; i < BEAT_SIXTEETHS_COUNT; i += 1) {
    if (i % 4 === 0) {
      if (window.generativeMusic.rng() < quarterP) {
        pattern.push(i);
      }
    } else if (i % 2 === 0) {
      if (window.generativeMusic.rng() < eighthP) {
        pattern.push(i);
      }
    } else if (window.generativeMusic.rng() < sixteethP) {
      pattern.push(i);
    }
  }
  return pattern;
};

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const drumGain = new Tone.Gain(0);
  const hatVolume = new Tone.Volume(-5).connect(drumGain);

  const activeSources = [];

  const percussionInstrument = instrumentName => {
    const instrumentSamples = samples[instrumentName];
    return createBuffers(instrumentSamples).then(buffers => {
      const randomBuffer = () =>
        buffers.get(
          Math.floor(window.generativeMusic.rng() * instrumentSamples.length)
        );

      let currentBuffer = randomBuffer();
      return {
        newSound: () => {
          currentBuffer = randomBuffer();
        },
        play: t => {
          const bufferSource = new Tone.BufferSource(currentBuffer).connect(
            instrumentName.includes('hats') ? hatVolume : drumGain
          );
          activeSources.push(bufferSource);
          bufferSource.onended = () => {
            const index = activeSources.indexOf(bufferSource);
            if (index >= 0) {
              activeSources.splice(index, 1);
            }
          };
          bufferSource.start(t);
        },
      };
    });
  };

  const didgeridooSamples =
    samples['vcsl-didgeridoo-sus'] ||
    samples['didgeridoobeats__vcsl-didgeridoo-sus'];

  const [hats, kick, snare, didgeridoo] = await Promise.all(
    ['itslucid-lofi-hats', 'itslucid-lofi-kick', 'itslucid-lofi-snare']
      .map(i => percussionInstrument(i))
      .concat([
        createPrerenderableBuffers({
          samples,
          sampleLibrary,
          sourceInstrumentName: 'vcsl-didgeridoo-sus',
          renderedInstrumentName: 'didgeridoobeats__vcsl-didgeridoo-sus',
          additionalRenderLength: 1,
          getDestination: () =>
            Promise.resolve(
              new Tone.Freeverb({ roomSize: 0.7, wet: 0.5 }).toDestination()
            ),
          onProgress: onProgress,
        }),
      ])
  );

  const playDrumLoop = () => {
    drumGain.gain.setValueAtTime(0, Tone.now());
    drumGain.gain.linearRampToValueAtTime(0.2, `+${DRUM_LOOP_LENGTH_S / 2}`);

    Tone.Transport.scheduleOnce(() => {
      drumGain.gain.setValueAtTime(0.2, Tone.now());
      drumGain.gain.linearRampToValueAtTime(0, `+${DRUM_LOOP_LENGTH_S / 2}`);
    }, `+${DRUM_LOOP_LENGTH_S / 2}`);
    const sixteenthTime = window.generativeMusic.rng() * 0.05 + 0.1;

    const hatPattern = getPattern(0.9, 0.5, 0.1);
    const snarePattern = getPattern(0.5, 0.25, 0.1);
    const kickPattern = getPattern(0.5, 0.2, 0.05);

    [hats, snare, kick].forEach(({ newSound }) => newSound());

    [[hats, hatPattern], [snare, snarePattern], [kick, kickPattern]].forEach(
      ([inst, pattern], i) => {
        if (i > 0 || window.generativeMusic.rng() < 0.25) {
          Tone.Transport.scheduleRepeat(
            () => {
              pattern.forEach(beat => {
                inst.play(`+${beat * sixteenthTime + 0.05}`);
              });
            },
            BEAT_SIXTEETHS_COUNT * sixteenthTime,
            '+0.05',
            DRUM_LOOP_LENGTH_S - BEAT_SIXTEETHS_COUNT * sixteenthTime + 0.5
          );
        }
      }
    );

    Tone.Transport.scheduleOnce(() => {
      playDrumLoop();
    }, `+${DRUM_LOOP_LENGTH_S + 3}`);
  };

  const chorus = new Tone.Chorus();

  const playDigeridoo = dest => {
    const index = Math.floor(
      window.generativeMusic.rng() * didgeridooSamples.length
    );
    const buffer = didgeridoo.get(index);
    let playbackRate = 1;
    if (window.generativeMusic.rng() < 0.1) {
      playbackRate -= 0.2;
    }
    if (window.generativeMusic.rng() < 0.1) {
      playbackRate -= 0.2;
    }
    const source = new Tone.ToneBufferSource({
      url: buffer,
      playbackRate,
    }).connect(dest);
    activeSources.push(source);
    source.onended = () => {
      const i = activeSources.indexOf(source);
      if (i >= 0) {
        activeSources.splice(index, 1);
      }
    };
    source.start('+1');
    Tone.Transport.scheduleOnce(() => {
      playDigeridoo(dest);
    }, `+${window.generativeMusic.rng() < 0.03 ? window.generativeMusic.rng() * 10 + 10 : window.generativeMusic.rng() * 5 + 5}`);
  };

  const schedule = ({ destination }) => {
    drumGain.connect(destination);
    const didgeridooAutoFilter = new Tone.AutoFilter({
      frequency: 0.06,
      octaves: 4,
      filter: { type: 'bandpass' },
    })
      .set({ wet: 0.7 })
      .start();
    const drumsAutoFilter = new Tone.AutoFilter({
      frequency: 0.08,
      octaves: 4,
      filter: { type: 'bandpass' },
    })
      .set({ wet: 0.7 })
      .start()
      .connect(destination);

    drumGain.connect(drumsAutoFilter);

    const delay = new Tone.FeedbackDelay({
      feedback: 0.8,
      delayTime: 0.2,
    }).chain(chorus, didgeridooAutoFilter, destination);

    Tone.Transport.scheduleOnce(() => {
      playDrumLoop();
    }, '+5');

    Tone.Transport.scheduleOnce(() => {
      playDigeridoo(delay);
    }, '+1');

    return () => {
      drumGain.gain.cancelScheduledValues(Tone.now());
      drumGain.gain.setValueAtTime(0, Tone.now());
      activeSources.forEach(source => {
        source.stop(0);
      });
      [delay, drumsAutoFilter, didgeridooAutoFilter].forEach(node => {
        node.dispose();
      });
    };
  };

  const deactivate = () => {
    [drumGain, hatVolume, chorus].forEach(node => {
      node.dispose();
    });
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['didgeridoobeats'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/theory/get-distance.js<|end_filename|>
import normalizeNote from './normalize-note';
import getPitchClass from './get-pitch-class';
import getOctave from './get-octave';
import pitchClassIndiciesByValue from './pitch-class-indicies-by-value';
import curry2 from '../utilities/curry-2';

const _getDistance = (note1, note2) => {
  const [
    [note1PitchClassIndex, note1Octave],
    [note2PitchClassIndex, note2Octave],
  ] = [note1, note2]
    .map(normalizeNote)
    .map(note => [
      pitchClassIndiciesByValue[getPitchClass(note)],
      getOctave(note),
    ]);
  const octaveChange = note2Octave - note1Octave;
  const pitchClassChange = note2PitchClassIndex - note1PitchClassIndex;
  return pitchClassChange + octaveChange * 12;
};

const getDistance = (note1, note2) => {
  if (typeof note2 === 'undefined') {
    return curry2(_getDistance)(note1);
  }
  return _getDistance(note1, note2);
};

export default getDistance;


<|start_filename|>packages/piece-stratospheric/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createPrerenderableBuffers,
  wrapActivate,
} from '@generative-music/utilities';
import { sampleNames } from '../stratospheric.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const activeSources = [];

  const getBufferPlayer = (bufferUrls, buffers, bufferDestination, getP) => {
    const firstDelays = bufferUrls.map(() => window.generativeMusic.rng() * 90);
    const minFirstDelay = Math.min(...firstDelays);
    bufferUrls
      .map((url, i) => buffers.get(i))
      .forEach((buffer, i) => {
        const play = () => {
          if (window.generativeMusic.rng() < getP()) {
            const source = new Tone.BufferSource(buffer).set({
              playbackRate: 0.75,
              onended: () => {
                const sourceIndex = activeSources.indexOf(source);
                if (sourceIndex >= 0) {
                  activeSources.splice(sourceIndex, 1);
                }
              },
            });
            activeSources.push(source);
            source.connect(bufferDestination);
            source.start('+1');
          }

          Tone.Transport.scheduleOnce(() => {
            play();
          }, `+${window.generativeMusic.rng() * 60 + 30}`);
        };
        const firstDelay = firstDelays[i] - minFirstDelay;
        Tone.Transport.scheduleOnce(() => {
          play();
        }, `+${firstDelay}`);
      });
  };

  const coilSpankUrls =
    samples['stratospheric__guitar-coil-spank'] || samples['guitar-coil-spank'];
  const dustyUrls =
    samples['stratospheric__guitar-dusty'] || samples['guitar-dusty'];

  const getReverb = () =>
    new Tone.Reverb(30)
      .set({ wet: 0.6 })
      .toDestination()
      .generate();

  const coilSpankBuffers = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'guitar-coil-spank',
    renderedInstrumentName: 'stratospheric__guitar-coil-spank',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.5),
  });

  const dustyBuffers = await createPrerenderableBuffers({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'guitar-dusty',
    renderedInstrumentName: 'stratospheric__guitar-dusty',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.5 + 0.5),
  });

  const dustyVol = new Tone.Volume(-15);
  const getCoilSpankP = () => 1 - ((Tone.now() / 60) % 60) / 60;
  const getDustyP = () => 1 - getCoilSpankP();

  const schedule = ({ destination }) => {
    dustyVol.connect(destination);
    getBufferPlayer(
      coilSpankUrls,
      coilSpankBuffers,
      destination,
      getCoilSpankP
    );
    getBufferPlayer(dustyUrls, dustyBuffers, dustyVol, getDustyP);

    return () => {
      activeSources.forEach(source => {
        source.stop(0);
      });
    };
  };

  const deactivate = () => {
    [coilSpankBuffers, dustyBuffers, dustyVol, ...activeSources].forEach(node =>
      node.dispose()
    );
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['stratospheric'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-day-dream/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffers,
  sampleNote,
  wrapActivate,
  toss,
  minor7th,
} from '@generative-music/utilities';
import { sampleNames } from '../day-dream.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const NOON_SEMITONE_CHANGE = 15;
const MIDNIGHT_SEMITONE_CHANGE = 30;

const NOTES = toss(['C'], [4, 5, 6])
  .map(minor7th)
  .flat();

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const samplesByNote = samples['vsco2-piano-mf'];
  const sampledNotes = Object.keys(samplesByNote);
  const buffers = await createBuffers(samplesByNote);
  const activeSources = [];
  const playNote = ({ note, destination }) => {
    const date = new Date();
    const hour = date.getHours();
    const minute = date.getMinutes();
    const second = date.getSeconds();
    let semitoneChange;
    if (hour >= 12) {
      const hoursPastNoon = hour - 12;
      const secondsPastNoon = hoursPastNoon * 60 * 60 + minute * 60 + second;
      const pctToMidnight = secondsPastNoon / (12 * 60 * 60 - 1);
      semitoneChange =
        pctToMidnight * (MIDNIGHT_SEMITONE_CHANGE - NOON_SEMITONE_CHANGE) +
        NOON_SEMITONE_CHANGE;
    } else {
      const secondsPastMidnight = hour * 60 * 60 + minute * 60 + second;
      const pctToNoon = secondsPastMidnight / (12 * 60 * 60 - 1);
      semitoneChange =
        pctToNoon * (NOON_SEMITONE_CHANGE - MIDNIGHT_SEMITONE_CHANGE) +
        MIDNIGHT_SEMITONE_CHANGE;
    }
    const { sampledNote, playbackRate } = sampleNote({
      note,
      sampledNotes,
      pitchShift: -semitoneChange,
    });
    const buffer = buffers.get(sampledNote);
    const bufferSource = new Tone.BufferSource(buffer).connect(destination);
    bufferSource.set({
      playbackRate,
      onended: () => {
        const index = activeSources.indexOf(bufferSource);
        if (index >= 0) {
          activeSources.splice(index, 1);
        }
      },
    });
    activeSources.push(bufferSource);
    bufferSource.start('+1');
    return semitoneChange;
  };

  const schedule = ({ destination }) => {
    const firstDelays = NOTES.map(
      () =>
        window.generativeMusic.rng() *
          ((NOON_SEMITONE_CHANGE + MIDNIGHT_SEMITONE_CHANGE) / 2) +
        15
    );

    const minFirstDelay = Math.min(...firstDelays);

    NOTES.forEach((note, i) => {
      const play = time => {
        Tone.Transport.scheduleOnce(() => {
          const semitoneChange = playNote({ note, destination });
          play(window.generativeMusic.rng() * (semitoneChange + 12) + 3);
        }, `+${time}`);
      };
      play(firstDelays[i] - minFirstDelay);
    });

    return () => {
      activeSources.forEach(node => node.stop());
    };
  };

  const deactivate = () => {
    buffers.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['day-dream'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-observable-streams/src/operators/short-term-throttle-by-note.js<|end_filename|>
import * as Tone from 'tone';
import { filter } from 'rxjs/operators';

const shortTermThrottleByNote = timeInSeconds => {
  const lastTimes = new Map();
  return source =>
    source.pipe(
      filter(note => {
        const now = Tone.now();
        if (lastTimes.has(note)) {
          const lastTime = lastTimes.get(note);
          if (now - lastTime < timeInSeconds) {
            return false;
          }
        }
        lastTimes.set(note, now);
        return true;
      })
    );
};

export default shortTermThrottleByNote;


<|start_filename|>packages/piece-drones-2/drones-2.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Drones II",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "drones-2",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "drone",
    "electronic"
  ],
  "releaseDate": "2019-04-28T00:00:00.000Z",
  "sampleNames": [
    "vsco2-violins-susvib"
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/drones-ii-excerpts"
}

<|start_filename|>packages/piece-townsend/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  createPrerenderableSampler,
  createPrerenderableBufferArray,
} from '@generative-music/utilities';
import { sampleNames } from '../townsend.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const FLUTE_NOTES = ['C3', 'C4', 'G3', 'G4'];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const flute = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: FLUTE_NOTES,
    sourceInstrumentName: 'vsco2-flute-susvib',
    renderedInstrumentName: 'townsend__vsco2-flute-susvib',
    additionalRenderLength: 2,
    getDestination: () => new Tone.Reverb(50).toDestination().generate(),
    onProgress: val => onProgress(val * 0.2),
  });

  const guitarBuffers = await createPrerenderableBufferArray({
    samples,
    sampleLibrary,
    sourceInstrumentName: 'acoustic-guitar-chords-cmaj',
    renderedInstrumentName: 'townsend__acoustic-guitar-chords-cmaj',
    additionalRenderLength: 0.5,
    getDestination: () =>
      Promise.resolve(
        new Tone.Freeverb({
          roomSize: 0.5,
          dampening: 5000,
          wet: 0.2,
        }).toDestination()
      ),
    onProgress: val => onProgress(val * 0.8 + 0.2),
  });

  const fluteGain = new Tone.Gain();
  flute.connect(fluteGain);
  const intervalTimes = FLUTE_NOTES.map(() => window.generativeMusic.rng() * 10 + 5);
  const shortestInterval = Math.min(...intervalTimes);
  const limiter = new Tone.Limiter();
  const activeSources = [];

  const playRandomChord = lastChord => {
    const nextChords = guitarBuffers.filter(chord => chord !== lastChord);
    const randomChord =
      nextChords[Math.floor(window.generativeMusic.rng() * nextChords.length)];
    const source = new Tone.ToneBufferSource(randomChord).connect(limiter);
    activeSources.push(source);
    source.onended = () => {
      const i = activeSources.indexOf(source);
      if (i >= 0) {
        activeSources.splice(i, 1);
      }
    };
    source.start('+1');
    Tone.Transport.scheduleOnce(() => {
      playRandomChord(randomChord);
    }, `+${window.generativeMusic.rng() * 10 + 5}`);
  };

  const schedule = ({ destination }) => {
    fluteGain.connect(destination);
    limiter.connect(destination);
    const fluteGainLfo = new Tone.LFO({
      frequency: window.generativeMusic.rng() / 100,
      min: 0,
      max: 0.2,
    });
    fluteGainLfo.connect(fluteGain.gain);
    fluteGainLfo.start();

    const delay = new Tone.FeedbackDelay({
      delayTime: 1,
      feedback: 0.7,
      maxDelay: 1,
    });
    fluteGain.connect(delay);

    FLUTE_NOTES.forEach((note, i) => {
      Tone.Transport.scheduleRepeat(
        () => flute.triggerAttack(note, '+1'),
        intervalTimes[i],
        intervalTimes[i] - shortestInterval
      );
    });

    Tone.Transport.scheduleOnce(() => {
      playRandomChord();
    }, window.generativeMusic.rng() * 5 + 5);

    return () => {
      fluteGainLfo.stop();
      fluteGainLfo.dispose();
      delay.dispose();
      flute.releaseAll(0);
      activeSources.forEach(source => {
        source.stop(0);
      });
    };
  };

  const deactivate = () => {
    guitarBuffers
      .concat([flute, fluteGain, limiter])
      .forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['townsend'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-lullaby/piece.gfm.manifest.json<|end_filename|>
{
  "title": "Lullaby",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "lullaby",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "dream",
    "calm",
    "electronic"
  ],
  "releaseDate": "2020-12-06T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    "vsco2-piano-mf",
    [
      "lullaby__birds",
      "birds"
    ],
    [
      "lullaby__explosion",
      "explosion"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/lullaby-excerpts"
}

<|start_filename|>packages/piece-awash/awash.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Awash",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "awash",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "electronic",
    "dream"
  ],
  "releaseDate": "2019-05-05T00:00:00.000Z",
  "sampleNames": [
    [
      "awash__vcsl-ocean-drum",
      "vcsl-ocean-drum"
    ],
    [
      "awash__dry-guitar-vib",
      "dry-guitar-vib"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/awash-excerpts"
}

<|start_filename|>packages/utilities/src/theory/get-octave.js<|end_filename|>
const getOctave = (note = '') => {
  const match = note.match(/[abcdefg][#b]?(\d+)/i);
  if (!match) {
    return null;
  }
  return Number.parseInt(match[1], 10);
};

export default getOctave;


<|start_filename|>packages/utilities/src/utilities/noop.js<|end_filename|>
import undefinedValue from './undefined';

const noop = () => undefinedValue;

export default noop;


<|start_filename|>packages/piece-enough/enough.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Enough",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "enough",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "dark",
    "electronic"
  ],
  "releaseDate": "2019-05-12T00:00:00.000Z",
  "sampleNames": [
    "sso-cor-anglais"
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/enough-excerpts"
}

<|start_filename|>packages/piece-transmission/transmission.gfm.manifest.json<|end_filename|>
{
  "title": "Transmission",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "transmission",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "electronic"
  ],
  "releaseDate": "2019-11-17T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "transmission__vsco2-piano-mf",
      "vsco2-piano-mf"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/transmission-excerpts"
}

<|start_filename|>packages/piece-beneath-waves/beneath-waves.gfm.manifest.json<|end_filename|>
{
  "title": "Beneath Waves",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "beneath-waves",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "dark",
    "drone",
    "electronic"
  ],
  "releaseDate": "2019-12-01T00:00:00.000Z",
  "visualizationType": "squareCut",
  "sampleNames": [
    [
      "beneath-waves__sso-chorus-female",
      "sso-chorus-female"
    ],
    [
      "beneath-waves__sso-cor-anglais",
      "sso-cor-anglais"
    ]
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/beneath-waves-excerpts"
}

<|start_filename|>packages/piece-at-sunrise/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  createPrerenderableSampler,
} from '@generative-music/utilities';
import { sampleNames } from '../at-sunrise.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const NOTES = ['C3', 'F3', 'G3', 'C4', 'F4', 'G4', 'C5', 'E5', 'G5', 'C6'];

function* makeValueOscillator(min, max) {
  let up = true;
  let value = up ? min : max;
  while (1) {
    const delta = window.generativeMusic.rng() * 0.2;
    if (up) {
      value += delta;
    } else {
      value -= delta;
    }
    if (up && value > max) {
      up = false;
    } else if (!up && value < min) {
      up = true;
    }
    yield value;
  }
}

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);

  const vibraphone = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    onProgress,
    pitchShift: -12,
    notes: NOTES.filter(note => note !== 'G3' && note !== 'G4'),
    sourceInstrumentName: 'vcsl-vibraphone-soft-mallets-mp',
    renderedInstrumentName: 'at-sunrise__vcsl-vibraphone-soft-mallets-mp',
    getDestination: () => new Tone.Reverb(5).toDestination().generate(),
  });

  const vol = new Tone.Volume(10);
  const filter = new Tone.Filter(2000);
  filter.connect(vol);

  const play = (note, timeGenerator) => {
    vibraphone.triggerAttack(note, '+1');
    Tone.Transport.scheduleOnce(() => {
      play(note, timeGenerator);
    }, `+${window.generativeMusic.rng() * timeGenerator.next().value + timeGenerator.next().value}`);
  };

  const schedule = ({ destination }) => {
    vol.connect(destination);
    const delay = new Tone.FeedbackDelay({
      delayTime: 0.2,
      feedback: 0.7,
    }).connect(filter);
    vibraphone.connect(delay);

    const timeGenerator = makeValueOscillator(5, 20);

    NOTES.forEach(note => {
      play(note, timeGenerator);
    });

    return () => {
      vibraphone.releaseAll(0);
      delay.dispose();
    };
  };

  const deactivate = () => {
    filter.dispose();
    vol.dispose();
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['at-sunrise'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-neuroplasticity/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  createBuffers,
  createSampler,
  wrapActivate,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../neuroplasticity.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const PIANO_NOTES = toss(['C#', 'D#'], [3, 4, 5, 6]);
const GUITAR_NOTES = ['G2', 'C3', 'G3', 'C4'];

const activate = async ({ sampleLibrary }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const [buffers, piano] = await Promise.all([
    createBuffers(samples['guitar-namaste']),
    createSampler(samples['vsco2-piano-mf']),
  ]);

  const guitarVol = new Tone.Volume(-10);
  const activeSources = [];

  const schedule = ({ destination }) => {
    const autoFilter = new Tone.AutoFilter(window.generativeMusic.rng() / 50, 200, 5)
      .start()
      .connect(destination);
    const delay = new Tone.FeedbackDelay({
      feedback: 0.5,
      wet: 0.5,
      delayTime: 1.5,
    }).connect(autoFilter);
    guitarVol.connect(delay);
    piano.connect(delay);

    const initialDelays = GUITAR_NOTES.map(() => window.generativeMusic.rng() * 45);
    const minDelay = Math.min(...initialDelays);

    GUITAR_NOTES.forEach((note, i) => {
      const b = buffers.get(note);

      const play = () => {
        const bufferSource = new Tone.BufferSource(b)
          .set({
            fadeIn: 5,
            fadeOut: 5,
            curve: 'linear',
            playbackRate: 0.6,
            onended: () => {
              const index = activeSources.indexOf(bufferSource);
              if (index >= 0) {
                bufferSource.dispose();
                activeSources.splice(index, 1);
              }
            },
          })
          .connect(guitarVol);
        activeSources.push(bufferSource);
        bufferSource.start('+1', 0, 14);
        Tone.Transport.scheduleOnce(() => {
          play();
        }, `+${window.generativeMusic.rng() * 15 + 30}`);
      };
      Tone.Transport.scheduleOnce(() => {
        play();
      }, `+${initialDelays[i] - minDelay}`);
    });

    const playPiano = () => {
      const randomPianoNote =
        PIANO_NOTES[Math.floor(window.generativeMusic.rng() * PIANO_NOTES.length)];

      piano.triggerAttack(randomPianoNote);

      Tone.Transport.scheduleOnce(() => {
        playPiano();
      }, `+${window.generativeMusic.rng() * 10 + 10}`);
    };

    Tone.Transport.scheduleOnce(() => {
      playPiano();
    }, `+${window.generativeMusic.rng() * 10 + 10}`);

    return () => {
      activeSources.forEach(source => {
        source.set({ fadeOut: 0 }).stop(0);
      });
      autoFilter.dispose();
      delay.dispose();
    };
  };

  const deactivate = () => {
    [buffers, piano, guitarVol].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['neuroplasticity'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/utilities/src/theory/transpose.js<|end_filename|>
import normalizeNote from './normalize-note';
import transposePitchClass from './transpose-pitch-class';
import transposeNote from './transpose-note';
import getOctave from './get-octave';
import getPitchClass from './get-pitch-class';
import swap2 from '../utilities/swap-2';
import curry2 from '../utilities/curry-2';

const _transpose = (note, steps) => {
  const normalizedNote = normalizeNote(note);
  const octave = getOctave(note);
  if (octave === null) {
    const result = transposePitchClass(normalizedNote, steps);
    return result;
  }
  const pitchClass = getPitchClass(normalizedNote);
  return transposeNote(pitchClass, octave, steps);
};

const transpose = (arg1, arg2) => {
  const getResult = typeof arg1 === 'string' ? _transpose : swap2(_transpose);
  return typeof arg2 === 'undefined'
    ? curry2(getResult)(arg1)
    : getResult(arg1, arg2);
};

export default transpose;


<|start_filename|>packages/piece-oxalis-1/src/piece.js<|end_filename|>
import makeOxalis from '@generative-music/make-piece-oxalis';

const notes = ['C3', 'C4', 'B4', 'E5', 'G5'];

export default makeOxalis({ notes, renderedInstrumentPrefix: 'oxalis-1' });


<|start_filename|>packages/utilities/src/utilities/curry-2.js<|end_filename|>
export default fn => arg1 => arg2 => fn(arg1, arg2);


<|start_filename|>packages/piece-soundtrack/src/piece.js<|end_filename|>
import * as Tone from 'tone';
import {
  wrapActivate,
  createPrerenderableSampler,
  getRandomElement,
  toss,
} from '@generative-music/utilities';
import { sampleNames } from '../soundtrack.gfm.manifest.json';
import gainAdjustments from '../../../normalize/gain.json';

const SECOND_NOTES = ['D', 'Eb', 'F', 'G', 'A'];
const OCTAVES = [2, 3, 4];

const activate = async ({ sampleLibrary, onProgress }) => {
  const samples = await sampleLibrary.request(Tone.context, sampleNames);
  const getReverb = () => new Tone.Reverb(50).toDestination().generate();
  const renderedPitchClasses = ['C'].concat(
    SECOND_NOTES.filter((_, i) => i % 2 !== 0)
  );

  const cellos = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: toss(renderedPitchClasses, OCTAVES),
    sourceInstrumentName: 'vsco2-cellos-susvib-mp',
    renderedInstrumentName: 'soundtrack__vsco2-cellos-susvib-mp',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.5),
    pitchShift: -24,
  });

  const glock = await createPrerenderableSampler({
    samples,
    sampleLibrary,
    notes: toss(renderedPitchClasses, [5, 6]),
    sourceInstrumentName: 'vsco2-glock',
    renderedInstrumentName: 'soundtrack__vsco2-glock',
    getDestination: getReverb,
    onProgress: val => onProgress(val * 0.5 + 0.5),
    pitchShift: -36,
  });

  glock.set({
    attack: 0.05,
    curve: 'exponential',
  });

  const playProgression = () => {
    const secondNote = getRandomElement(SECOND_NOTES);
    const secondNoteTime = window.generativeMusic.rng() * 10 + 10 + 1;
    OCTAVES.forEach(octave => {
      cellos.triggerAttack(`C${octave}`, '+1');
      if (window.generativeMusic.rng() < 0.75) {
        glock.triggerAttack(
          `C${window.generativeMusic.rng() < 0.5 ? 5 : 6}`,
          `+${1 + window.generativeMusic.rng() * secondNoteTime}`
        );
      }
      cellos.triggerAttack(`${secondNote}${octave}`, `+${secondNoteTime}`);
      if (window.generativeMusic.rng() < 0.75) {
        glock.triggerAttack(
          `${secondNote}${window.generativeMusic.rng() < 0.5 ? 5 : 6}`,
          `+${secondNoteTime + window.generativeMusic.rng() * 10}`
        );
      }
    });
    Tone.Transport.scheduleOnce(() => {
      playProgression();
    }, `+${window.generativeMusic.rng() * 20 + 30}`);
  };

  const schedule = ({ destination }) => {
    cellos.connect(destination);

    const glockDelay = new Tone.PingPongDelay(0.7, 0.7)
      .set({ wet: 0.4 })
      .connect(destination);

    glock.connect(glockDelay);

    playProgression();

    return () => {
      glock.releaseAll(0);
      cellos.releaseAll(0);
      glockDelay.dispose();
    };
  };

  const deactivate = () => {
    [cellos, glock].forEach(node => node.dispose());
  };

  return [deactivate, schedule];
};

const GAIN_ADJUSTMENT = gainAdjustments['soundtrack'];

export default wrapActivate(activate, { gain: GAIN_ADJUSTMENT });


<|start_filename|>packages/piece-spring-again/spring-again.gfm.manifest.json<|end_filename|>
{
  "visualizationType": "squareCut",
  "title": "Spring Again",
  "makePiece": "./dist/esm.js",
  "image": "./image.png",
  "id": "spring-again",
  "artistId": "alex-bainter",
  "isRecordable": true,
  "tags": [
    "acoustic"
  ],
  "releaseDate": "2019-02-22T00:00:00.000Z",
  "sampleNames": [
    [
      "spring-again__vsco2-piano-mf",
      "vsco2-piano-mf"
    ],
    "vsco2-violins-susvib",
    "vsco2-cello-susvib-f"
  ],
  "bandcampUrl": "http://alexbainter.bandcamp.com/album/spring-again-excerpts"
}