/// <summary> /// Initializes a new sound instance, and loads its sample from the given /// file path (and or memory url). /// </summary> /// <param name="path">File path or memory url to sound to load.</param> /// <param name="flags">Describes how this sound should be loaded and played.</param> public Sound(object path, SoundFlags flags) { // Save details. _flags = flags; _url = path as string; // Load the audio sample then. _sample = SampleFactory.LoadSample(path, (flags & SoundFlags.Streamed) != 0); // Load one buffer for quick response. ISampleBuffer buffer = AudioManager.Driver.CreateSampleBuffer(_sample, flags); _frequency = buffer.Frequency; _buffers.Add(buffer); // If its not dynamic then destroy the samples data as its just cluttering up space. if ((_flags & SoundFlags.Dynamic) == 0) { if (_sample != null) { _sampleLength = _sample.Data.Length; _sample.Data = null; } //GC.Collect(); _globalBuffer = buffer; // Save the global buffer as its what we will get our data from. } }
/// <summary> /// This method is called when Sample load is requested, if you return a Sample /// it will return it from the user else it will keep trying all the other SampleLoaders /// until it does get one /// </summary> /// <param name="path">File path of the sound to load.</param> /// <param name="streamed">If set to true the sound will be loaded in piece by piece rather than all at once.</param> /// <returns>New Sample instance or NULL if this factory can't load the given audio sample file format.</returns> protected override Sample RequestLoad(object path, bool streamed) { // Load in the audio file's data. Stream stream = StreamFactory.RequestStream(path, StreamMode.Open); if (stream == null) return null; if (stream.ReadByte() != 'O' || stream.ReadByte() != 'g' || stream.ReadByte() != 'g' || stream.ReadByte() != 'S') { stream.Close(); return null; } stream.Position = 0; byte[] data = new byte[stream.Length]; stream.Read(data, 0, (int)stream.Length); stream.Close(); // Create an audiere memory file to place our data into. ManagedAudiere.File file = Audiere.CreateMemoryFile(new MemoryFileBuffer(data, data.Length)); SampleSource audiereSample = Audiere.OpenSampleSource(file); SampleFormatData sampleFormat = audiereSample.GetFormat(); int size = audiereSample.Length * (Audiere.GetSampleSize(sampleFormat.sample_format) * sampleFormat.channel_count); MemoryFileBuffer audiereBuffer = new MemoryFileBuffer(new byte[size], size); audiereSample.Read(audiereSample.Length, audiereBuffer); // Put the audiere audio buffer into a new sample buffer. byte[] pcmBuffer = audiereBuffer.GetBuffer(); Sample sample = null; if (sampleFormat.channel_count == 1) sample = new Sample(SampleFormat.MONO16LE, pcmBuffer.Length); else sample = new Sample(SampleFormat.STEREO16LE, pcmBuffer.Length); sample.SampleRate = sampleFormat.sample_rate; sample.ChannelCount = sampleFormat.channel_count; sample.BlockAlign = Audiere.GetSampleSize(sampleFormat.sample_format) * sampleFormat.channel_count; sample.ByteRate = sample.SampleRate * sample.BlockAlign; sample.BitsPerSample = Audiere.GetSampleSize(sampleFormat.sample_format) * 8; for (int i = 0; i < pcmBuffer.Length; i++) sample.Data[i] = pcmBuffer[i]; return sample; }
/// <summary> /// Loads a chunk of data from a samples stream. /// </summary> /// <param name="sample">Sample that streamed data needs to be loaded from.</param> /// <param name="size">Size in bytes of data that is required.</param> /// <param name="position">The variable used for tracking streamed data.</param> /// <param name="wrap">Variable indicating if data can be wrapped (used for looping).</param> /// <returns>Newly loaded data.</returns> public byte[] RequestStreamedData(Sample sample, long size, ref int position, bool wrap) { if ((sample.DataStream.Position - sample.DataChunkPosition) >= sample.DataChunkLength) return new byte[0]; // Keep reading and wrapping around until we read all the data that we need byte[] data = null; long readPosition = sample.DataChunkPosition + position; sample.DataStream.Position = readPosition; if (wrap == true) { data = new byte[size]; long dataPosition = 0; while (true) { // Read in what is left. long sizeLeft = sample.DataChunkLength - (sample.DataStream.Position - sample.DataChunkPosition); long possibleSize = ((data.Length - dataPosition) >= sizeLeft ? sizeLeft : (data.Length - dataPosition)); sample.DataStream.Read(data, (int)dataPosition, (int)possibleSize); dataPosition += possibleSize; // Wrap around to the beginning if neccessary. if (dataPosition >= data.Length) break; else sample.DataStream.Position = sample.DataChunkPosition; } } else { long sizeLeft = sample.DataChunkLength - (sample.DataStream.Position - sample.DataChunkPosition); data = new byte[size > sizeLeft ? sizeLeft : size]; sample.DataStream.Read(data, 0, data.Length); } position = (int)(sample.DataStream.Position - sample.DataChunkPosition); return data; }
/// <summary> /// This method is called when Sample save is requested, if it returns true /// the calling method will stop illiterating through the SampleFactorys and /// return success to the user. /// </summary> /// <param name="path">File path or object of the image to load.</param> /// <param name="sample">Sample to save.</param> /// <param name="flags">Bitmask of flags defining how the sample should be saved.</param> /// <returns>True if the save was successfull else false.</returns> protected override bool RequestSave(object path, Sample sample, SampleSaveFlags flags) { if (path.ToString().ToLower().EndsWith(".ogg") == false) return false; throw new Exception("OGG save factory currently unfinished and as such unsupported."); }
/// <summary> /// This method is called when Sample load is requested, if you return a Sample /// it will return it from the user else it will keep trying all the other SampleLoaders /// until it does get one /// </summary> /// <param name="path">File path of the sound to load.</param> /// <param name="streamed">If set to true the sound will be loaded in piece by piece rather than all at once.</param> /// <returns>New Sample instance or NULL if this factory can't load the given audio sample file format.</returns> protected override Sample RequestLoad(object path, bool streamed) { if (path.ToString().ToLower().EndsWith(".wav") == false && path.ToString().ToLower().EndsWith(".wave") == false) return null; Stream stream = StreamFactory.RequestStream(path, StreamMode.Open); if (stream == null) return null; BinaryReader reader = new BinaryReader(stream); //Check the header is correct. if (reader.ReadByte() == 'R' && reader.ReadByte() == 'I' && reader.ReadByte() == 'F' && reader.ReadByte() == 'F') { // Read the header of the RIFF chunk reader.ReadBytes(4); // Chunk length reader.ReadBytes(4); // WAVE identifier. // Local variabl's used to load chunks. int audioFormat, channelCount; int sampleRate, byteRate; int blockAlign, bitsPerSample; int chunkIndex = 0; bool foundFormatChunk = false; bool foundDataChunk = false; long dataChunkPosition = 0; long dataChunkLength = 0; Sample sample = null; // Read in every chunk till we find the end of file chunk or // we read past the end of the stream. while (true) { // Check we are not at the end of the stream. if (stream.Position >= stream.Length) break; // Read in chunk's data byte[] chunkTypeChars = reader.ReadBytes(4); int chunkLength = reader.ReadInt32(); string chunkType = ((char)chunkTypeChars[0]).ToString() + ((char)chunkTypeChars[1]).ToString() + ((char)chunkTypeChars[2]).ToString() + ((char)chunkTypeChars[3]).ToString(); long originalPosition = stream.Position; switch (chunkType) { // Format chunk. case "fmt ": // Check that the format chunk is first. if (chunkIndex != 0) throw new Exception("Found out of sequence format chunk while reading wav file."); // Check we haven't found multiple occurances. if (foundFormatChunk == true) throw new Exception("Found multiple format chunks while reading wav file, only singular chunks are valid."); foundFormatChunk = false; // Read in the format specifications. audioFormat = reader.ReadInt16(); channelCount = reader.ReadInt16(); sampleRate = reader.ReadInt32(); byteRate = reader.ReadInt32(); blockAlign = reader.ReadInt16(); bitsPerSample = reader.ReadInt16(); // Make sure audio format is PCM as other mode // are more or less obselete and unsupported by this loader. if (audioFormat != 1) throw new Exception("Encountered unsupported audio format while reading wav file."); // Check for extra parameter if format is PCM. if (stream.Position - originalPosition < chunkLength) { int extraParamSize = reader.ReadInt16(); // Lets just skip the extra parameters for // now as we don't need them. stream.Position += extraParamSize; } // Create the audio sample to write data to, based // on the number of channels and amount of bits used // to store the audio sample. if (bitsPerSample == 8 && channelCount == 1) sample = new Sample(SampleFormat.MONO8,0); if (bitsPerSample == 16 && channelCount == 1) sample = new Sample(SampleFormat.MONO16LE,0); if (bitsPerSample == 8 && channelCount == 2) sample = new Sample(SampleFormat.STEREO8,0); if (bitsPerSample == 16 && channelCount == 2) sample = new Sample(SampleFormat.STEREO16LE,0); // Write the format description into the sample. sample.BitsPerSample = bitsPerSample; sample.ChannelCount = channelCount; sample.SampleRate = sampleRate; sample.ByteRate = byteRate; sample.BlockAlign = blockAlign; break; // Audio data chunk. case "data": // Check we haven't found multiple occurances. if (foundDataChunk == true) throw new Exception("Found multiple data chunks while reading wav file, only singular chunks are valid."); foundDataChunk = false; // Read all the audio data into the sample. dataChunkPosition = stream.Position; dataChunkLength = chunkLength; if (streamed == false) sample.Data = reader.ReadBytes(chunkLength); else stream.Position += chunkLength; break; default: // Probably a good idea to throw up an error // here but for the moment lets just skip it :). stream.Position += chunkLength; break; } chunkIndex++; } // If its a streamed sound then link it up the the stream loader. if (streamed == true) { sample.Streamed = true; sample.DataStream = stream; sample.DataChunkPosition = dataChunkPosition; sample.DataChunkLength = dataChunkLength; sample.StreamedDataRequired += RequestStreamedData; } else stream.Close(); return sample; } else // Header check { reader.Close(); return null; } }
/// <summary> /// Creates and returns a new sample buffer compatible with the current /// audio driver filled with the audio contents of the given sample. /// </summary> /// <param name="sample">The sample that should be used to create the sound buffer from.</param> /// <param name="flags">Describes how this sample buffer should act.</param> /// <returns>A newly created ISampleBuffer instance.</returns> public static ISampleBuffer CreateSampleBuffer(Sample sample, SoundFlags flags) { return _driver.CreateSampleBuffer(sample, flags); }
/// <summary> /// This method is called when Sample save is requested, if it returns true /// the calling method will stop illiterating through the SampleFactorys and /// return success to the user. /// </summary> /// <param name="path">File path or object of the image to load.</param> /// <param name="sample">Sample to save.</param> /// <param name="flags">Bitmask of flags defining how the sample should be saved.</param> /// <returns>True if the save was successfull else false.</returns> protected abstract bool RequestSave(object path, Sample sample, SampleSaveFlags flags);
public static void SaveSample(object path, Sample sample) { SaveSample(path, sample, 0); }
/// <summary> /// This method is called when a Sample save is requested, it illiterates through all /// the registered SampleFactory instances to see if there is one capable of saving the /// given format. /// </summary> /// <param name="sample">Sample to be saved.</param> /// <param name="path">File path or save sample to.</param> /// <param name="flags">Bitmask of flags to define how the sample should be saved.</param> /// <returns>True if save was successfull else false.</returns> public static bool SaveSample(object path, Sample sample, SampleSaveFlags flags) { foreach (SampleFactory factory in _loaderList) { if (factory.RequestSave(path, sample, flags) == true) return true; } return false; }
/// <summary> /// This method is called when a sample load is requested, it illiterates through all /// the registered SampleFactory instances to see if there is one capable to loading the /// given format. /// </summary> /// <param name="path">File path or object of the sound to load.</param> /// <param name="streamed">If set to true the sound will be loaded in piece by piece rather than all at once.</param> /// <returns>A Sample or NULL if it can't find a factory able to load the given audio sample format.</returns> public static Sample LoadSample(object path, bool streamed) { string fullPath = path.ToString(); if (fullPath.ToString().ToLower().StartsWith(Environment.CurrentDirectory.ToLower()) == false) fullPath = Environment.CurrentDirectory + "\\" + fullPath; Sample sample; foreach (SampleFactory factory in _loaderList) { sample = factory.RequestLoad(path, streamed); if (sample != null) { sample.URL = fullPath; return sample; } } // Can't load it natively? Use MCI. sample = new Sample(SampleFormat.STEREO16BE, 0); sample.URL = fullPath; sample.PlayedUsingMCI = true; return sample; }