Parte S

Parte S

por Allan Felipe Pereira de Brito -
Número de respostas: 28

Como o exemplo compilado não está funcionando no meu pc, será que é possível enviar aqui algum arquivo qualquer de exemplo naquele formato de entrada de texto, de alguns segundos de áudio, para eu poder iniciar uns testes mais robustos?

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Marcelo Queiroz -

Coloquei em http://www.ime.usp.br/~mqz/cm/ep3/ os vários arquivos de áudio e análise (.pv) que funcionam com os executáveis de exemplo.

Acho que alguns dos problemas encontrados em fazer o pvanalyzer~ funcionar é que ele não entende todo e qualquer arquivo wav, mas apenas arquivos "bem comportados", que contenham um cabeçalho correto e que usem codificação PCM para as amostras. O arquivo voice.wav que vem com o Pd, por exemplo, não funciona! Uma dica para quem quiser pegar arquivos de áudio quaisquer (wav ou em outro formato) e deixá-lo "bem comportado" para o pvanalyzer~ entender, basta instalar o SoX (http://sox.sourceforge.net/) e rodar

sox meuarquivo.extensão meuarquivo.au

sox meuarquivo.au arquivolegal.wav

Este último (arquivolegal.wav) deve ser legível pelo pvanalyzer~. Esta receita funcionou com o voice.wav.

Em resposta à Marcelo Queiroz

Re: Parte S

por Allan Felipe Pereira de Brito -

Realmente, eu estava utilizando o .wav que vem com o Pd e não rodava, agora este da página está gerando o .pv certinho, obrigado!

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Allan Felipe Pereira de Brito -

Caso alguém esteja apanhando um pouco da parte S, começo alguma troca de experiências.

Eu consigo ler arquivos pequenos que fiz, com poucos osciladores e manipular na mão as frequências e as amplitudes. Algumas coisas bobas estão me impedindo de avançar. Existe alguma razão lógica para o seguinte código funcionar:

*out = result[0] + result[1];

sendo result[i] cada onda ( amplitude vezes um x->SENO[(int)(ind[i]-((int)ind[i]/x->S)*x->S)] )

e o seguinte código não funcionar (o som sai, mas não é limpo e fiel a senos, é um pouco "chiado"):

*out = final;

sendo final a mesma soma só que dentro de um laço, que vai calculando os result[i] e somando final += result[i]

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Marcelo Queiroz -

Allan,

tem várias coisas que poderiam estar gerando este ruído, mas nenhuma nas linhas que você indicou. Algumas ideias:

1) Você lembrou de reinicializar final=0 a cada nova amostra? Sei que é meio besta, mas às vezes a gente esquece...

2) os tipos de result[i] e final são exatamente os mesmos? É que o Pd usa aqueles tipos especializados (t_float, t_sample,t_int), e usar os tipos originais da linguagem pode gerar alguma conversão implícita incorreta em algum contexto. No meu código a soma é feita na própria variável *out, e os termos somados são todos do tipo t_float.

3) Os ind[i] estão avançando de acordo com que fórmula? Você mandou imprimir os valores de frequência lidos e os valores de Delta[i] calculados pra checar se os dados estão consistentes? Imagino que sim, pois pelo que entendi a primeira versão do seu código está funcionando.

Vamos tentando...

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Paulo Cheadi Haddad Filho -

Oi!

Eu tenho umas dúvidas:

1) "Fica faltando considerar a soma de K osciladores [1], o tratamento das amplitudes [2] e o tratamento dos parâmetros α e β [3]."

Q eu percebi:

[1] cálculo do y(t):  y(t) = \sum_{k=1}^{K} a_{k}...

[2] trocando para linguagem de computação: a_{1} [n] = (1 - \lambda( n) ) . a_{1} [ j . \delta_{t}] +  \lambda( n) . a_{1} [ (j+1) . \delta_{t}]
[3] da seção 5: "ao receber uma atualização de α ou β, o inlet correspondente deverá armazenar este valor numa variável auxiliar; ao terminar o processamento de um segmento temporal, o código se encarregará de transferir os valores pendentes de α e β para as variáveis que serão usadas de fato na geração do sinal no próximo segmento."
Então eu devo guardar 1 único valor novo de α ou β para usar na execução do próximo oscilador a ser executado, senão mantenho os mesmos valores.
É isso?
2) De onde vem o parâmetro K da quantidade de osciladores?
3) Na equação do y(t) na seção 3 tem um tal de osc(). Ele é a leitura da tabela SENO na posição \beta f_{i} (\alpha t) ?
Obrigado!
Em resposta à Paulo Cheadi Haddad Filho

Re: Parte S

por Allan Felipe Pereira de Brito -

2) Pelo que entendi o número de osciladores k é uma função de N, no caso (N/2-1), o que pode ser visto pela explicação na segunda página do EP (os componentes são 0, f_0, ... , N/2 f_0 ; mas excluí-se o 0 e o N/2 f_0 )

 

