Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Разбиение звукового файла на несколько частей


Автор: agealex 2.6.2009, 10:29
Проект на С# 3.5 на мобильной платформе.

Есть:
1. файл со звуком. в котором содержатся все звуки проекта 
2. Файл с разбиением в байтах - с какого байта начинается какой участок звука

Нужно:
Проигрывать звук с определенного байта до определенного байта.
Как это сделать? smile 

Автор: agealex 3.6.2009, 02:29
Пробывал различные варианты но при всех, если использовать SoundPlayer вылетает ошибка при вызове Play();:dash1 
вот один из вариантов: 
Код

               FileStream fs = File.Open("@\\rus.wav", FileMode.Open);
               byte[] soundData = new Byte[fs.Length];
               int count = fs.Read(soundData, 0, (int)fs.Length);   
               Stream fs2 = new MemoryStream();
               fs2.Write(soundData, 0, soundData.Length);
               fs2.Position = 0;
               System.Media.SoundPlayer player2 = new System.Media.SoundPlayer(fs2);
               SoundPlayer player = new SoundPlayer(fs);
               player.Load();
               player.Play(); // ругается всегда в этом месте , к сожалению еще не успел узнать на что ругается требует "NETCFv35.Messages.EN.wm.cab", еще не установил


В общем проблема в том что он не может проиграть имеющийся буфер прочитанный из wav файла, буду рад любым вариантам

Автор: Экскалупатор 3.6.2009, 07:31
using (FileStream fs = new FileStream(@"C:\WINDOWS\Media\tada.wav", FileMode.Open))
            {
                byte[] b = new byte[fs.Length];
                fs.Read(b, 0, 30000);
                MemoryStream ms = new MemoryStream(b);
                System.Media.SoundPlayer sound = new System.Media.SoundPlayer(ms);
                sound.Play();
            }

Автор: Экскалупатор 3.6.2009, 09:04
хотя это немного не то, при попытке изменить начало позиции для чтения выдает ошибку: The wave header is corrupt. почему не знаю. может кто нить знает? тоже интересует этот вопрос...

Автор: Partizan 3.6.2009, 11:16
Экскалупатор, потому что в начале wav- файла должен быть соответствующий формату заголовок...

Автор: Экскалупатор 3.6.2009, 11:26
Partizan, это я уже понял.
вопрос как все таки сделать что бы можно было проигрывать из любого места. перезаписывать файл? это не выход... нужно наверное как то извлечь заголовок и запихать его в свой поток, но как?

Автор: agealex 3.6.2009, 11:43
Да собственно оказалось просто - считываешь заголовок из файла, затем и пишешь его в нужный тебе буфер, затем дозаписываешь при надобности в него нужные тебе данные и меняешь поле отвечающее за размер файла.

Автор: agealex 3.6.2009, 12:00
а как сделать плавный переход от одного буфера к другому что бы непрерывно играл? Если я использую 2 буфера?

Автор: Экскалупатор 3.6.2009, 12:54
agealex, можно посмотреть что получилось(выложите код, плиз)?

 smile 

Автор: agealex 3.6.2009, 16:35
Там считываешь с начала массива  40 байт - это твой заголовок (весь заголовок занимает 44 байта учти при поправке на считывание,последние  4 байта длинна тела) - сохраняешь его в массив, затем при считывание буффера с нужного места дописываешь к заголовку сначала 4 байта длины будующего тела, а уж потом и само тело - и проигрываешь его, код вечером выложу.

Автор: agealex 4.6.2009, 00:44
В коде это выглядит вот так тут кусок класса аудио, осталось разобраться только с последывательным пройгрышем массивов, да всю эту штуку надо запускать либо в потоке либо самое простое  - в таймере

Если где предожите оптимизировать милости прошу


