public void ExportSong() { // Get the active song data var songData = musicChip.trackerDataCollection[patterns[currentPattern]]; var totalNotesRendered = 0; int tracknum; int notenum; int gotANote; var tcount = songData.tracks.Length; var ncount = songData.tracks[0].notes.Length; var totalNotesInSong = ncount; // total musical notes in song loops x notes note_tick_s = 15.0f / songData.speedInBPM; // (15.0f/120.0f) = 120BPM sixteenth notes note_tick_s_odd = note_tick_s * swing_rhythm_factor; // small beat var notedatalength = (int)(preRenderBitrate * 2f * note_tick_s_odd); // one note worth of stereo audio (x2) // TODO this is off. It is happening to fast and not matching up to the correct speed of the song. var beatlength = preRenderBitrate * note_tick_s_odd; // one note worth of time var songdatalength = notedatalength * totalNotesInSong; // stereo cd quality x song length var notebuffer = new float[notedatalength]; if (trackresult == null) { // all the tracks we need - an array of audioclips that will be merged into result trackresult = new RawAudioData[tcount]; for (var i = 0; i < tcount; i++) { trackresult[i] = new RawAudioData(0, 1, preRenderBitrate); } } var instrument = new SfxrSynth[songData.totalTracks]; var newStartPos = trackresult[0].samples / 2; var newLength = trackresult[0].samples + songdatalength; for (tracknum = 0; tracknum < tcount; tracknum++) { if (instrument[tracknum] == null) { instrument[tracknum] = new SfxrSynth(); } var songdataCurrentPos = newStartPos; trackresult[tracknum].Resize(newLength); // set the params to current track's instrument string instrument[tracknum].parameters .SetSettingsString(soundChip.ReadSound(songData.tracks[tracknum].sfxID).param); // Loop through all of the notes in the track for (notenum = 0; notenum < ncount; notenum++) { // what note is it? gotANote = songData.tracks[tracknum].notes[notenum]; // generate one note worth of audio data if (gotANote > 0 && instrument[tracknum] != null) { //instrument[tracknum].Reset(true); // seems to do nothing // doing this for every note played is insane: // try a fresh new instrument (RAM and GC spammy) instrument[tracknum] = new SfxrSynth(); // shouldn't be required, but for some reason it is // set the params to current track's instrument string instrument[tracknum].parameters .SetSettingsString(soundChip.ReadSound(songData.tracks[tracknum].sfxID).param); // pitch shift the sound to the correct musical note frequency instrument[tracknum].parameters.startFrequency = noteStartFrequency[gotANote]; // maybe this will help // instrument[tracknum].GenerateAudioFilterData(float[] __data, int __channels) // or perhaps this - nope: outputs silence... hmm //bool gotAllData = instrument[tracknum].SynthWave(notebuffer, 0, (uint)notedatalength);// (uint)notebuffer.Length); // unlikely but helpful - used by GenerateAudioFilterData // WriteSamples(float[] __originSamples, int __originPos, float[] __targetSamples, int __targetChannels) { // Writes raw samples to Unity's format and return number of samples actually written // does play need to be called for it to be generated properly? instrument[tracknum].CacheSound(); // generate cachedWave data // grab the sample data - this data is not in the same fmt as unity's internal? // sounds pitch-shifted and faster - a different bitrate? // WORKS GREAT IF WE RUN CACHESOUND() FIRST // TODO need to figure out why we can't access the cachedWave notebuffer = instrument[tracknum].data; // the wave data for the current note // this SHOULD be the solution. but outputs all 0's //bool gotAllData = instrument[tracknum].GenerateAudioFilterData(notebuffer, 2); // move sound data into audioclip buffer if (notebuffer != null && notebuffer.Length > 0) { trackresult[tracknum].SetData(notebuffer, songdataCurrentPos); } // else // Debug.LogWarning("Notebuffer was blank. Ignoring."); totalNotesRendered++; } // TODO converted this to an int, may break? songdataCurrentPos += (int)beatlength; //notedatalength; this is x2 due to stereo //yield return null; // if here, min one frame PER note PER track PER loop: too slow } // for all notes } // for all tracks currentPattern++; currentStep++; }
private void MixdownAudioClips() { result = MixdownAudioClips(trackresult); currentStep++; }
// pre-rendered waveforms - OPTIMIZATION - SLOW SLOW SLOW - FIXME public RawAudioData MixdownAudioClips(params RawAudioData[] clips) { if (clips == null || clips.Length == 0) { return(null); } var length = clips[0].samples * clips[0].channels; // assumes all are same length var buffer = new float[length]; var data = new float[length]; var loop = 0; var clipWarnings = 0; // did we red zone? var redlineMax = 0f; // so we know how overly loud it was var maxVolume = 0f; // before clipping // fill with silence for (loop = 0; loop < length; loop++) { data[loop] = 0f; } for (var i = 0; i < clips.Length; i++) { if (clips[i] == null) { continue; } buffer = clips[i].data; //.GetData(buffer); // mix two signals together: we might get too loud... // FIXME: we may need to make all clips a bit quieter for (loop = 0; loop < length; loop++) { data[loop] += buffer[loop] * mixdownTrackVolume; // add it to the old signal if (data[loop] > maxVolume) { maxVolume = data[loop]; } if (data[loop] > 1f) // too loud? { clipWarnings++; if (data[loop] - 1f > redlineMax) { redlineMax = data[loop] - 1f; } data[loop] = 1f; } } } // stereo //AudioClip result = AudioClip.Create("MixdownSTEREO", length / 2, 2, preRenderBitrate, false); // mono var result = new RawAudioData(length / 2, 1, preRenderBitrate); result.SetData(data); // TODO: we can get a warning here: data too large to fit: discarded x samples // the truncation can happen with a large sustain of a note that could go on after the song is over // one solution is to pad the end with 4sec of 0000s then maybe search and TRIM return(result); }
public RawAudioData Mp3ToPcm(string fileName) { var resStream = new MemoryStream(); var result = new RawAudioData(); if (File.Exists(fileName)) { using (var mp3 = new Mp3FileReader(fileName)) { var frame0 = mp3.ReadNextFrame(); result.SampleRate = frame0.SampleRate; result.BitRate = frame0.BitRate; mp3.Seek(0, SeekOrigin.Begin); mp3.CopyTo(resStream); } } result.Data = resStream.ToArray(); return result; }