Bem, estou agora no problema da interpolação linear, tentando bolar um jeito de guardar as amplitudes do futuro, sem estragar a ordem de leitura.

 

 

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Paulo Cheadi Haddad Filho -

Do 2), ainda não vejo quais dos atributos da struct fileosc me fornecem o N pra achar o K.

Do seu problema, não é só calcular o vetor y todo e depois varrer ele fazendo a interpolação, guardando num outro vetor?

Em resposta à Paulo Cheadi Haddad Filho

Re: Parte S

por Allan Felipe Pereira de Brito -

O N está na primeira linha do arquivo a ser lido, tanto é que você nota que as linhas seguintes têm (N/2-1) amplitudes e frequências, então eu dei um fscanf nele antes de mais nada e depois aloquei o tamanho necessário para os vetores.

É, eu to nessa de tentar encaixar um vetor auxiliar aqui, acho que isto sai.

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Paulo Cheadi Haddad Filho -

O N está na primeira linha do arquivo a ser lido, tanto é que você nota que as linhas seguintes têm (N/2-1) amplitudes e frequências, então eu dei um fscanf nele antes de mais nada e depois aloquei o tamanho necessário para os vetores.

Qual arquivo? O fileosc~-entrada.txt que não é, pelo menos no que tá no compactado de arquivos auxiliares.

E o enunciado tb não diz isso, vide: "Você pode construir o seu external a partir do template de exemplo chamado fileosc~. Este objeto lê um arquivo com uma lista de frequências e durações (em número de amostras)". Geralmente quando o arquivo de entrada tem essas linhas adicionais é citado.

Valeu!

Em resposta à Paulo Cheadi Haddad Filho

Re: Parte S

por Allan Felipe Pereira de Brito -

Oi, Paulo,

Este arquivo de exemplo é mais simples e como diz, é apenas para servir de template. Na verdade existe a instrução do arquivo .txt a ser gerado conter o N e o M na primeira linha sim. Está na seção 4: "output arquivo.txt (...) este arquivo conterá os valores N e M na primeira linha, e depois ...". O arquivo dos exemplos compilados sai neste formato recomendado.

Em resposta à Paulo Cheadi Haddad Filho

Re: Parte S

por Andre Jucovsky Bianchi -

Sobre (1):

Digamos que você utilize variáveis alpha e beta para armazenar os valores da síntese de um certo segmento temporal.

A qualquer momento, valores novos para α e β que chegam pelos inlets devem ser armazenados em variáveis temporárias alphatmp e betatmp. Podem chegar diversos valores diferentes durante o processamento de um certo segmento, que vão sempre atualizando os valores dessas variáveis temporárias.

No final do processamento de um segmento temporal, você transfere os valores de alphatmp e betatmp para as variáveis alpha e beta que serão utilizadas no processamento do próximo segmento.

Sobre (2):

K = N/2 - 1 como explicou o Allan acima.

Sobre (3):

osc: R → [-1,1] seria, idealmente, a função que todos os K osciladores utilizariam para gerar cada sinal (um seno ou um coseno resolveriam bem). Computacionalmente, não dispomos desta função, mas sim de uma tabela que podemos utilizar para aproximar os resultados dessa função, através de leitura circular e interpolação cúbica. Então, a aproximação de osc(x) é dada pela leitura, a uma velocidade constante em cada intervalo de processamento, da tabela interpolada.

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Allan Felipe Pereira de Brito -

Eu consigo mexer nos parâmetros alfa e beta dentro do código e ouvir a diferença do pitch e velocidade, mas estou apanhando um pouco pra fazer a interface dos 2 inlets com o usuário. Alguém já conseguiu isto?

Imaginei fazer um static t_float fileosc_float(t_fileosc *x, t_float f , que retorna o próprio f, para usar depois dentro do _perform,  mas aí como há o reconhecimento dos 2 floats diferentes? E como ficaria a mudança do _new e do _setup

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Henrique Stagni -

Para o alfa e beta eu to fazendo algo parecido com o HOWTO

-Dentro da estrutura tem que ter um t_float alpha; e um t_float beta;

-No _new: as linhas floatinlet_new(&this->x_obj,  &this->alpha) e floatinlet_new(&this->x_obj,  &this->beta)

Em resposta à Henrique Stagni

Re: Parte S

por Flávio Luiz Schiavoni -

floatinlet_new(&this->x_obj,  &this->alpha)

Isto garante um inlet de entrada passiva, ou seja, não irá chamar nenhum método. O valor deste inlet será passado diretamente para a variável &this->alpha.

Em resposta à Flávio Luiz Schiavoni

Re: Parte S

por Flávio Luiz Schiavoni -

Só para constar, a maneira de construir um inlet ativo, ou seja, que não recebe seu valor diretamente em uma variável mas em um método, é a seguinte.

Defina, no "setup", um novo método:

class_addmethod(vovocodersynth2_class, (t_method)vovocodersynth2_alfa,  gensym("alfa"), A_DEFFLOAT,0);

No "new" crie o inlet e associe-o a assinatura deste método, ou seja:

inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("alfa"));

