Getting a SoundFile as a copy. (SoundFile.copy() nonexistant)

Hello everyone! It’s Matty here, back with another question in regards to Processing.
I’m messing around with sound right now, and I wanted to know if there was an easy way to ‘clone’ or copy a soundfile object?
I know about new SoundFile(this, "sound.mp3");, but it takes time trying to load the file. I’m basically trying to load a soundfile as a reference for other objects to copy, but I can’t seem to find an easy way to copy a soundfile.

SoundFile snd;
SoundFile snd2;
void setup() {
  snd = new SoundFile(this, "fly.mp3");
  snd2 = new SoundFile(this, "fire.mp3");
  snd.play();
}
/*
THIS NEXT CHUNK OF CODE IS IN A SEPARATE CLASS. I ONLY NEED TO WORRY ABOUT THE SOUNDFILE OBJECT.
*/
//soundObject class innit:
  private SoundFile soundEmit; //THIS FILE HERE NEEDS TO COPY THE INPUTTED SOUND FILE.
  float vol;
  soundObject(SoundFile sound, PVector origin, float range) {
    pos = origin;
    soundEmit = sound; //There's no sound.copy() or anything like that.
    maxRange = range;
  }

Help would be greatly appreciated, thank you!

1 Like

Hi @Mattyjak,

It’s true that there is no .copy() method on the SoundFile class. You can try the following methods (not tested with SoundFile class):

More importantly, why do you want to copy a SoundFile instance? Are you modifying it individually in each of your class instances?

A few things I probably should have mentioned:

  1. I’m using Processing Java, not Javascript, so the syntax/object creation is a little weirder.
  2. The reason why I want to copy it is that whenever you loud a sound using the sound library, it takes time to physically go into files and find/insert it into my program. (That is without multi-threading it, which doesn’t work with classes/objects)
  3. I want to essentially get a copy of it via code, as opposed to through the files in order to save time.
2 Likes

Looking at SoundFile, I’m not sure that you can do this. Edit: see solution below.

  1. There is no .copy() or .clone() etc – no public method returns a SoundFile
  2. There is no copy constructor
  3. It extends AudioSample, which does have a copy constructor for use by getUnusedPlayer(), but it is protected, and makes use of the protected .sample().
  1. You also can’t copy the object data to a new object yourself – SoundFile.SAMPLECACHE is private and AudioSample.sample is protected, with no getters

Things look buttoned up pretty tight. Maybe submit an issue or pull request for access, or for a copy method? Still, sound isn’t in active development right now as far as I know, so that probably won’t be an immediate solution.

My best guess (untested!) would be that you should load / obtain your sample data as a float[] array, then create as many AudioSample objects as you want directly from that float[]. That should give you fast sound object creation with no disk access.

2 Likes

Well, I’m not really a Sound library user, so I missed read() (which can write out the frame data).

HERE IS THE SOLUTION:

A copy method for SoundFile:

AudioSample copySoundFile(SoundFile file) {
  float[] data = new float[fileIn.frames()];
  file.read(data);
  return new AudioSample(this, data);
}

To try it out:

  1. open Examples > Libraries > Sound > Soundfile > JumbleSoundfile
  2. add the copySoundFile method from above
  3. replace global variables and setup as follows, leaving the rest of the sketch as-is
import processing.sound.*;

SoundFile fileIn;
AudioSample file;

void setup() {
  size(640, 360);
  background(255);
  // Load a soundfile and start playing a copy on loop
  fileIn = new SoundFile(this, "beat.aiff");
  file = copySoundFile(fileIn);
  file.loop();
}
  1. Add a reset method to demonstrate that the copy data and the original are different.
void keyPressed() {
  file.stop();
  file = copySoundFile(fileIn);
  file.loop();
}

The demo will work, running on the copied AudioSample object rather than on the original file. Click repeatedly to jumble the (copied) sample. Press any key to reset the playing sample to the original file load, then jumble again – all with in-memory copying, without ever loading a new SoundFile object from the file system.

// JumbleSoundfileCopy

/**
 * First load a sample sound file from disk, then start manipulating it using the
 * low-level data access functions provided by AudioSample.
 * With every mouseclick, two random 1 second chunks of the sample are swapped around
 * in their position. The sample always stays the same length and it keeps on looping,
 * but the more often you do random swaps, the more the original soundfile gets cut up
 * into smaller and smaller portions that seem to be resampled at random.
 */

import processing.sound.*;

SoundFile fileIn;
AudioSample file;

void setup() {
  size(640, 360);
  background(255);
  // Load a soundfile and start playing a copy on loop
  fileIn = new SoundFile(this, "beat.aiff");
  file = copySoundFile(fileIn);
  file.loop();
}

AudioSample copySoundFile(SoundFile file) {
  float[] data = new float[fileIn.frames()];
  file.read(data);
  return new AudioSample(this, data);
}

void keyPressed() {
  file.stop();
  file = copySoundFile(fileIn);
  file.loop();
}

void draw() {
}

void mousePressed() {
  // Every time the mouse is pressed, take two random 1 second chunks of the sample
  // and swap them around.

  int partOneStart = int(random(file.frames()));
  int partTwoOffset = int(random(file.frames() - file.sampleRate()));
  // Offset part two by at least one second
  int partTwoStart = partOneStart + file.sampleRate() + partTwoOffset;
  // Make sure the start of the second sample part is not past the end of the file.
  partTwoStart = partTwoStart % file.frames();

  // Read one second worth of frames from each position
  float[] partOne = new float[file.sampleRate()];
  float[] partTwo = new float[file.sampleRate()];
  file.read(partOneStart, partOne, 0, partOne.length);
  file.read(partTwoStart, partTwo, 0, partTwo.length);
  // And write them back the other way around
  file.write(partOneStart, partTwo, 0, partTwo.length);
  file.write(partTwoStart, partOne, 0, partOne.length);
}
3 Likes

It works! I had to change a couple of lines in my own code to work with the new method, but it WORKS. Thank you so much!

And thank you for coming up with the idea in the first place.
(Btw, yes the float array thing works!)

1 Like