Pergunta

Estou escrevendo um editor de som para minha graduação. estou a usar GRAVES Para extrair amostras de arquivos MP3, WAV, OGG etc e adicionar efeitos DSP como Echo, Flanger etc. Simplesmente, eu fiz minha estrutura que aplique um efeito de Position1 a Position2, Cut/Polar Management.

Agora, meu problema é que quero criar um controle semelhante com este de Cool Edit Pro que desenham uma representação de forma de onda da música e tem a capacidade de ampliar/sair partes selecionadas da forma de onda etc. Após uma seleção, posso fazer algo como:

TInterval EditZone = WaveForm->GetSelection();

Onde Tinterval tem este formulário:

struct TInterval
{
    long Start;
    long End;
}

Eu sou iniciante quando se trata de desenho sofisticado, portanto, qualquer dica sobre como criar uma representação de uma música de onda de uma música, usando dados de amostra retornados pelo baixo, com a capacidade de aumentar/diminuir o zoom.

Estou escrevendo meu projeto em C ++, mas posso entender o código C#, Delphi, por isso, se você quiser, pode postar trechos nos últimos dois idiomas :)

Thanx DropTix

Foi útil?

Solução

Por zoom, presumo que você quer dizer zoom horizontal em vez de vertical. A maneira como os editores de áudio fazem isso é escanear a Wavform que a quebra nas janelas do tempo, onde cada pixel em x representa algum número de amostras. Pode ser um número fracionário, mas você pode se safar com as relações fracionárias de zoom fracionário sem irritar muito o usuário. Depois que você diminui um pouco, o valor máximo é sempre um número inteiro positivo e o valor mínimo é sempre um número inteiro negativo.

Para cada pixel na tela, você precisa saber o valor mínimo da amostra para esse pixel e o valor máximo da amostra. Portanto, você precisa de uma função que digitalize os dados da forma de onda em pedaços e acompanha o máximo e o mínimo acumulados para esse pedaço.

Este é um processo lento, portanto, os editores profissionais de áudio mantêm uma tabela pré-calculada de valores mínimos e máximos em alguma razão de zoom fixa. Pode estar em 512/1 ou 1024/1. Quando você está desenhando com uma ração de zoom de> 1024 amostras/pixels, usa a tabela pré-calculada. Se você estiver abaixo dessa relação, obtém os dados diretamente do arquivo. Se você não fizer isso, descobrirá que o código de desenho fica muito lento quando você diminui o zoom.

Vale a pena escrever código que lida com todos os canais do arquivo em um único passe ao fazer essa digitalização, a lentidão aqui fará com que todo o seu programa pareça lento, é o disco que importa aqui, a CPU não tem problemas para acompanhar, então O código C ++ direto é bom para a criação das tabelas Min/Max, mas você não deseja passar pelo arquivo mais de uma vez e deseja fazê -lo sequencialmente.

Depois de ter as mesas Min/Max, mantenha -as por perto. Você deseja voltar ao disco o mínimo possível e muitos dos motivos para querer repintar sua janela não exigirão que você rescante suas tabelas Min/Max. O custo de memória de manter -lhes não é tão alto em comparação com o custo de IO do disco de construí -los em primeiro lugar.

Em seguida, você desenha a forma de onda desenhando uma série de linhas verticais de 1 pixels entre o valor máximo e o valor mínimo para o tempo representado por esse pixel. Isso deve ser bastante rápido se você estiver desenhando de tabelas Min/Max pré.

Outras dicas

Eu já fiz isso recentemente. Como Marius sugere que você precisa descobrir quantas amostras estão em cada coluna de pixels. Em seguida, você trabalha no mínimo e no máximo e, em seguida, plote uma linha vertical do máximo ao mínimo.

Como primeiro passe, isso aparentemente funciona bem. O problema que você terá é que, ao aumentar o zoom, começará a demorar muito para recuperar as amostras do disco. Como solução para isso, criei um arquivo "pico" ao lado do arquivo de áudio. O arquivo de pico armazena os pares mínimos/máximos para grupos de n amostras. Brincar com n até você obter a quantidade certa de acordo com você. Pessoalmente, achei 128 amostras uma boa troca entre tamanho e velocidade. Também vale a pena lembrar que, a menos que você esteja desenhando um controle maior que 65536 pixels de tamanho, você não precisa armazenar essa informação de pico como algo mais que valores de 16 bits que economizam um pouco de espaço.

Você não planejaria os pontos da amostra em uma tela de 2? Você deve saber quantas amostras existem por segundo para um arquivo (leia -o no cabeçalho) e, em seguida, plote o valor no eixo y. Como você deseja aumentar e diminuir o zoom, você precisa controlar o número de amostras por pixel (o nível de zoom). Em seguida, você pega a média desses pontos de amostra por pixel (por exemplo, pegue a média de cada 5 pontos se tiver 5 amostras por pixel. Em seguida, você pode usar uma API de desenho 2D para desenhar linhas entre os pontos.

Usando o pacote de Naudio de código aberto -

public class WavReader2
{
    private readonly WaveFileReader _objStream;

    public WavReader2(String sPath)
    {
        _objStream = new WaveFileReader(sPath);
    }

    public List<SampleRangeValue> GetPixelGraph(int iSamplesPerPixel)
    {
        List<SampleRangeValue> colOutputValues = new List<SampleRangeValue>();

        if (_objStream != null)
        {
            _objStream.Position = 0;
            int iBytesPerSample = (_objStream.WaveFormat.BitsPerSample / 8) * _objStream.WaveFormat.Channels;
            int iNumPixels = (int)Math.Ceiling(_objStream.SampleCount/(double)iSamplesPerPixel);

            byte[] aryWaveData = new byte[iSamplesPerPixel * iBytesPerSample];
            _objStream.Position = 0; // startPosition + (e.ClipRectangle.Left * iBytesPerSample * iSamplesPerPixel);

            for (float iPixelNum = 0; iPixelNum < iNumPixels; iPixelNum += 1)
            {
                short iCurrentLowValue = 0;
                short iCurrentHighValue = 0;
                int iBytesRead = _objStream.Read(aryWaveData, 0, iSamplesPerPixel * iBytesPerSample);
                if (iBytesRead == 0)
                    break;

                List<short> colValues = new List<short>();
                for (int n = 0; n < iBytesRead; n += 2)
                {
                    short iSampleValue = BitConverter.ToInt16(aryWaveData, n);
                    colValues.Add(iSampleValue);
                }

                float fLowPercent =  (float)((float)colValues.Min() /ushort.MaxValue);
                float fHighPercent = (float)((float)colValues.Max() / ushort.MaxValue);

                colOutputValues.Add(new SampleRangeValue(fHighPercent, fLowPercent));
            }
        }

        return colOutputValues;
    }
}

public struct SampleRangeValue
{
    public float HighPercent;
    public float LowPercent;
    public SampleRangeValue(float fHigh, float fLow)
    {
        HighPercent = fHigh;
        LowPercent = fLow;
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top