Agora crie um método com a assinatura desejada

void vovocodersynth2_alfa(t_vovocodersynth2 *x, t_floatarg f){
post("alfa %f",f);
}

Voilá!

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Paulo Cheadi Haddad Filho -

Caiu uma ficha aqui:

pq calcular mod usando (q nem no arquivo exemplo)

(int) (ind - ((int) ind / x->S) * x->S)

ao invés de

ind%x->S

 

Dei uma procurada e achei:

http://embeddedgurus.com/stack-overflow/2011/02/efficient-c-tip-13-use-the-modulus-operator-with-caution/

Interessante! Nunca ia imaginar, achava q o compilador fizesse essa otimização...

Em resposta à Paulo Cheadi Haddad Filho

Re: Parte S

por Marcelo Queiroz -

Paulo,

na verdade a expressão não tem absolutamente nada a ver com a otimização de código que você encontrou, e que eu nunca tinha visto (mas é interessante!).

A questão é que ind é um índice fracionário, e a operação ind%x->S nem mesmo está definida. A expressão sugerida no arquivo de exemplo faz a conta (ind - ((int) ind / x->S) * x->S, correspondente a achar o módulo x->S incluindo o resíduo fracionário, e depois trunca esse valor para fazer a leitura da tabela. Por causa desse último passo, a mesma conta poderia ter sido escrita como ((int)ind)%x->S.

Já no EP, a gente tem que fazer a leitura com interpolação de 4 pontos, então o módulo tem que ser calculado junto com o resíduo fracionário. A diferença entre as duas formas de acesso está no fato de que agora esse resíduo será usado como parâmetro na fórmula envolvendo SENO[i-1], SENO[i], SENO[i+1] e SENO[i+2], onde i=(int) (ind - ((int) ind / x->S) * x->S).

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por Paulo Cheadi Haddad Filho -

Duas dúvidas:

1) o que se quer dizer com "amostras" no trecho "vamos considerar a produção de L = N/(alfa M) amostras"? Seria a quantidade de pares de linhas do arquivo de entrada?

2) esqueci de reperguntar: a linha da definição dos a's deve ser interpretada como

a1[n] = (1-lambda[n]) * a1[delta[j]] +  lambda[n]*a1[delta[j+1]] ? O modo como 'j' está escrito é meio ambíguo...

 

Obrigado!

Em resposta à Paulo Cheadi Haddad Filho

Re: Parte S

por Andre Jucovsky Bianchi -

Sobre (1):

Na parte de análise, são obtidos K = ((N/2) - 1) pares de amplitude e frequência para cada bloco de tamanho N analisado, que corresponde a um período de N/R segundos. Com fator de sobreposição igual a M, a diferença entre o início de dois blocos adjacentes é de N/M amostras, que corresponde um período de áudio de N/(MR) segundos. Isso significa que a geração dos conjuntos de K pares é feita com uma resolução temporal maior do que o tamanho da janela.

Se A é o número total de amostras de áudio, serão analisados um total de (AM)/N blocos de tamanho N. Para cada bloco analisado será gerado um conjunto de K pares de amplitude e frequência.

Assim, o arquivo gerado pela análise deve ter (((AM)/N * 2) + 2) linhas, contando com as duas primeiras linhas que contém os valores de N e M. Cada linha do arquivo (exceto as duas primeiras) deve conter K valores. Supondo que N e M são impressos em linhas diferentes, as linhas ímpares do arquivo (a partir da linha 3) deverão conter K valores de amplitude e as linhas pares (a partir da linha 4) deverão conter K valores de frequência.

Na parte de Síntese a idéia é utilizar os dados de amplitude e frequência obtidos pela Análise para ressintetizar o som aplicando alterações de altura e velocidade. Ao alterar a velocidade através de um fator α, o período correspondente a cada conjunto de K pares de amplitude e frequência passa a ser de N/(αMR) segundos. Em outras palavras, a resolução temporal de análise fornece parâmetros para K osciladores que deverão ser utilizados para sintetizar o novo sinal por N/(αMR) segundos. Nesse período de tempo, a uma taxa de amostragem igual a R Hz, cabem L = N/(αM) amostras.

Sobre (2):

No enunciado, ak(t) corresponde à amplitude do k-ésimo oscilador em função do tempo. Como a análise é feita em blocos, a cada N/M amostras, os valores de ak(t) correspondem a valores fixos para cada bloco analisado, ou seja, ak(t) está definido apenas para t = (jN)/(MR), com j = 1, 2, 3, ..., (AM)/N. Ou seja, a amplitude de cada oscilador está definida para todo um período de tempo, e não individualmente para cada amostras de um bloco.

