Hello!
I would like to save an audio recording with an incremental addendum after the filename: MySound_01.wav up to MySound_99.wav.
I tried to solve it like this:
~sounds = Pathname(“…dedicated folder, full path…”);
Pathname(~sound.fullPath ++ (“MySound”).nextName ++ “.wav”);
this produces an error message: message nextName not understood.
if I put it like this:
… ++ (“MySound.nextName”) ++ …
there is no error message but the filename looks like this: …/MySound.nextname.wav.
how can I manage to get an incremental addendum as I would like it to be?
thanks for all assistance!
best
Rainer
Hello Rainer,
nextName is not a method in String but in PathName. However, it returns a string:
// call nextName on an instance of PathName
~song = PathName("~/Desktop/song").nextName;
// ~song now holds the string "~/Desktop/song1"
~song = PathName(~song).nextName;
// ~song now holds the string "~/Desktop/song2"
… I haven’t used PathName in my code yet. I’m not sure how practical it really is. E.g. you might want to append a file suffix - song.wav. Now your ‘next name’ won’t be song1.wav but song.wav1 - probably not exactly what you want.
Anyway, hope you’re going to figure out your way.
Best, Stefan
hello Stefan!
thank you for your suggestion!
I tried it, as you provided it, and it works when evaluating the first line and then the second, but when I evaluate the second line more often, the post window returns always „song2“ and doesn’t count up.
I have also used it in a Routine, and it overwrites the existing audio files as well.
it’s a tiny thing, but I have to put it in a home assesment, which is far mor complicated than this problem. ![]()
thanks again and all the best
Rainer
hello Stefan!
a little addendum:
it seams, as the code you provided only works propperly, if I start a new SC session…
but it dowsn’t work within a Routine either if it’s in a fresh session.
very strange…
thanks for all!
Rainer
Hi Rainer,
when I evaluate the second line more often, the post window returns always „song2“ and doesn’t count up.
Hmm… that’s strange. I just tried. First I evaluated (just once!)
~song = PathName("~/Desktop/song").nextName
… that returned a string “song1”.
Then I repeatedly executed
~song = PathName(~song).nextName
and I get:
-> song2
-> song3
-> song4
-> song5
-> song6
-> song7
-> song8
-> song9
… usw.
But anyway, I find it a bit awkward using PathName. Rather I’d set up a counter variable that gets incremented with each run and construct a name with that afterwards. As the helpfile states as well most classes in SC operating on files expect paths to be strings anyway.
One way I found using Pathname is the this:
// execute only once
~song = PathName("~/Desktop/song").nextName ++ ".wav";
// execute repeatedly afterwards
~song = PathName(PathName(~song).fileNameWithoutExtension).nextName ++ ".wav";
… not very much to my liking.
Best Stefan
Ah! A Routine… I don’t know what your Routine looks like but you know routines work asynchronously. So, maybe you’re asking for the next filename before it has been created (sorry, don’t know how to put it better at the moment). I don’t think a new session should change anything but routines may be tricky.
Hm, wait, we should probably clarify the requirement a bit.
When you start a new session, do you want the paths always to begin with “song1.wav” etc., or do you want to look in the directory and find the current latest number, and start with the next one after that?
~song = PathName("~/song").nextName will always be "song1". If there is already a “song” on disk, then yes, it will be overwritten.
Routines by themselves are fully synchronous – actually I think in this case a routine would be a great way to generate new filenames on demand.
We often use routines in conjunction with a scheduler (clock) for sequencing – maybe even a majority of the time, which makes it seem like routines are asynchronous. But it’s the scheduler that introduces the asynchronous behavior, not the routine.
If, as I inquired above, the requirement is to find out what is on disk and start with the next number, Routine is actually quite nice for that because it fully encapsulates its own state. Encapsulating the state makes it safer for use with asynchronous callers.
(
~makeNameStream = { |filePrefix, extension = "wav", minDigits = 2|
// not actually using 'inval';
// I'm including it to show where it goes if you do need it later
Routine { |inval|
// ask the filesystem what's there matching "/your/path/stuff*.wav"
var num = (filePrefix +/+ "*." ++ extension).pathMatch
// then extract numbers following the prefix
.collect { |path| path.drop(filePrefix.size).asInteger }
.maxItem; // last, what's the highest number?
var str;
if(num.isNil) { num = 0 }; // no matching files, start with 0+1 for the first result
loop {
num = num + 1;
str = num.asString;
if(str.size < minDigits) {
str = String.fill(minDigits - str.size, $0) ++ str;
};
inval = (filePrefix ++ str ++ "." ++ extension).yield;
};
};
};
)
// when initializing:
r = ~makeNameStream.("~/xyz".standardizePath, "wav");
// whenever you need the next pathname
r.next;
-> /home/dlm/xyz01.wav
-> /home/dlm/xyz02.wav
-> /home/dlm/xyz03.wav
-> /home/dlm/xyz04.wav
hjh