public WaveGenerator(List <Note> notes, float volume) { // Init chunks header = new WaveHeader(); format = new WaveFormatChunk(); data = new WaveDataChunk(); var milliseconds = notes.Select((x) => x.Duration.TotalMilliseconds).Sum(); uint numSamples = (uint)(((format.dwSamplesPerSec * milliseconds) / 1000)); int amplitude = 32760; // Max amplitude for 16-bit audio uint sampleCount = 0; // used as a "cursor" in the next Foreach // Initialize the 16-bit array and Calculate the repsective values data.shortArray = new short[numSamples]; foreach (var n in notes) { // the time-steps the sine wave takes (period [2 pi f]) divided by the steps per period double t = (Math.PI * 2 * n.Frequency) / format.dwSamplesPerSec; // Calculate the amount of samples you will use for said Note var samplesThisNote = format.dwSamplesPerSec * n.Duration.TotalSeconds; // Loop over sampleCount as said before (it acts as u cursor) for (uint i = sampleCount; i < (samplesThisNote + sampleCount) - 1; i++) { data.shortArray[i] = Convert.ToInt16(amplitude * Math.Sin(t * i) * volume); } sampleCount += (uint)samplesThisNote; } // Calculate data chunk size in bytes data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); }
public fileSaver(WaveHeader header, WaveFormatChunk format, WaveDataChunk data, string filePath) { using (var fileStream = new FileStream(filePath, FileMode.Create)) { using (var writer = new BinaryWriter(fileStream)) { // Write the header writer.Write(header.sGroupID.ToCharArray()); writer.Write(header.dwFileLength); writer.Write(header.sRiffType.ToCharArray()); // Write the format chunk writer.Write(format.sChunkID.ToCharArray()); writer.Write(format.dwChunkSize); writer.Write(format.wFormatTag); writer.Write(format.wChannels); writer.Write(format.dwSamplesPerSec); writer.Write(format.dwAvgBytesPerSec); writer.Write(format.wBlockAlign); writer.Write(format.wBitsPerSample); // Write the data chunk writer.Write(data.sChunkID.ToCharArray()); writer.Write(data.dwChunkSize); foreach (short dataPoint in data.shortArray) { writer.Write(dataPoint); } // Jump back to Header.FileLength and calculate the TOTAL filesize writer.Seek(4, SeekOrigin.Begin); writer.Write((uint)writer.BaseStream.Length - 8); // Header parts are ignore (-8) } } }
public void Write (float[] clipData, WaveFormatChunk format, FileStream stream) { WaveHeader header = new WaveHeader (); WaveDataChunk data = new WaveDataChunk (); data.shortArray = new short[clipData.Length]; for (int i = 0; i < clipData.Length; i++) data.shortArray [i] = (short)(clipData [i] * 32767); data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); BinaryWriter writer = new BinaryWriter (stream); writer.Write (header.sGroupID.ToCharArray ()); writer.Write (header.dwFileLength); writer.Write (header.sRiffType.ToCharArray ()); writer.Write (format.sChunkID.ToCharArray ()); writer.Write (format.dwChunkSize); writer.Write (format.wFormatTag); writer.Write (format.wChannels); writer.Write (format.dwSamplesPerSec); writer.Write (format.dwAvgBytesPerSec); writer.Write (format.wBlockAlign); writer.Write (format.wBitsPerSample); writer.Write (data.sChunkID.ToCharArray ()); writer.Write (data.dwChunkSize); foreach (short dataPoint in data.shortArray) { writer.Write (dataPoint); } writer.Seek (4, SeekOrigin.Begin); uint filesize = (uint)writer.BaseStream.Length; writer.Write (filesize - 8); writer.Close (); }
public SoundGenerator(List <SoundFragment> sound) { header = new WaveHeader(); format = new WaveFormatChunk(); data = new WaveDataChunk(); int numberOfSamples = sound.Sum(x => (int)(format.dwSamplesPerSec * format.wChannels * x.Duration)); data.shortArray = new short[numberOfSamples]; int amplitude = 32760; int counter = 0; foreach (SoundFragment fragment in sound) { double t = (Math.PI * 2 * fragment.Frequency) / (format.dwSamplesPerSec * format.wChannels); uint sampleCountForFragment = (uint)(format.dwSamplesPerSec * format.wChannels * fragment.Duration); for (uint i = 0; i < sampleCountForFragment - 1; i++) { for (int channel = 0; channel < format.wChannels; channel++) { data.shortArray[counter + channel] = Convert.ToInt16(amplitude * Math.Sin(t * i)); } counter++; } } data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); }
double freq = 220.0f; // Concert A: 440Hz public WaveGenerator() { // Init chunks header = new WaveHeader(); format = new WaveFormatChunk(); data = new WaveDataChunk(); }
private long m_PcmDataOffset; // offset of 'pcm_data' in output file /// <summary> /// Constructs a new WaveFile instance. /// </summary> public WaveFile() { m_PcmData = new RiffChunkHeader(this); m_WaveFormat = new WaveFormatChunk(this); m_PcmData.CkId = FourCC("data"); m_PcmData.CkSize = 0; m_NumSamples = 0; }
private long _PcmDataOffset; // offset of 'pc_data' in output file /// <summary> /// Creates a new WaveFile instance. /// </summary> internal WaveFile() { _PcmData = new RiffChunkHeader(this); _WaveFormat = new WaveFormatChunk(this); _PcmData.CkId = FourCC("data"); _PcmData.CkSize = 0; _NumSamples = 0; }
private long m_PcmDataOffset; // offset of 'pcm_data' in output file #endregion Fields #region Constructors /// <summary> /// Constructs a new WaveFile instance. /// </summary> public WaveFile() { m_PcmData = new RiffChunkHeader(this); m_WaveFormat = new WaveFormatChunk(this); m_PcmData.CkId = FourCC("data"); m_PcmData.CkSize = 0; m_NumSamples = 0; }
/// <summary> /// Write the stored audio data as a WAVE file. /// </summary> public void SaveAsWav(string filePath) { // Make sure we have data. if (String.IsNullOrEmpty(_tempfile)) { _cbWritten = 0; return; } if (!File.Exists(_tempfile)) { _tempfile = null; _cbWritten = 0; return; } if (_cbWritten == 0) { File.Delete(_tempfile); _tempfile = null; return; } FileInfo fi = new FileInfo(_tempfile); Debug.Assert(fi.Length == _cbWritten); WaveFileWriter writer = new WaveFileWriter(filePath); writer.WriteFileHeader((int)fi.Length); WaveFormatChunk format = new WaveFormatChunk(); format.chunkId = "fmt "; format.chunkSize = 16; // size of the struct in bytes - 8 format.audioFormat = WAV_FMT_PCM; format.channelCount = _channelCount; format.sampleRate = _sampleRate; format.byteRate = (uint)(_sampleRate * _channelCount * _bitsPerFrame / 8); format.blockAlign = (ushort)(_channelCount * _bitsPerFrame / 8); format.bitsPerSample = _bitsPerFrame; writer.WriteFormatChunk(format); writer.WriteDataHeader((int)fi.Length); byte[] data = File.ReadAllBytes(_tempfile); Debug.Assert(data.Length == _cbWritten); writer.WriteData(data); writer.Close(); // Clean up the temporary data from the recording process. File.Delete(_tempfile); _tempfile = null; _cbWritten = 0; }
/// <summary> /// Writes the WAVE format block. /// </summary> public void WriteFormatChunk(WaveFormatChunk format) { if (_writer == null) { return; } _writer.Write((byte)'f'); _writer.Write((byte)'m'); _writer.Write((byte)'t'); _writer.Write((byte)' '); _writer.Write(format.chunkSize); _writer.Write(format.audioFormat); _writer.Write(format.channelCount); _writer.Write(format.sampleRate); _writer.Write(format.byteRate); _writer.Write(format.blockAlign); _writer.Write(format.bitsPerSample); }
public WaveGenerator(WaveExampleType type) { // Init chunks header = new WaveHeader(); format = new WaveFormatChunk(); data = new WaveDataChunk(); // Fill the data array with sample data switch (type) { case WaveExampleType.ExampleSineWave: // Number of samples = sample rate * channels * bytes per sample uint numSamples = format.dwSamplesPerSec * format.wChannels; // Initialize the 16-bit array data.shortArray = new short[numSamples]; int amplitude = 32760; // Max amplitude for 16-bit audio double freq = 440.0f; // Concert A: 440Hz // The "angle" used in the function, adjusted for the number of channels and sample rate. // This value is like the period of the wave. double t = (Math.PI * 2 * freq) / (format.dwSamplesPerSec * format.wChannels); for (uint i = 0; i < numSamples - 1; i++) { // Fill with a simple sine wave at max amplitude for (int channel = 0; channel < format.wChannels; channel++) { data.shortArray[i + channel] = Convert.ToInt16(amplitude * Math.Sin(t * i)); } } // Calculate data chunk size in bytes data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); break; } }
public void Write(float[] clipData, WaveFormatChunk format, FileStream stream) { WaveHeader header = new WaveHeader(); WaveDataChunk data = new WaveDataChunk(); data.shortArray = new short[clipData.Length]; for (int i = 0; i < clipData.Length; i++) { data.shortArray [i] = (short)(clipData [i] * 32767); } data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); BinaryWriter writer = new BinaryWriter(stream); writer.Write(header.sGroupID.ToCharArray()); writer.Write(header.dwFileLength); writer.Write(header.sRiffType.ToCharArray()); writer.Write(format.sChunkID.ToCharArray()); writer.Write(format.dwChunkSize); writer.Write(format.wFormatTag); writer.Write(format.wChannels); writer.Write(format.dwSamplesPerSec); writer.Write(format.dwAvgBytesPerSec); writer.Write(format.wBlockAlign); writer.Write(format.wBitsPerSample); writer.Write(data.sChunkID.ToCharArray()); writer.Write(data.dwChunkSize); foreach (short dataPoint in data.shortArray) { writer.Write(dataPoint); } writer.Seek(4, SeekOrigin.Begin); uint filesize = (uint)writer.BaseStream.Length; writer.Write(filesize - 8); writer.Close(); }
public WaveGenerator(List <short> Data) { header = new WaveHeader(); format = new WaveFormatChunk(); data = new WaveDataChunk(); int numSamples = Data.Count; data.shortArray = new short[numSamples]; for (int i = 0; i < numSamples; i++) { data.shortArray[i] = Data[i]; } data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); TimeSpan TS = new TimeSpan(0, 0, (int)((float)numSamples / (float)format.dwSamplesPerSec)); if (Verbose) { Console.WriteLine(""); } Console.WriteLine("wave generated: {0} seconds: {1}", (float)numSamples / (float)format.dwSamplesPerSec, TS.ToString()); }
public WaveFile(int sampleRate, short bitsPerSample, short channels, ushort audioFormat) { m_waveHeader = new RiffHeaderChunk("WAVE"); m_waveFormat = new WaveFormatChunk(sampleRate, bitsPerSample, channels, audioFormat); m_waveData = new WaveDataChunk(m_waveFormat); }
public WaveFile(RiffHeaderChunk waveHeader, WaveFormatChunk waveFormat, WaveDataChunk waveData) { m_waveHeader = waveHeader; m_waveFormat = waveFormat; m_waveData = waveData; }
/// <summary> /// Creates a new in-memory wave loaded from an existing wave audio stream. /// </summary> /// <param name="source">Stream of WAV formatted audio data to load.</param> /// <returns>In-memory representation of wave file.</returns> public static WaveFile Load(Stream source) { RiffChunk riffChunk; RiffHeaderChunk waveHeader = null; WaveFormatChunk waveFormat = null; WaveDataChunk waveData = null; while (waveData == null) { riffChunk = RiffChunk.ReadNext(source); switch (riffChunk.TypeID) { case RiffHeaderChunk.RiffTypeID: waveHeader = new RiffHeaderChunk(riffChunk, source, "WAVE"); break; case WaveFormatChunk.RiffTypeID: if (waveHeader == null) throw new InvalidDataException("WAVE format section encountered before RIFF header, wave file corrupted"); waveFormat = new WaveFormatChunk(riffChunk, source); break; case WaveDataChunk.RiffTypeID: if (waveFormat == null) throw new InvalidDataException("WAVE data section encountered before format section, wave file corrupted"); waveData = new WaveDataChunk(riffChunk, source, waveFormat); break; default: // Skip unidentified section source.Seek(riffChunk.ChunkSize, SeekOrigin.Current); break; } } return new WaveFile(waveHeader, waveFormat, waveData); }
/// <summary> /// Creates a new empty in-memory wave file using standard CD quality settings. /// </summary> public WaveFile() { m_waveHeader = new RiffHeaderChunk("WAVE"); m_waveFormat = new WaveFormatChunk(44100, 16, 2, 0x1); m_waveData = new WaveDataChunk(m_waveFormat); }
/// <summary> /// Writes the WAVE format block. /// </summary> public void WriteFormatChunk(WaveFormatChunk format) { if (_writer == null) return; _writer.Write((byte)'f'); _writer.Write((byte)'m'); _writer.Write((byte)'t'); _writer.Write((byte)' '); _writer.Write(format.chunkSize); _writer.Write(format.audioFormat); _writer.Write(format.channelCount); _writer.Write(format.sampleRate); _writer.Write(format.byteRate); _writer.Write(format.blockAlign); _writer.Write(format.bitsPerSample); }
/// <summary> /// Creates a new in-memory wave loaded from an existing wave audio stream. /// </summary> /// <param name="source">Stream of WAV formatted audio data to load.</param> /// <param name="loadData">Determines if wave data should be loaded into memory.</param> /// <returns>In-memory representation of wave file.</returns> public static WaveFile Load(Stream source, bool loadData = true) { RiffChunk riffChunk; RiffHeaderChunk waveHeader = null; WaveFormatChunk waveFormat = null; WaveDataChunk waveData = null; ListInfoChunk listInfo = null; while (source.Position < source.Length) { riffChunk = RiffChunk.ReadNext(source); switch (riffChunk.TypeID) { case RiffHeaderChunk.RiffTypeID: waveHeader = new RiffHeaderChunk(riffChunk, source, "WAVE"); break; case WaveFormatChunk.RiffTypeID: if ((object)waveHeader == null) throw new InvalidDataException("WAVE format section encountered before RIFF header, wave file corrupted"); waveFormat = new WaveFormatChunk(riffChunk, source); break; case WaveDataChunk.RiffTypeID: if ((object)waveFormat == null) throw new InvalidDataException("WAVE data section encountered before format section, wave file corrupted"); if (loadData) { waveData = new WaveDataChunk(riffChunk, source, waveFormat); } else { source.Seek(riffChunk.ChunkSize, SeekOrigin.Current); waveData = new WaveDataChunk(waveFormat); waveData.ChunkSize = riffChunk.ChunkSize; } break; case ListInfoChunk.RiffTypeID: listInfo = new ListInfoChunk(riffChunk, source); break; default: // Skip unidentified section source.Seek(riffChunk.ChunkSize, SeekOrigin.Current); break; } } return new WaveFile(waveHeader, waveFormat, waveData, listInfo); }
/// <summary>Creates a new in-memory wave loaded from an existing wave file.</summary> /// <param name="waveFileName">File name of WAV file to load.</param> /// <returns>In-memory representation of wave file.</returns> public static WaveFile Load(string waveFileName) { FileStream source = File.Open(waveFileName, FileMode.Open, FileAccess.Read, FileShare.Read); RiffChunk riffChunk; RiffHeaderChunk waveHeader = null; WaveFormatChunk waveFormat = null; WaveDataChunk waveData = null; while (waveData == null) { riffChunk = RiffChunk.ReadNext(source); switch (riffChunk.TypeID) { case RiffHeaderChunk.RiffTypeID: waveHeader = new RiffHeaderChunk(riffChunk, source, "WAVE"); break; case WaveFormatChunk.RiffTypeID: if (waveHeader == null) throw new InvalidDataException("WAVE format section encountered before RIFF header, wave file corrupted."); waveFormat = new WaveFormatChunk(riffChunk, source); break; case WaveDataChunk.RiffTypeID: if (waveFormat == null) throw new InvalidDataException("WAVE data section encountered before format section, wave file corrupted."); waveData = new WaveDataChunk(riffChunk, source, waveFormat); break; default: break; } } return new WaveFile(waveHeader, waveFormat, waveData); }
public WaveFormatChunk MakeFormat(AudioClip clip) { WaveFormatChunk format = new WaveFormatChunk(8000, 1); return(format); }
public WaveGenerator(WaveExampleType type, float frequency, float volume, float duration) { //Cap parameters if (frequency < 200) { frequency = 200; } else if (frequency > 2000) { frequency = 1200; } if (volume < 0f) { volume = 0f; } else if (volume > 1f) { volume = 1f; } if (duration < 0) { duration = 0f; } else if (duration > 2.5f) { duration = 1.0f; } // Init chunks header = new WaveHeader(); format = new WaveFormatChunk(); data = new WaveDataChunk(); // Fill the data array with sample data switch (type) { case WaveExampleType.Sine: // Number of samples = duration * sample rate * channels * bytes per sample uint numSamples = (uint)(duration * format.dwSamplesPerSec * format.wChannels); // Initialize the 16-bit array data.shortArray = new short[numSamples]; float amplitude = volume * 32760; // Max amplitude for 16-bit audio if (frequency == 0) { frequency = 440.0f; // Concert A: 440Hz } // The "angle" used in the function, adjusted for the number of channels and sample rate. // This value is like the period of the wave. double t = (Math.PI * 2 * frequency) / (format.dwSamplesPerSec * format.wChannels); for (uint i = 0; i < numSamples - 1; i++) { // Fill with a simple sine wave at max amplitude for (int channel = 0; channel < format.wChannels; channel++) { data.shortArray[i + channel] = Convert.ToInt16(amplitude * Math.Sin(t * i)); } } // Calculate data chunk size in bytes data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); break; } //switch } //Constructor
public WaveFormatChunk MakeFormat (AudioClip clip) { WaveFormatChunk format = new WaveFormatChunk (8000, 1); return format; }
/// <summary> /// Main program. /// </summary> /// <param name="args"></param> private void Run(string[] args) { if (args.Length == 0) { Console.WriteLine( @"{0} [options ...] <path to MIDI.mid> Description: Clicker generates a WAVE file click track given a MIDI sequence with meter/key and tempo change messages. The click track serves as a solid, sample-accurate metronome that will line up with the MIDI sequence. You can import the generated click track into any MIDI-friendly DAW tool such as SONAR, Cubase, etc. to record with. You can even share the click track with other recording artists working on your project to serve as a timebase to help synchronize work across distances. Author: James S. Dunne http://bittwiddlers.org/ Copyright: 2011, bittwiddlers.org Source: http://github.com/JamesDunne/clicker Options: -s <samplerate> Set the output WAVE file's sample rate in Hz (default 48000 Hz) -c <channels> Set the output WAVE file's number of channels (1 or 2, default 2) -d <click division> Set the metronome to click on each (2^N)th note, scaling meter signatures appropriately to match. (default: off, click on meter beats only) -ao Attenuate meter's off-beats if meter is faster than the metronome. (default: off) -ad Attenuate inserted beats if metronome is clicking faster than the meter. (default: off) <path to MIDI.mid> Path to the MIDI arrangement to generate the click track for. Outputs: <path to MIDI.mid>.click.wav ", Process.GetCurrentProcess().ProcessName); return; } bool early = false; Queue<string> aq = new Queue<string>(args); while (!early && (aq.Count > 0)) { string arg = aq.Peek(); switch (arg.ToLower()) { case "-s": aq.Dequeue(); if (!Int32.TryParse(aq.Dequeue(), out samplesPerSec)) samplesPerSec = 48000; break; case "-c": aq.Dequeue(); if (!Int32.TryParse(aq.Dequeue(), out channels)) channels = 1; break; case "-d": aq.Dequeue(); if (Int32.TryParse(aq.Dequeue(), out clickOnDivision)) forceClickDivision = true; break; case "-ao": aq.Dequeue(); attenuateProperOffBeats = true; break; case "-ad": aq.Dequeue(); attenuateDividedBeats = true; break; default: early = true; break; } } if (aq.Count < 1) { Console.WriteLine("Expected path to MIDI sequence."); return; } FileInfo midiFile = new FileInfo(aq.Dequeue()); if (!midiFile.Exists) { Console.WriteLine("Could not find path '{0}'.", midiFile.FullName); return; } // Load the MIDI sequence: Sequence seq = new Sequence(midiFile.FullName); // Load our clicks (stereo 16-bit clips): var asm = System.Reflection.Assembly.GetExecutingAssembly(); #if true byte[] pinghiraw = getAllBytes(asm.GetManifestResourceStream("Clicker.pinghi48k16b.raw")); #else byte[] pinghiraw = File.ReadAllBytes("pinghi48k16b.raw"); #endif short[,] pinghi = new short[pinghiraw.Length / 4, 2]; for (int i = 0, b = 0; i < pinghiraw.Length - 4; i += 4, ++b) { pinghi[b, 0] = unchecked((short)(pinghiraw[i + 0] | (pinghiraw[i + 1] << 8))); pinghi[b, 1] = unchecked((short)(pinghiraw[i + 2] | (pinghiraw[i + 3] << 8))); } int pinghiLength = pinghi.GetUpperBound(0) + 1; #if true byte[] pingloraw = getAllBytes(asm.GetManifestResourceStream("Clicker.pinglo48k16b.raw")); #else byte[] pingloraw = File.ReadAllBytes("pinglo48k16b.raw"); #endif short[,] pinglo = new short[pingloraw.Length / 4, 2]; for (int i = 0, b = 0; i < pingloraw.Length - 4; i += 4, ++b) { pinglo[b, 0] = unchecked((short)(pingloraw[i + 0] | (pingloraw[i + 1] << 8))); pinglo[b, 1] = unchecked((short)(pingloraw[i + 2] | (pingloraw[i + 3] << 8))); } int pingloLength = pinglo.GetUpperBound(0) + 1; // Grab meter and tempo changes from any track: var timeChanges = from tr in seq from ev in tr.Iterator() where ev.MidiMessage.MessageType == MessageType.Meta let mm = (MetaMessage)ev.MidiMessage where mm.MetaType == MetaType.TimeSignature || mm.MetaType == MetaType.Tempo orderby ev.AbsoluteTicks ascending select new { ev, mm }; var lastEvent = ( from tr in seq from ev in tr.Iterator() orderby ev.AbsoluteTicks ascending select ev ).Last(); // Create a default tempo of 120 bpm (500,000 us/b): var tcb = new TempoChangeBuilder() { Tempo = 500000 }; tcb.Build(); currentTempo = new TempoMessage(tcb.Result); // Create a default time signature of 4/4: var tsb = new TimeSignatureBuilder() { Numerator = 4, Denominator = 4 }; tsb.Build(); currentTimeSignature = new TimeSignatureMessage(tsb.Result); ticksPerQuarter = seq.Division; calcUsecPerTick(); calcBeatTicks(); double sample = 0d; samplesPerUsec = (double)samplesPerSec / 1000000d; string outWaveFile = Path.Combine(midiFile.Directory.FullName, midiFile.Name + ".click.wav"); Console.WriteLine("Writing click track to '{0}'", outWaveFile); var format = new WaveFormatChunk(); format.dwSamplesPerSec = (uint)samplesPerSec; format.wChannels = (ushort)channels; format.wBitsPerSample = (ushort)bitsPerSample; Console.WriteLine( "Sample rate = {0,6} Hz; Channels = {1,1}; BitsPerSample = {2,2}", format.dwSamplesPerSec, format.wChannels, format.wBitsPerSample ); // Open the WAVE for output: using (var wav = File.Open(outWaveFile, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var bs = new BufferedStream(wav)) using (var bw = new BinaryWriter(bs)) { var header = new WaveHeader(); // Write the header bw.Write(header.sGroupID.ToCharArray()); bw.Write(header.dwFileLength); bw.Write(header.sRiffType.ToCharArray()); // Write the format chunk bw.Write(format.sChunkID.ToCharArray()); bw.Write(format.dwChunkSize); bw.Write(format.wFormatTag); bw.Write(format.wChannels); bw.Write(format.dwSamplesPerSec); bw.Write(format.dwAvgBytesPerSec); bw.Write(format.wBlockAlign); bw.Write(format.wBitsPerSample); var data = new WaveDataChunk(); // Write the data chunk bw.Write(data.sChunkID.ToCharArray()); bw.Write(data.dwChunkSize); double lastSample = sample; int nextBeatTick = 0; int note = 0; int tick = 0; using (var en = timeChanges.GetEnumerator()) { MidiEvent nextEvent; bool haveKeyOrTempoChange = en.MoveNext(); var me = en.Current; nextEvent = me.ev; while (tick < lastEvent.AbsoluteTicks) { for (; tick < nextEvent.AbsoluteTicks; ++tick) { // Start a click at this tick: if (tick == nextBeatTick) { int beat = note; //Debug.WriteLine("Click at tick {0,7}, sample {1,12:#######0.00}, beat {2,2}", tick, sample, beat); // Copy in a click: double vol = doAttenuateBeat(beat) ? 0.3d : 1d; // Silence until start of this click: int x = (int)((long)sample - (long)lastSample); for (; x > 0; --x) { for (int j = 0; j < channels; ++j) bw.Write((short)0); } // Choose the click sound based on the beat: short[,] click = (beat == 0) ? pinglo : pinghi; int clickLength = (beat == 0) ? pingloLength : pinghiLength; // Write the portion of the click if we missed the start: int samplesWritten = 0; long delta = x; for (x = -x; x < clickLength; ++x, ++samplesWritten) { int y = (int)((double)x * 48000d / (double)samplesPerSec); if (y >= clickLength) break; for (int j = 0; j < channels; ++j) bw.Write((short)(click[y, j] * vol)); } lastSample = sample + samplesWritten + delta; // Set next beat tick: nextBeatTick = tick + beatTicks; note = (note + 1) % getNumerator(); } sample += samplesPerTick; } if (haveKeyOrTempoChange) { if (me.mm.MetaType == MetaType.Tempo) { currentTempo = new TempoMessage(me.mm); calcUsecPerTick(); Console.WriteLine( "{0,9}: tempo {1,8:###0.000} bpm = {2,9:#,###,##0} usec/qtr", me.ev.AbsoluteTicks, 500000d / currentTempo.MicrosecondsPerQuarter * 120, currentTempo.MicrosecondsPerQuarter ); } else { currentTimeSignature = new TimeSignatureMessage(me.mm); calcBeatTicks(); #if false // NOTE: Assume key change is on a beat tick; force a reset of beats anyway. //nextBeatTick = tick; //note = 0; #endif Console.WriteLine( "{0,9}: meter {1,2}/{2,-2} treating as {3,2}/{4,-2}", me.ev.AbsoluteTicks, currentTimeSignature.Numerator, currentTimeSignature.Denominator, getNumerator(), getDenominator() ); } haveKeyOrTempoChange = en.MoveNext(); if (haveKeyOrTempoChange) { me = en.Current; nextEvent = me.ev; } else { me = null; nextEvent = lastEvent; } } } } // Write RIFF file size: bw.Seek(4, SeekOrigin.Begin); uint filesize = (uint)wav.Length; bw.Write(filesize - 8); // Write "data" chunk size: bw.Seek(0x28, SeekOrigin.Begin); bw.Write(filesize - 0x2C); } Console.WriteLine("Click track written to '{0}'", outWaveFile); Console.WriteLine( "Sample rate = {0,6} Hz; Channels = {1,1}; BitsPerSample = {2,2}", format.dwSamplesPerSec, format.wChannels, format.wBitsPerSample ); }
public WaveGenerator(Signal type, int amplitude, int frequency, int phaze, bool noize) { header = new WaveHeader(); format = new WaveFormatChunk(); data = new WaveDataChunk(); double sinValue; uint numSamples; Amplitude = amplitude; Frequency = frequency; Phaze = phaze; switch (type) { case Signal.syn: numSamples = format.dwSamplesPerSec * format.wChannels; data.shortArray = new short[numSamples]; double t = (Math.PI * 2 * Frequency) / (format.dwSamplesPerSec * format.wChannels); for (uint i = 0; i < numSamples - 1; i++) { for (int channel = 0; channel < format.wChannels; channel++) { data.shortArray[i + channel] = Convert.ToInt16(Amplitude * Math.Sin(t * i + Phaze)); data.shortArray[i + channel] = noize ? Convert.ToInt16(data.shortArray[i + channel] + NoizeStep) : data.shortArray[i + channel]; } } data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); break; case Signal.rectangle: numSamples = format.dwSamplesPerSec * format.wChannels; data.shortArray = new short[numSamples]; for (uint i = 0; i < numSamples - 1; i++) { for (int channel = 0; channel < format.wChannels; channel++) { sinValue = (2 * Math.PI * Frequency) / (format.dwSamplesPerSec * format.wChannels); data.shortArray[i + channel] = Convert.ToInt16(amplitude * Math.Sign(Math.Sin((i * sinValue + phaze)))); data.shortArray[i + channel] = noize ? Convert.ToInt16(data.shortArray[i + channel] + NoizeStep) : data.shortArray[i + channel]; } } data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); break; case Signal.triangle: numSamples = format.dwSamplesPerSec * format.wChannels; data.shortArray = new short[numSamples]; for (uint i = 0; i < numSamples - 1; i++) { for (int channel = 0; channel < format.wChannels; channel++) { sinValue = (2 * Math.PI * Frequency) / (format.dwSamplesPerSec * format.wChannels); data.shortArray[i + channel] = Convert.ToInt16(amplitude * Math.Asin(Math.Sin((i * sinValue + phaze)))); data.shortArray[i + channel] = noize ? Convert.ToInt16(data.shortArray[i + channel] + NoizeStep) : data.shortArray[i + channel]; } } data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); break; case Signal.saw: numSamples = format.dwSamplesPerSec * format.wChannels; data.shortArray = new short[numSamples]; for (uint i = 0; i < numSamples - 1; i++) { for (int channel = 0; channel < format.wChannels; channel++) { sinValue = (Math.PI * Frequency) / (format.dwSamplesPerSec * format.wChannels); data.shortArray[i + channel] = Convert.ToInt16(-2 * amplitude / Math.PI * Math.Atan(1 / Math.Tan((i * sinValue + phaze)))); data.shortArray[i + channel] = noize ? Convert.ToInt16(data.shortArray[i + channel] + NoizeStep) : data.shortArray[i + channel]; } } data.dwChunkSize = (uint)(data.shortArray.Length * (format.wBitsPerSample / 8)); break; } }
public WaveFile(RiffHeaderChunk waveHeader, WaveFormatChunk waveFormat, WaveDataChunk waveData, ListInfoChunk listInfo = null) { m_waveHeader = waveHeader; m_waveFormat = waveFormat; m_waveData = waveData; m_listInfo = listInfo; }
public void save(WaveHeader h, WaveFormatChunk f, WaveDataChunk d, string path) { fileSaver fs = new fileSaver(h, f, d, path); }