A idéia é, então, realizar a transição entre dois valores de amplitude ao longo de todo um bloco, utilizando interpolação linear. A amplitude do k-ésimo oscilador deve então ser igual a exatamente ak(jΔt) para a primeira amostra do bloco e quase igual a ak((j+1)Δt) para a última, recebendo um valor intermediário em todas as amostras do meio do caminho, que varia linearmente com respeito à posição daquela amostra no bloco gerado.

Supondo então a geração de L = N/(αM) amostras em um bloco, a n-ésima amostra do bloco deve ter um valor de amplitude dado pela soma dos K osciladores, cada qual contribuindo com uma amplitude ponderada em relação à distância da posição da amostra sendo gerada dentro do bloco. A amplitude do k-ésimo oscilador para a n-ésima amostra do bloco j é, então, dada pela fórmula que está no enunciado (generalizada aqui):

ak(n) = (1 - λ(n))ak(jΔt) + λ(n)ak((j+1)Δt).

Certo?

Em resposta à Andre Jucovsky Bianchi

Re: Parte S

por Paulo Cheadi Haddad Filho -

2) É, não consegui sair do problema... =/

Tá difícil pra mim, perceber onde entra e como escrever isso.

Pq a gente tem uma lista de amplitudes, com índices inteiros, então j*delta é um índice desse vetor. Mas não é isso, e não consigo pensar em outra coisa...

Em resposta à Andre Jucovsky Bianchi

Re: Parte S

por Flávio Luiz Schiavoni -

A interpolação das amplitudes ainda me parece algo um tanto confuso. A análise de um determinado áudio deu para o primeiro oscilador no primeiro bloco (N=1024, M=1) a frequência 44.2801 com amplitude 0.022941. Para o mesmo oscilador, no segundo bloco temos a frequência 51.2728 com amplitude 0.0213965.

Isto significa que teríamos que interpolar linearmente do valor da amplitude do primeiro bloco (0.022941) para a amplitude do segundo bloco (0.0213965) para definir a amplitude pontual nas 1024 amostras do primeiro bloco? Isto me parece estranho pois trata-se de 2 frequências diferentes para o mesmo oscilador. A amplitude da frequência do segundo bloco irá influenciar na amplitude da frequência do primeiro bloco, mesmo sendo frequências distintas? É isto mesmo?

Talvez isto justifique alguns "picos" absurdos que minha síntese estava gerando...

Em resposta à Flávio Luiz Schiavoni

Re: Parte S

por Marcelo Queiroz -

Sim, a interpolação define valores intermediários para cada uma das amostras de cada bloco.

O problema com variações instantâneas de amplitude é que elas necessariamente geram descontinuidades que são percebidas como "clicks". A interpolação dos valores de amplitude é uma maneira simples e direta de resolver este problema. É verdade que a amplitude do segundo bloco irá sempre influenciar a amplitude do bloco atual, seja no caso de frequências iguais ou diferentes, e isso é como uma "antecipação" do próximo evento. Para blocos de tamanho pequeno (<= 100ms) e sinais +/- estacionários (notas, acordes) esse efeito não deve ser muito perceptível.

Um jeito de diminuir este efeito e ainda cuidar para que não haja descontinuidades é restringir a interpolação dos valores de amplitude a uma fração do bloco (por exemplo, nos 10% finais), ou mesmo a uma quantidade pré-fixada de amostras (por exemplo, 100 amostras). Mas nenhuma dessas soluções é perfeita, pois todas elas têm algum impacto no sinal. Se os blocos são pequenos para serem percebidos individualmente, este impacto provavelmente será percebido como uma alteração de timbre. E nem adianta buscar uma "solução perfeita", pois o processo de amostrar a amplitude de cada oscilador uma única vez a cada bloco corresponde a uma codificação com perdas.

Em resposta à Allan Felipe Pereira de Brito

Re: Parte S

por William Gnann -

Eu sei que é uma dúvida meio boba, mas por algum motivo meu beta não mexe com a frequência propriamente dita. Ele aparenta mexer com a envoltória e não com a frequência.

Não faço ideia de onde o erro possa estar. O beta só entra no cálculo dos deltas, né?

Em resposta à William Gnann

Re: Parte S

por Marcelo Queiroz -
Oi, William! De fato o beta só entra neste cálculo dos deltas, que são as velocidades de varredura da tabela seno, e portanto definem a frequência. Especificamente o único lugar onde o beta é usado é no início de cada evento, onde você recupera o último valor de beta que entrou pelo inlet e define os Deltas que valerão durante o próximo evento. É estranho que você observe algo nas envoltórias...