Код

 class Audio
    {
        #region declaretion

        private const byte WavHeaderSize = 40; //Размер заголовка Wav файла без размера тела (4 байта еще)
        private const byte WavHeaderBodyLenghSize = 4;// Размер тела WAV файла

        private const String WavFileName = "\\rus.wav"; // Название фалйла и путь к нему

        private byte CurrentBuffId = 1;
        public byte[] SoundBuffFirst;
        public byte[] SoundBuffSecond;

        public int BuffSize = 60000; //размер буфера
        public SoundPlayer Player;

        private FileStream FileStreamWav;
        private Stream SoundStream;

        #endregion

        public Audio()
        {
            //Буфера wav фала
            SoundBuffFirst = new byte[BuffSize]; 
            SoundBuffSecond = new byte[BuffSize];

            FileStreamWav = File.Open(WavFileName, FileMode.Open);
            FileStreamWav.Read(SoundBuffFirst, 0, WavHeaderSize + 4);
            FileStreamWav.Read(SoundBuffSecond, 0, WavHeaderSize);
            
            SoundStream = new MemoryStream();
            Player = new SoundPlayer();
          
        }

        //Воспроизводит WAV файлы
        public void PlaySound(int StartPosition, int StopPosition)
        {
            int PlayingSoundSize = 0; // Размер тела wav файла
            byte[] DataLenArray;
            if ((StopPosition - StartPosition) <= (BuffSize - WavHeaderSize - WavHeaderBodyLenghSize))
            {
                PlayingSoundSize = StopPosition - StartPosition;

                DataLenArray = BitConverter.GetBytes(PlayingSoundSize);
                DataLenArray.CopyTo(SoundBuffFirst, WavHeaderSize);
                DataLenArray = BitConverter.GetBytes(PlayingSoundSize + WavHeaderSize - WavHeaderBodyLenghSize);
                DataLenArray.CopyTo(SoundBuffFirst, WavHeaderBodyLenghSize);
               
                FileStreamWav.Position = StartPosition;
                FileStreamWav.Read(SoundBuffFirst, WavHeaderSize + WavHeaderBodyLenghSize, PlayingSoundSize);
                SoundStream.Write(SoundBuffFirst, 0, PlayingSoundSize + WavHeaderSize + WavHeaderBodyLenghSize);
                SoundStream.Position = 0;
                
                Player.Stream = SoundStream;
                Player.Stream.Position = 0;
                Player.Load();
                Player.Play();
            }


там оказалось 2 места  в которых длина хранится вот кстати структура кому интересно заголовка wav  файла (он на С++ - ну какой нашел просто так глянуть)
Код

struct head_data
 {
  public:
  char s1[4];             // 'RIFF' - сигнатура - 4 байта
  long len1;              // длина всего оставшегося  - long - 4 байта             -- вот его нужно менять
  char s2[8];             // 'WAVEfmt '  - сигнатура - 8 байт
  long d_size;            // размер блока данных заголовка - обычно 16 - long - 4 байта

  short int format;       // формат данных  - short int - 2 байта
  // 1-PCM, 2-ADPCM, ... , 85-MPEG Layer-3, ... sorry, it's all that i know :-(
  short int channels;     // число каналов (моно-1/стерео-2) - short int - 2 байта
  long discret;           // частота дискретизации - long - 4 байта
  long stream;            // поток, байт/сек, = (частота дискр * алигмент) - long - 4 байта
  short int bl_align;     // алигмент, (байт за 1 отсчет = channels*bit/8) - short int - 2 байта
  short int bitrate;      // число бит при оцифровке (8/16) - short int - 2 байта

  char s3[4];             // 'data' - сигнатура - 4 байта
  long len2;              // длина следующих после этого чистых wave-данных - long - 4 байта        -- и его тоже 
  //Summary 44 байта
 };

Автор: Экскалупатор 4.6.2009, 11:02
agealex, спасибо, интересная инфа, но по сути это почти перезапись файла, т.е. мы вырезаем то что нам надо и пере собираем все в новом потоке, но мне кажется должен быть способ попроще, ведь по сути поток байтов из файла не меняется, просто нужно его запускать из разных мест...

Автор: agealex 4.6.2009, 18:21
Ну в принцепи можно попробывать, закачать один раз буфер целиком в SoundPlayer.Stream и меняя его позицию запускать в разных частях , и соответственно останавливать по достижению определеннойй позиции, так скорее даже будет быстрее, так как одни из существенных недостатков данного кода к сожалению выяснилось, что является временная задержка порядка  1 - 4х секунд . сегодня попробую потом выложу если получится удачнее.

Автор: agealex 4.6.2009, 20:33
Вот конечная версия, более упрощенная чем преведущие, вроде все лишнее из нее убрал, и исчезли проблемы с ожиданием запуска, размерами буфера и необходимость в пинпонге буферами. Если еще больше можно упростить не стесняйтесь пишите.
Код

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Media;
using System.IO;
namespace Audio
{
    class Audio
    {
        #region declaretion

        private const byte WavHeaderSize = 40; //Размер заголовка Wav файла без размера тела (4 байта еще)
        private const byte WavHeaderBodyLenghSize = 4;// Размер тела WAV файла

        private const String WavFileName = "\\rus.wav"; // Название фалйла и путь к нему

        
        public byte[] SoundBuffHeader;
        private byte[] SoundBuffMain;


        public SoundPlayer Player;
        private FileStream FileStreamWav;

        #endregion

        public Audio()
        {
            //Буфера wav фала
            SoundBuffHeader = new byte[WavHeaderSize + WavHeaderBodyLenghSize]; 
            FileStreamWav = File.Open(WavFileName, FileMode.Open);
            SoundBuffMain = new byte[FileStreamWav.Length - WavHeaderSize - WavHeaderBodyLenghSize];
            
            #region BufferRead
            // Считывание всех буфферов
            FileStreamWav.Position = 0;
            FileStreamWav.Read(SoundBuffHeader, 0, WavHeaderSize); // Заголовок кроме последней длины
            FileStreamWav.Position = WavHeaderSize + WavHeaderBodyLenghSize;
            FileStreamWav.Read(SoundBuffMain,0,SoundBuffMain.Length); // Весь файл кроме заголовка

            #endregion
            
            SoundStream = new MemoryStream();
            Player = new SoundPlayer();
            Player.Stream = new MemoryStream();
        }
        
        //Воспроизводит WAV файлы
        public void PlaySound(int StartPosition, int StopPosition)
        {
            int PlayingSoundSize = 0; // Размер тела wav файла
            byte[] DataLenArray;
            if ((StopPosition - StartPosition) <= (BuffSize - WavHeaderSize - WavHeaderBodyLenghSize))
            {
                PlayingSoundSize = StopPosition - StartPosition;
                // Запись в заголовок информации о размере тела файла
                DataLenArray = BitConverter.GetBytes(PlayingSoundSize);
                DataLenArray.CopyTo(SoundBuffHeader, WavHeaderSize);
                DataLenArray = BitConverter.GetBytes(PlayingSoundSize + WavHeaderSize - WavHeaderBodyLenghSize);
                DataLenArray.CopyTo(SoundBuffHeader, WavHeaderBodyLenghSize);
                //Запись заголовка в проигрываемый файл
                Player.Stream.Write(SoundBuffHeader, 0, SoundBuffHeader.Length);
                // Запись самого тела
                Player.Stream.Write(SoundBuffMain, StartPosition, PlayingSoundSize + WavHeaderSize + WavHeaderBodyLenghSize);

                Player.Stream.Position = 0;
                Player.Load();
                Player.Play();
            }
           
        }
    }
    
    
}


Автор: agealex 8.6.2009, 09:41
К сожалению при испытаниях выявилась странная закономерность - в независимости от места начала проигрыша , при повторном запуске воспроизводится начинало с начала.

Вот окончательная доработанная и протестированная версия:

Отличия - весь звуковой файл не записывается целиком в буфер а берутся толь ко нужные куски, как ни странно это исправило проблему неправильного места старта звукового файла, избавило от проблем с работой с большими звуковыми файлами(нехватка памяти)

Да и тут еще появился программный регулятор громкости - для комплекта smile
Код

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Media;
using System.IO;
using System.Runtime.InteropServices;

namespace ShowDialog
{
    class Audio
    {
        #region SoundMixer
        [DllImport("coredll.dll", SetLastError = true)]
        protected static extern int waveOutSetVolume(IntPtr device, uint volume);
        //values for 'volume' are between value between 0 (off) and 65536 (max)
        //if there is only one audio device, you can set 'device' to null (or IntPtr.Zero)
        public enum Volumes : int
        {

            OFF = 0,
            LOW = 858993459,
            NORMAL = 1717986918,
            MEDIUM = -1717986919,
            HIGH = -858993460,
            VERY_HIGH = -1

        }

        [DllImport("coredll.dll", SetLastError = true)]

        internal static extern int waveOutSetVolume(IntPtr device, int volume);

        [DllImport("coredll.dll", SetLastError = true)]

        internal static extern int waveOutGetVolume(IntPtr device, ref int volume);
        public void VolumeLevelUP()
        {
            int v = (int)0;
            int Value = 0;
            waveOutGetVolume(IntPtr.Zero, ref v);
            switch (v)
            {
                case (int)Volumes.OFF:
                    Value = (int)Volumes.LOW;
                    break;
                case (int)Volumes.LOW:
                    Value = (int)Volumes.NORMAL;
                    break;
                case (int)Volumes.NORMAL:
                    Value = (int)Volumes.MEDIUM;
                    break;
                case (int)Volumes.MEDIUM:
                    Value = (int)Volumes.HIGH;
                    break;
                case (int)Volumes.HIGH:
                    Value = (int)Volumes.VERY_HIGH;
                    break;
                case (int)Volumes.VERY_HIGH:
                    Value = (int)Volumes.OFF;
                    break;
                default: Value = (int)Volumes.VERY_HIGH;
                    break;
            }
            waveOutSetVolume(IntPtr.Zero, Value);
        }
        public void VolumeLevelDown()
        {
            int v = (int)0;
            int Value = 0;
            waveOutGetVolume(IntPtr.Zero, ref v);
            switch (v)
            {
                case (int)Volumes.OFF:
                    Value = (int)Volumes.VERY_HIGH;
                    break;
                case (int)Volumes.LOW:
                    Value = (int)Volumes.OFF;
                    break;
                case (int)Volumes.NORMAL:
                    Value = (int)Volumes.LOW;
                    break;
                case (int)Volumes.MEDIUM:
                    Value = (int)Volumes.NORMAL;
                    break;
                case (int)Volumes.HIGH:
                    Value = (int)Volumes.MEDIUM;
                    break;
                case (int)Volumes.VERY_HIGH:
                    Value = (int)Volumes.HIGH;
                    break;
                default: Value = (int)Volumes.OFF;
                    break;
            }
            waveOutSetVolume(IntPtr.Zero, Value);
        }
        
        #endregion


        #region declaretion

        private const byte WavHeaderSize = 40; //Размер заголовка Wav файла без размера тела (4 байта еще)
        private const byte WavHeaderBodyLenghSize = 4;// Размер тела WAV файла

        private const String WavFileName = "\\rus.wav"; // Название фалйла и путь к нему

        public byte[] SoundBuffHeader;
        private byte[] SoundBuffMain;

        public int BuffSize = 600000; //размер буфера
        public SoundPlayer Player;

        private FileStream FileStreamWav;
        private Stream SoundStream;

        #endregion

        public Audio()
        {
            //Буфера wav фала
            SoundBuffHeader = new byte[WavHeaderSize + WavHeaderBodyLenghSize];
            FileStreamWav = File.Open(WavFileName, FileMode.Open);
            SoundBuffMain = new byte[FileStreamWav.Length - WavHeaderSize - WavHeaderBodyLenghSize];

            #region BufferRead
            // Считывание заголовка
            FileStreamWav.Position = 0;
            FileStreamWav.Read(SoundBuffHeader, 0, WavHeaderSize); // Заголовок кроме последней длины
         

            #endregion

            SoundStream = new MemoryStream();
            Player = new SoundPlayer();
            Player.Stream = new MemoryStream();
        }

        //Воспроизводит WAV файлы
        public void PlaySound(int StartPosition, int StopPosition)
        {
            Player = new SoundPlayer();
            Player.Stream = new MemoryStream();
            int PlayingSoundSize = 0; // Размер тела wav файла
            byte[] DataLenArray;
           
            PlayingSoundSize = StopPosition - StartPosition;
            // Запись в заголовок информации о размере тела файла
            DataLenArray = BitConverter.GetBytes(PlayingSoundSize);
            DataLenArray.CopyTo(SoundBuffHeader, WavHeaderSize);
            DataLenArray = BitConverter.GetBytes(PlayingSoundSize + WavHeaderSize - WavHeaderBodyLenghSize);
            DataLenArray.CopyTo(SoundBuffHeader, WavHeaderBodyLenghSize);
            
            //Запись заголовка в проигрываемый файл
            Player.Stream.Position = 0;
            Player.Stream.Write(SoundBuffHeader, 0, SoundBuffHeader.Length);
            // Запись самого тела
            
            byte[] test = new byte[PlayingSoundSize];

            FileStreamWav.Position = StartPosition;
            FileStreamWav.Read(test, 0, PlayingSoundSize); // Весь файл кроме заголовка

            Player.Stream.Write(test, 0, test.Length);

            Player.Stream.Position = 0;
            
            Player.Load();
            Player.Play();
           

        }
        //Остановка плеера
        public void StopPlaying()
        {
            Player.Stop();
        }
    }
}


Автор: agealex 23.6.2009, 00:55
Честное слово замучила совесть  - там не хватает еще функции высвобождения памяти без нее происходит постепенное переполнение памяти

Код

private void MemoryClear()
        {
            try
            {


                Player.Stream.Close();
                Player.Dispose();
                GC.SuppressFinalize(Player);
                GC.Collect();
                long check3 = GC.GetTotalMemory(true);
            }
            catch (Exception v)
            {

            }
        }
 
Вызывать при остановке плеера и перед начало нового воспроизведения

Да  и эта версия имеет ограничения по размеру  отрезка воспроизводимого  - (все свободное меcто / 2)

И данный класс разбиению и пинпонгу буферами не поддается - (Майкрасофт подарило нам секундную задержку перед запуском плеера, и невозможность отследить состояния проигрываемого отрезка).

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)