PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : wav Dateien: Programieren von Wiedergabe und Aufnahme



HaWe
21.05.2016, 11:25
hallo,
ich mach mal hier eine eigenes Topic, leider ist es ja offenbar doch viel komplizierter als ursprünglich gedacht...

Ich suche (auch) für mich eine einfache wav C/C++ API lib.
Eine gute Basis scheint zur Zeit die sehr komplexe ALSA lib als Basis zu bieten, aber als Vereinfachung dazu könnte tinyalsa sein:
https://github.com/tinyalsa/tinyalsa

Installation:


git clone https://github.com/tinyalsa/tinyalsa.git
cd tinyalsa
make

wenn ich damit allerdings das (allerdings immer noch viel zu umfangreiche) Codebeispiel
https://github.com/tinyalsa/tinyalsa/blob/master/tinyplay.c
kompiliere, wird trotzdem eine wichtige lib zum Verlinken nicht gefunden.
Ich arbeite ausschließlich mit Geany.



/* tinyplay.c
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of The Android Open Source Project nor the names of
** its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
*/

#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>

#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164

struct riff_wave_header {
uint32_t riff_id;
uint32_t riff_sz;
uint32_t wave_id;
};

struct chunk_header {
uint32_t id;
uint32_t sz;
};

struct chunk_fmt {
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
};

static int close = 0;

void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
unsigned int rate, unsigned int bits, unsigned int period_size,
unsigned int period_count);

void stream_close(int sig)
{
/* allow the stream to be closed gracefully */
signal(sig, SIG_IGN);
close = 1;
}

int main(int argc, char **argv)
{
FILE *file;
struct riff_wave_header riff_wave_header;
struct chunk_header chunk_header;
struct chunk_fmt chunk_fmt;
unsigned int device = 0;
unsigned int card = 0;
unsigned int period_size = 1024;
unsigned int period_count = 4;
unsigned int channels = 2;
unsigned int rate = 48000;
unsigned int bits = 16;
unsigned int is_raw = 0; /* Default wav file */
char *filename;
int more_chunks = 1;

if (argc < 2) {
fprintf(stderr, "Usage1: %s file.wav [-D card] [-d device] [-p period_size]"
" [-n n_periods] \n", argv[0]);
fprintf(stderr, "Usage2: %s file.raw [-D card] [-d device] [-p period_size] "
"[-n n_periods] [-c channels] [-r rate] [-b bits] -i raw \n", argv[0]);
return 1;
}

filename = argv[1];
file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
return 1;
}

/* parse command line arguments */
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
}
if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
period_size = atoi(*argv);
}
if (strcmp(*argv, "-n") == 0) {
argv++;
if (*argv)
period_count = atoi(*argv);
}
if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
}
if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
channels = atoi(*argv);
}
if (strcmp(*argv, "-r") == 0) {
argv++;
if (*argv)
rate = atoi(*argv);
}
if (strcmp(*argv, "-b") == 0) {
argv++;
if (*argv)
bits = atoi(*argv);
}
if (strcmp(*argv, "-i") == 0) {
argv++;
if (*argv) {
if (strcasecmp(*argv, "raw") == 0) {
is_raw = 1;
}
}
}
if (*argv)
argv++;
}

if ( !is_raw ) {
fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
if ((riff_wave_header.riff_id != ID_RIFF) ||
(riff_wave_header.wave_id != ID_WAVE)) {
fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
fclose(file);
return 1;
}
do {
fread(&chunk_header, sizeof(chunk_header), 1, file);
switch (chunk_header.id) {
case ID_FMT:
fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
/* If the format header is larger, skip the rest */
if (chunk_header.sz > sizeof(chunk_fmt))
fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
break;
case ID_DATA:
/* Stop looking for chunks */
more_chunks = 0;
break;
default:
/* Unknown chunk, skip bytes */
fseek(file, chunk_header.sz, SEEK_CUR);
}
} while (more_chunks);
channels = chunk_fmt.num_channels;
rate = chunk_fmt.sample_rate;
bits = chunk_fmt.bits_per_sample;
}

play_sample(file, card, device, channels, rate, bits, period_size, period_count);

fclose(file);

return 0;
}

int check_param(struct pcm_params *params, unsigned int param, unsigned int value,
char *param_name, char *param_unit)
{
unsigned int min;
unsigned int max;
int is_within_bounds = 1;

min = pcm_params_get_min(params, param);
if (value < min) {
fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
param_unit, min, param_unit);
is_within_bounds = 0;
}

max = pcm_params_get_max(params, param);
if (value > max) {
fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
param_unit, max, param_unit);
is_within_bounds = 0;
}

return is_within_bounds;
}

int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,
unsigned int rate, unsigned int bits, unsigned int period_size,
unsigned int period_count)
{
struct pcm_params *params;
int can_play;

params = pcm_params_get(card, device, PCM_OUT);
if (params == NULL) {
fprintf(stderr, "Unable to open PCM device %u.\n", device);
return 0;
}

can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");
can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");
can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz");
can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz");

pcm_params_free(params);

return can_play;
}

void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
unsigned int rate, unsigned int bits, unsigned int period_size,
unsigned int period_count)
{
struct pcm_config config;
struct pcm *pcm;
char *buffer;
int size;
int num_read;

memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;
config.period_count = period_count;
if (bits == 32)
config.format = PCM_FORMAT_S32_LE;
else if (bits == 16)
config.format = PCM_FORMAT_S16_LE;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;

if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
return;
}

pcm = pcm_open(card, device, PCM_OUT, &config);
if (!pcm || !pcm_is_ready(pcm)) {
fprintf(stderr, "Unable to open PCM device %u (%s)\n",
device, pcm_get_error(pcm));
return;
}

size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "Unable to allocate %d bytes\n", size);
free(buffer);
pcm_close(pcm);
return;
}

printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);

/* catch ctrl-c to shutdown cleanly */
signal(SIGINT, stream_close);

do {
num_read = fread(buffer, 1, size, file);
if (num_read > 0) {
if (pcm_write(pcm, buffer, num_read)) {
fprintf(stderr, "Error playing sample\n");
break;
}
}
} while (!close && num_read > 0);

free(buffer);
pcm_close(pcm);
}




compilation error:

g++ -Wall -pthread -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -c "tinyplay0000.c" -lshapes -lwiringPi -lrt -lpigpio -std=c++11 (im Verzeichnis: /home/pi/Desktop/pi/programs/soundprogs)
tinyplay0000.c:29:32: fatal error: tinyalsa/asoundlib.h: Datei oder Verzeichnis nicht gefunden
#include <tinyalsa/asoundlib.h>

compilation terminated.
Kompilierung fehlgeschlagen.

Fehlt noch was für die korrekte Installation der Lib?
Was muss ich ggf. für Geany dazulinken?

botty
21.05.2016, 13:30
Hallo,

das ist ein Compiler Fehler, weil er nicht den Pfad zum Include Directory kennt, wo die tinyalsa lib liegt. Deine installation von oben befördert die Lib nicht in irgendwelche Standardpfade.
Da muß also noch ein

-I/wo/immer/liegt/tinyalsa/include ins makefile rein dann auch ein
-L/wo/immer/liegt/tinyalsa/lib
sonst meckert später der Linker.

Gruß

Chris

HaWe
21.05.2016, 13:57
aha....?
ich dachte, weil doch oben im include schon der Pfad steht, müsste er's wissen..:
#include <tinyalsa/asoundlib.h>

aber stimmt ja auch, es gibt ja gar kein Verzeichnis
/tinyalsa/

wo ist dann die lib hin installiert worden?

botty
21.05.2016, 14:15
Guck mal kurz ins Makefile vom tinyalsa. Es gibt kein 'install' target, sprich die lib wird nicht nach /opt-oder-sonstwohin gelegt, sodass g++ das Ding über die Standardpfade finden kann. Statt dessen liegt alles in dem Verzeichnis wohin Du den Source gepackt hast und das *.h file im include Verzeichnis darunter.
Die Pfade mußt Du Deiner IDE/makefile mitgeben und dann auch noch ein
-ltinyalsa für die Library selbst beim Linken, sorry hatte ich oben vergessen.

HaWe
21.05.2016, 15:29
aha, hier ist was:

/home/pi/tinyalsa/

das ist jetzt ein ziemlich blöder Speicherort, warum nicht global ?
Ich verschiebs mal nach /lib/tinyalsa....

Schei**e, keine Berechtigung.
Was ein Dreck.
Ich HASSE Linux.

wie kann ich das Verzeichnis umändern?

- - - Aktualisiert - - -

hm,
sudo cp -rf ... ...
hat geklappt.

ok.

jetzt also als Zusatz-Parameter für compile und build:


-I/lib/tinyalsa/include -L/lib/tinyalsa/lib -ltinyalsa

richtig?

auch
-L/lib/tinyalsa/lib
ist richtig??? wieso nochmal lib am Schluss?

botty
21.05.2016, 16:27
nein kein lib am ende.

du müßtest jetzt die dateien

/lib/tinyalsa/libtinyalsa.so
/lib/tinyalsa/libtinyalsa.a

haben. Damit muß



-I/lib/tinyalsa/include -L/lib/tinyalsa -ltinyalsa

lauten.

HaWe
21.05.2016, 16:55
g++ -Wall -pthread -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -c "tinyplay0000.c" -lshapes -lwiringPi -lrt -lpigpio -I/lib/tinyalsa/include -L/lib/tinyalsa -ltinyalsa -std=c++11 (im Verzeichnis: /home/pi/Desktop/pi/programs/soundprogs)
tinyplay0000.c: In function ‘int check_param(pcm_params*, unsigned int, unsigned int, char*, char*)’:
tinyplay0000.c:201:43: error: invalid conversion from ‘unsigned int’ to ‘pcm_param’ [-fpermissive]
min = pcm_params_get_min(params, param);
^
In file included from tinyplay0000.c:29:0:
/lib/tinyalsa/include/tinyalsa/asoundlib.h:151:14: note: initializing argument 2 of ‘unsigned int pcm_params_get_min(pcm_params*, pcm_param)’
unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
^
tinyplay0000.c:208:43: error: invalid conversion from ‘unsigned int’ to ‘pcm_param’ [-fpermissive]
max = pcm_params_get_max(params, param);
^
In file included from tinyplay0000.c:29:0:
/lib/tinyalsa/include/tinyalsa/asoundlib.h:153:14: note: initializing argument 2 of ‘unsigned int pcm_params_get_max(pcm_params*, pcm_param)’
unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
^
tinyplay0000.c: In function ‘int sample_is_playable(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)’:
tinyplay0000.c:231:77: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");
^
tinyplay0000.c:231:77: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
tinyplay0000.c:232:88: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");
^
tinyplay0000.c:232:88: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
tinyplay0000.c:233:84: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
^
tinyplay0000.c:233:84: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
tinyplay0000.c:234:92: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz");
^
tinyplay0000.c:234:92: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
tinyplay0000.c:235:90: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz");
^
tinyplay0000.c:235:90: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
tinyplay0000.c: In function ‘void play_sample(FILE*, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)’:
tinyplay0000.c:277:25: error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]
buffer = malloc(size);
^
Kompilierung fehlgeschlagen.



was ein Mist

- - - Aktualisiert - - -


du müßtest jetzt die dateien

/lib/tinyalsa/libtinyalsa.so
/lib/tinyalsa/libtinyalsa.a

ja, habe ich.

botty
21.05.2016, 17:12
das ist ein c und kein c++ beispiel!
kompilier das mal mit gcc und nicht g++.

die fehler die hier auftreten beruhen darauf das enums in c++ klassen und nicht unsigned ints sind.
auch die typprüfung von buffer ganz unten beruht auf dem strikterem c++ typsystem.

HaWe
21.05.2016, 18:14
das geht dann nicht, weil wav files ein Teil eines C++ Projekts werden müssen.
Kann man die lib umschreiben auf C++ Kompatibilität?

botty
21.05.2016, 19:40
Es ist das Beispiel, dass nicht sauber ist, nicht die Bibliothek.

Leider mußt Du noch was anderes machen, da habe ich nicht aufgepaßt.
Entweder Du verschiebst die /lib/tinyalsa/libtinyalsa.* nach /usr/local/lib oder du machst einen symbolischen link in /usr/local/lib auf diese Dateien, sonst findet der Linker zur Laufzeit die Libs nicht.

Ansonsten sollte dieses modifizierte Beispiel sich kompilieren und starten lassen, zumindest auf meinem Lappi hat das geklappt.


Gruß
Chris



/* tinyplay.c
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of The Android Open Source Project nor the names of
** its contributors may be used to endorse or promote products derived
** from this software withplay_cpp.cout specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
*/

#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>

#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164

struct riff_wave_header {
uint32_t riff_id;
uint32_t riff_sz;
uint32_t wave_id;
};

struct chunk_header {
uint32_t id;
uint32_t sz;
};

struct chunk_fmt {
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
};

static int close = 0;

void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
unsigned int rate, unsigned int bits, unsigned int period_size,
unsigned int period_count);

void stream_close(int sig)
{
/* allow the stream to be closed gracefully */
signal(sig, SIG_IGN);
close = 1;
}

int main(int argc, char **argv)
{
FILE *file;
struct riff_wave_header riff_wave_header;
struct chunk_header chunk_header;
struct chunk_fmt chunk_fmt;
unsigned int device = 0;
unsigned int card = 0;
unsigned int period_size = 1024;
unsigned int period_count = 4;
unsigned int channels = 2;
unsigned int rate = 48000;
unsigned int bits = 16;
unsigned int is_raw = 0; /* Default wav file */
char *filename;
int more_chunks = 1;

if (argc < 2) {
fprintf(stderr, "Usage1: %s file.wav [-D card] [-d device] [-p period_size]"
" [-n n_periods] \n", argv[0]);
fprintf(stderr, "Usage2: %s file.raw [-D card] [-d device] [-p period_size] "
"[-n n_periods] [-c channels] [-r rate] [-b bits] -i raw \n", argv[0]);
return 1;
}

filename = argv[1];
file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
return 1;
}

/* parse command line arguments */
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
}
if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
period_size = atoi(*argv);
}
if (strcmp(*argv, "-n") == 0) {
argv++;
if (*argv)
period_count = atoi(*argv);
}
if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
}
if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
channels = atoi(*argv);
}
if (strcmp(*argv, "-r") == 0) {
argv++;
if (*argv)
rate = atoi(*argv);
}
if (strcmp(*argv, "-b") == 0) {
argv++;
if (*argv)
bits = atoi(*argv);
}
if (strcmp(*argv, "-i") == 0) {
argv++;
if (*argv) {
if (strcasecmp(*argv, "raw") == 0) {
is_raw = 1;
}
}
}
if (*argv)
argv++;
}

if ( !is_raw ) {
fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
if ((riff_wave_header.riff_id != ID_RIFF) ||
(riff_wave_header.wave_id != ID_WAVE)) {
fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
fclose(file);
return 1;
}
do {
fread(&chunk_header, sizeof(chunk_header), 1, file);
switch (chunk_header.id) {
case ID_FMT:
fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
/* If the format header is larger, skip the rest */
if (chunk_header.sz > sizeof(chunk_fmt))
fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
break;
case ID_DATA:
/* Stop looking for chunks */
more_chunks = 0;
break;
default:
/* Unknown chunk, skip bytes */
fseek(file, chunk_header.sz, SEEK_CUR);
}
} while (more_chunks);
channels = chunk_fmt.num_channels;
rate = chunk_fmt.sample_rate;
bits = chunk_fmt.bits_per_sample;
}

play_sample(file, card, device, channels, rate, bits, period_size, period_count);

fclose(file);

return 0;
}

int check_param(struct pcm_params *params, enum pcm_param param,
unsigned int value, const char *param_name,
const char *param_unit)
{
unsigned int min;
unsigned int max;
int is_within_bounds = 1;

min = pcm_params_get_min(params, param);
if (value < min) {
fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
param_unit, min, param_unit);
is_within_bounds = 0;
}

max = pcm_params_get_max(params, param);
if (value > max) {
fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
param_unit, max, param_unit);
is_within_bounds = 0;
}

return is_within_bounds;
}

int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,
unsigned int rate, unsigned int bits, unsigned int period_size,
unsigned int period_count)
{
struct pcm_params *params;
int can_play;

params = pcm_params_get(card, device, PCM_OUT);
if (params == NULL) {
fprintf(stderr, "Unable to open PCM device %u.\n", device);
return 0;
}

can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");
can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");
can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz");
can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz");

pcm_params_free(params);

return can_play;
}

void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
unsigned int rate, unsigned int bits, unsigned int period_size,
unsigned int period_count)
{
struct pcm_config config;
struct pcm *pcm;
char *buffer;
int size;
int num_read;

memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;
config.period_count = period_count;
if (bits == 32)
config.format = PCM_FORMAT_S32_LE;
else if (bits == 16)
config.format = PCM_FORMAT_S16_LE;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;

if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
return;
}

pcm = pcm_open(card, device, PCM_OUT, &config);
if (!pcm || !pcm_is_ready(pcm)) {
fprintf(stderr, "Unable to open PCM device %u (%s)\n",
device, pcm_get_error(pcm));
return;
}

size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
buffer = (char *)malloc(size);
if (!buffer) {
fprintf(stderr, "Unable to allocate %d bytes\n", size);
free(buffer);
pcm_close(pcm);
return;
}

printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);

/* catch ctrl-c to shutdown cleanly */
signal(SIGINT, stream_close);

do {
num_read = fread(buffer, 1, size, file);
if (num_read > 0) {
if (pcm_write(pcm, buffer, num_read)) {
fprintf(stderr, "Error playing sample\n");
break;
}
}
} while (!close && num_read > 0);

free(buffer);
pcm_close(pcm);
}

HaWe
21.05.2016, 20:28
Was ???
findet nicht in /lib/ ???
gottverdammt nochmal, was ist Linux doch ein total beschissenes Betriebssystem!


- - - Aktualisiert - - -

ja, jetzt lässt es sich kompilieren, aber exit mit code 127.

Wie bringt man jetzt das Programm dazu, einen wav file abzuspielen?

- - - Aktualisiert - - -

also, ich gebs auf, diese tinyalsa ist einfach ein völlig unbrauchbares Programm.
Jetzt krieg ich mit den neuen -l -I -L Parametern noch nichtmal mehr meine alten programme compiliert.
So eine Kacke!


Gibt es andere, einfache C/C++ API libs, um wav files abzuspielen, ohne solchen beknackten compile- und link- Einstellungen wie bei tinyalsa?
es muss doch verdammt nochmal ein einfaches #include <XXX.h> ausreichen, verdammt!!

HaWe
22.05.2016, 12:16
unterm Strich muss es eine C/C++ API geben, die zum wav-Abspielen per C Programmierung nicht viel mehr braucht als simple 3 Befehle:

fp=fopen("wavfile.wav", "r");
playwavfile(fp);
close(fp);

Alles andere muss eine interne Angelegenheit der API lib sein, mit der man als Programmier-End-User nichts zu tun haben muss.

Ceos
23.05.2016, 08:32
also ich muss hier jetzt mal Linux verteidigen .. und das als alteingesessener Windows Nutzer
Linux hat sehr wohl eine brauchbare Nutzer/Gruppen/Gast Rechteverwaltung, man muss nur mal ein wenig Hirnschmalz einsetzen um von den ewig parallelen Zugriffsrechten runterzukommen ...

einige Ordner auf meiner Festplatte haben 8 Benutzer(-gruppen) ... 0 Übersicht! In Linux lege ich einen Nutzer für NEtzwerkkram an, einen für Datenbank und einen für Webseiten Zeug. Jeder bekommt seine Ordner und für die Libs und Shared Elemente verwende ich dann einfach eine gemeinsame Gruppe mit einem dedizierten Ordner ... so habe ich maximale Sicherheit

außerdem ist Linux weit flexibler als Windows und unter der Aufwand eines initialen (aber spürbaren) Lernaufwandes sehr produktiv einsetzbar ... der rest erklärt sich i.d.R. mit ein wenig Google für die notwendigen Komponenten, etwas "man someLib" (wenn man es per apt-get installiert hat) und der Rest ist enthusiasmus ... der RPI ist definitv nichts für mal eben schnell und sauber, sondern für absolute bastler und perfektionisten!

HaWe
23.05.2016, 09:59
ja, für spezielle Zwecke mag das ja sein, aber es fehlt an der Möglichkeit, die Userrechte auf Wunsch auch zu vereinfachen. Dass ich als alleiniger Nutzer nicht festlegen kann, dass ich auch als "pi" immer root Rechte habe, und dann möglicherweise andere Dinge nicht mehr laufen, die nicht für root erlaubt sind, ist einfach ein Unding. Wie auch peterfido schon schrieb, das könnte man auch anders lösen, u.a. als embedded Linux oder wie auch immer. Wer viele User braucht, soll sie haben, aber dass ich meine eigenen wiringPi Programme nicht als pi starten kann vom File Manager aus, ist ein Unding, und dass ich keine Lib-Dateien verschieben kann in ein Systemverzeichnis (z.B. /lib/), ebenfalls, und dass ich ständig sudo nano brauche, um /boot/config.txt zu editieren, ebenfalls, weil es der Editor Leafpad mangels Berechtigung nicht zulässt.
Die ganze GUI ist vollkommener Quatsch, wenn man wegen jedem Mist wieder auf das terminal und die Kommandozeile zurückgeworfen wird, wie vor hundert Jahren. Wozu hat man ne GUI, wenn ständig LeafPad und FileManger ihren Dienst versagen mangels "User-Rechten" ?

Aber hier geht es in dem Topic ja um wav Dateien, und wie man sie mit einer C API einfach programmieren kann, ohne tonnenweise Code selber schreiben zu müssen. Eher so wie oben im Beispiel:

fp=fopen("wavfile.wav", "r");
playwavfile(fp);
close(fp);


Die API Lib kann selbstständig die Metadaten für die Wiedergabe im File lesen und ihn dann entsprechend abspielen, da muss ich mich doch nicht jedesmal haarklein selber drum kümmern müssen!

Das hat ja auch nichts mit Linux direkt zu tun, sondern nur mit den zur Verfügung stehenden C/C++ API libs, und so was muss es jetzt einfach mal schon geben, in den 40 Jahren seit K+R.

Allerdings hat es was mit der Raspi org zu tun, dass die nämlich endlich mal nicht nur Python, sondern auch C/C++ anständig supporten.
Es gibt ja noch nicht mal für die meisten HATs auch C/C++ Treiber- libs, sondern nur für Python

HaWe
28.05.2016, 10:03
es klappt immer noch nicht.

Wo gibt es denn jetzt ein funktionierenden Code, mit dem man per tinyalsa oder was auch immer einen wav sound einfach mal eben aus einem C programm heraus abspielen kann?? :confused:

HaWe
29.05.2016, 10:12
ob es noch jemand gibt, der einfache Kommandos aus "echten originären, portierbaren" C API libs kennt oder einen Wrapper um tinyalsa Codes selber erstellen kann, damit einfaches wav abspielen mit automatischer Parametererkennung mit 1 einfachen Befehl möglich ist?

HaWe
30.05.2016, 20:59
leider kann ich das Programm bei mir nicht zum Laufen kriegen, um einen wav file abzuspielen...
:(
ich finde aber auch die Doku (wie auch schon hirnfrei bemerkte) zu unklar.
:(


neuer Ansatz:
hirnfreis Code:



#include <alsa/asoundlib.h>

#include <iostream>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

using namespace std;

typedef struct _FILE_head
{
unsigned char ID[4];
unsigned int Length;
unsigned char Type[4];
} FILE_head;

typedef struct _FORMAT
{
short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
} FORMAT;

typedef struct _CHUNK_head
{
unsigned char ID[4];
unsigned int Length;
} CHUNK_head;

snd_pcm_t *soundKarte;

bool Init(const char *name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

unsigned int resample = 1;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name == NULL)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
}
else
{
err = snd_pcm_open (&soundKarte, name, SND_PCM_STREAM_PLAYBACK, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

err = snd_pcm_hw_params_set_rate_resample(soundKarte, hw_params, resample);

if(err < 0)
{
cout << "Init: Resampling kann nicht eingeschaltet werden " << snd_strerror(err) << endl;

return err;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params (soundKarte, hw_params)) < 0)
{
cout << "Init: Üarameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}
// snd_pcm_hw_params_get_buffer_size(hw_params, &bufferSize);

snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool UnInit()
{
snd_pcm_close(soundKarte);

return true;
}

int main()
{
FORMAT format;
FILE_head head;
CHUNK_head chead;

char *wave;

register snd_pcm_uframes_t count, frames;

int datei;

unsigned int WaveSize;

datei = open("/home/pi/Music/cantalow.wav", 00);

read(datei, &head, sizeof(FILE_head));

read(datei, &chead, sizeof(CHUNK_head));

read(datei, &format, sizeof(FORMAT));

wave = (char *) malloc(head.Length);

read(datei, wave, head.Length);

WaveSize = head.Length * 8 / ((unsigned int)format.wBitsPerSample * (unsigned int)format.wChannels);

close(datei);

Init("plughw:1,0", format.wChannels, format.dwSamplesPerSec, format.wBitsPerSample);

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if (frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if (frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

free(wave);

UnInit();

return 0;
}


Ich selber muss hier aber wieder komplett passen, ich verstehe absolut nichts mehr von dem, was da passiert, noch nicht mal wie und wo in main() jetzt überhaupt etwas in der Art steht wie
playwavfile(filename)

ob man das irgendwie in einen verständlichen wrapper rein kriegt?

:confused:

hirnfrei
30.05.2016, 23:25
Machen wir nen Deal.

Du hilfst mir die globale Variable los zu werden und ich bau dir daraus ein


playwave(file, soundkarte);

Weil ich kämpfe heute schon den ganzen Tag daran anstatt eine globale Variable eine übergebne Variable zu basteln und ich kriege es nicht gebacken. Wie so ein kleine Noob :(

HaWe
31.05.2016, 00:24
meinst du
snd_pcm_t *soundKarte
?

das ist wschl Teil des Problems, eventuell wäre es das beste, ein Objekt daraus zu machen mit den Methoden
init()
open()
close()
play()
record()


ich würde sie ansonsten in einer Funktion


snd_pcm_t * init_soundKarte(soundKarte){
snd_pcm_t * retstruc = soundKarte;
//...
//...
return retstruc;
}

eingangs lokal mit Werten initialisieren, dann kannst du sie als Struktur an andere Funktionen über den Pointer weiterreichen.

Der allererste Aufruf müsste dann in main() erfolgen.
In main() müsste dazu nur stellvertretend eine lokale Variable

snd_pcm_t * soundKarte;

angelegt werden.

Dann wird hier per

init_soundKarte(soundKarte);

die Struktur "gefüllt",
und dann kann sie im Rest des Programms entsprechend weiter gereicht und benutzt werden, und so hat man sie per Parameter dort zur Verfügung.

z.B. auch in


bool UnInit(soundKarte)
{
snd_pcm_close(soundKarte);
return true;
}

und entsprechend an allen anderen Funktionen, wie z.B. auch als weiterer Parameter in Init().

Meintest du es so in etwa?


ps,
Ich sehe allerdings in deinem Code auch gar nicht die Stelle, wo soundKarte überhaupt erstmalig mit Werten "gefüllt" wird... :-/

hirnfrei
31.05.2016, 01:06
if(name == NULL)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
}


Hier wird die Soundkarte geöffnet und der Handler soundKarte initialisiert. Von da an geht es ja nur noch darum den Funktionen zu sagen mit welchem Handler sie zu arbeiten hat.

Ich hab auch glaub ich eine gute Lösung. Funktioniert zwar noch nicht, aber ich denke morgen knack ich das. Dann schau ich nach deiner Funktion ^^

PS:

Wenn ich mir meine Threads so ansehe, wie zwei scheinen uns gut zu verstehen ^^

HaWe
31.05.2016, 09:18
jap, denke ich auch ^^

übrigens:
ich hätte kein Problem mit einer globalen Variablen "soundCard", denn wenn man später alles in eine gesonderte Wrapper-lib verpackt (was mir ja eh am liebsten wäre)
<alsabrain.h> // brain ... hirn...du verstehst... ;)
dann könnte man ja super drauf zugreifen, und sehen muss man sie auch nicht im eigentlichen Programm.
Der ganze alsa-overhead wäre dann raus aus dem eigentlichen Anwendungs-Programm.

hirnfrei
31.05.2016, 12:17
Sooooooo.

Das mit der globalen Variable habe ich nicht weg bekommen. Bleibt also erst einmal so. Aber jetzt schön in einer Header-Datei verwurstet. Hier ist der Code:



#include <alsa/asoundlib.h>

#include <iostream>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

using namespace std;

typedef struct _FILE_head
{
unsigned char ID[4];
unsigned int Length;
unsigned char Type[4];
} FILE_head;

typedef struct _FORMAT
{
short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
} FORMAT;

typedef struct _CHUNK_head
{
unsigned char ID[4];
unsigned int Length;
} CHUNK_head;

snd_pcm_t *soundKarte;

bool Init(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

unsigned int resample = 1;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

err = snd_pcm_hw_params_set_rate_resample(soundKarte, hw_params, resample);

if(err < 0)
{
cout << "Init: Resampling kann nicht eingeschaltet werden " << snd_strerror(err) << endl;

return err;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params (soundKarte, hw_params)) < 0)
{
cout << "Init: Üarameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}
// snd_pcm_hw_params_get_buffer_size(hw_params, &bufferSize);

snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool UnInit()
{
snd_pcm_close(soundKarte);

return true;
}

int playwave(string waveDatei, string name)
{
FORMAT format;
FILE_head head;
CHUNK_head chead;

char *wave;

register snd_pcm_uframes_t count, frames;

int datei;

unsigned int WaveSize;

datei = open(waveDatei.c_str(), 00);

read(datei, &head, sizeof(FILE_head));

read(datei, &chead, sizeof(CHUNK_head));

read(datei, &format, sizeof(FORMAT));

wave = (char *) malloc(head.Length);

read(datei, wave, head.Length);

WaveSize = head.Length * 8 / ((unsigned int)format.wBitsPerSample * (unsigned int)format.wChannels);

close(datei);

Init(name, format.wChannels, format.dwSamplesPerSec, format.wBitsPerSample);

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if (frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if (frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

free(wave);

UnInit();

return 0;
}


Jetzt kannst du mit



playwave(Dateiname, Soundkarte);

zB:

playwave("/home/pi/Music/cantalow.wav", "plughw:1,0");


Ganz locker eine Wave abspielen.

HaWe
31.05.2016, 12:33
sieht ja wirklich perfekt aus! 8-)
super, danke für deine Mühe :D

hirnfrei
31.05.2016, 12:40
War keine wirkliche Mühe. Ich werde es sicher auch noch gebrauchen können ^^.

HaWe
31.05.2016, 13:46
ich glaube ich werde dann auch mal in die Mikrophonsache mit einsteigen...
was für eine USB soundCard hast du (Hersteller- oder Bestell-Link) ?

hirnfrei
31.05.2016, 17:01
Dieses da (https://www.roboternetz.de/tools/?view_co=873877)

Knappe 11€ und läuft ganz gut muss ich sagen. Vor Allem, einstecken und geht. Also nichts noch installieren oder umstellen oder wie auch immer. Das hat mir ganz gut gefallen. Bei Gentoo-Linux, was ich ja normalerweise nutze, muss man in der Regel für jede Hardware neue Module gebaut werden. Hat natürlich den Vorteil, der Kernel ist nur so gross wie er sein muss und es werden keine Treiber geladen die man nicht will.

HaWe
31.05.2016, 17:42
satte 111 Euronen?? O.o
da kriegt man ja 3 Raspis für....
hättest aber du mal den Link zum Artikel bitte?

ich glaube, ich werde mir das hier mal näher angucken...
http://www.ebay.de/itm/External-USB-2-0-to-Virtual-7-1CH-Audio-Sound-Card-Adapter-for-Raspberry-Pi-/391115836971?hash=item5b10520e2b:g:M~UAAOxylh1SKKw m

ps,
allerdings ist es für später ja blöd, wenn man dem Pi immer hinterher brüllen muss zur Sprach-Fernsteuerung.
Da wäre dann ein mobiles (kabelloses) Micro-Sendegerät sicher sinnvoller, und ein Funkempfänger am Raspi selber, oder was denkst du?
(meine jetzige Fernsteuer-Konsole hat einen Arduino Due, der immerhin schon mal 2 DAC ports plus BT mit UART bei 115200 baud hat, was immer man dann später damit anfangen will, außer Joystick- und Keypad-Fernsteuerung...)

hirnfrei
31.05.2016, 17:57
Da habe ich eine 1 zu viel getippt. Sorry.

Deine Variante ist aber auch nicht schlecht! Bei meinem blockiert das Teil die anderen USB-Ports. Kann man zwar mit einer Verlängerung umgehen, aber blöd ist es schon etwas.

Und die Idee mit einem Headset hatte ich auch schon. Aber meine Kinder hören leider auch nicht wenn ich nicht schreie ;). Also das wird schon funktionieren denke, hoffe ich.

HaWe
31.05.2016, 18:02
haha, und du meinst, weil deine Kinder nicht auf Schreien hören (meine auch noch nie), da machts dann nichts, wenn der Pi dann auch nicht hört? :D

wie würde das denn technisch funktionieren mit einem Headset? Per BT? Da hast du mit deinem Pi 3 ja einen Vorteil, ich brauch da doch wieder einen USB Port.
Und dann wie weiter, wie kriegt man den Sound vom BT-Empfänger dann in den Pi als wav file?

hirnfrei
31.05.2016, 18:13
Das weiss ich leider nicht. Habe ich bisher noch nicht mit gearbeitet. Ich hatte allerdings vor einigen Jahren ein BT Headset am Rechner, um mit meinen Leuten in SecondLife zu reden und wenn ich das richtig im Kopf habe lief das dann auch über Alsa, also das würde behandelt wie eine Netzwerkkarte und in dem Fall wäre es relativ einfach denke ich. Mal davon ausgehend das es mit dem Aufnehmen genau so einfach geht wie mit dem Abspielen bei Alsa.

HaWe
31.05.2016, 20:53
verrätst du mir dann vlt doch noch den Link zu deiner Soundkarte für 11 EUR ? :)

hirnfrei
31.05.2016, 21:15
Klick doch auf "Diese da". Das ist der Link.

HaWe
31.05.2016, 21:19
?????
schreib doch bitte einfach den Link in Klarschrift, ich mag diese versteckten Links nicht - wenn ich sie überhaupt mal finde, was jetzt aber immer noch nicht der Fall ist :(

hirnfrei
31.05.2016, 21:28
Normalerweise werde ich immer angeprangert, wenn ich einen kompletten Link post...

https://www.roboternetz.de/tools/?view_co=873877

HaWe
31.05.2016, 21:52
deswegen "angeprangert"? :confused:
wenn man es beschreibt wie


ich habe diese da: https://www.roboternetz.de/tools/?view_co=873877

oje, da kann doch niemand ernsthaft was dagegen haben... :confused: :confused:

aber danke, jetzt sehe ich es: von Conrad, das hieße jetzt: +50% Versandkosten.... :-/
Hätte ichs geahnt, dann hätte ichs vor ein paar Tagen wschl schon mitbestellt...

aber da guck ich eben mal bei Ebay ... ;)

i_make_it
01.06.2016, 07:28
aber es fehlt an der Möglichkeit, die Userrechte auf Wunsch auch zu vereinfachen. Dass ich als alleiniger Nutzer nicht festlegen kann, dass ich auch als "pi" immer root Rechte habe, und dann möglicherweise andere Dinge nicht mehr laufen, die nicht für root erlaubt sind,


Eigentlich hat Unix ein sehr einfaches Rechtesystem. (User, Gruppe, Alle) Im Gegensatz zu Windows ist es halt auch eingeschaltet und man kann nicht einfach den Standartuser von vorneherein als Administrator anlegen. Da das bei Windows die Hauptsicherheitslücke ist, hat Microsoft ja mitlerweile die "Data Execution Prevention" (DEP) eingeführt, mit der man mit "run as Administrator" ein sudo bei Windows eingeführt hat. (ein Adminstrator ist bei Windows somit heute kein Admin mehr sondern ein privilegierter User)

Wenn es um fein graduierte Rechte geht, dann mal mit "apt-get install acl attr" befassen.
Damit kann man alle von Windows bekannten Attribute und Access Controll Lists auf Unix implementieren (dann hat man eine komplizierte Rechteverwaltung im Windows Stil)

Man kann die Sicherheit auch auschalten in dem man den "pi" user in die sudo Gruppe aufnimmt oder mit visudo /etc/sudoers
die Zeile "pi ALL=(ALL)"
Analog zu root einfügt.
Wirft man root raus oder beschädigt die Datei sudoers hat man ein ganz sicheres System und darf neu installieren.
deshalb visudo und kein anderer Editor.

NACHTRAG:
Da Microsoft seit Server 2008 mit "Windows Server Core" auch Unix Style Server ohne GUI und klicki bunti Schnick Schnack anbietet, dürfte klar sein das UNIX in vielen Bereichen doch einfach ausgereifter ist und nicht veraltet. Sonst würde Microsoft bestimmt nicht DEP (Sudo) und Core (Console) nach so langer Zeit nachbauen und Anbieten.

HaWe
01.06.2016, 10:52
so wie es jetzt ist ist es IMO nicht handhabbar und unzumutbar für Einzel-User und Hobby-Anwender, wenn man noch nichtmal als pi user das leafpad und den filemanager über die GUI so einstellen kann, dass man Systemdateien ändern und verschieben oder löschen kann, wie man es auch mit sudo im Terminal kann. Aber ich will den ganzen pi, sudo und root Mist hier nicht nochmal aufwärmen. Und es geht ja auch z.B. bei embedded Linux ganz ohne Userkram, das wäre für Raspi Bastelein völlig ausreichend.

Also wieder zurück zum Stand der Dinge, jetzt steht aufnehmen von wav files mit alsa auf dem Programm...

hirnfrei
01.06.2016, 16:12
Ich arbeite ja schon lange mit Linux. Habe mir kürzlich nochmal Windows 7 in die virtuelle Box geladen (war bei Netbook dabei aber noch nie im Einsatz ^^) da ich für eine bekannte Firma die Rechnerwartung übernommen habe und da ja schon Probleme reproduzieren können sollte und ich fühle mich bei Windows einfach eingesperrt. Wenn bei Linux was nicht startet über die GUI starte ich es über einen Terminal und siehe da, dir Rückgaben sind meist sehr aufschlussreich. Ich könnte also theoretisch ebenfalls sagen, dass Windows für mich unzumutbar ist, weil es einen so gesehen einfach einsperrt, auf einen Desktop beschränkt usw ;).

Aber zurück zum Thema.

Ich denke weder wird meine Karte die Beste sein, noch die Billigste oder die Geeignetste. Sie hatte nur den Vorteil, ich hab meiner Tochter 12€ in die Hand gedrückt und als die von der Schule gekommen ist hat sie das Teil aus dem Conrad grade mit gebracht.

Bedenkt man aber wie lange es gedauert hat, bis ich dann wirklich daran zum Arbeiten kam würde ich sagen, ich hätte es auch für einen Bruchteil in China bestellen und drauf warten können, bis der arme Chinese es her gerudert hat.

HaWe
01.06.2016, 18:38
aha, daher hast du also deine Profi-Kenne.... :cool:

OT
im Prinzip klappt es mit DOS und Windows ohne jedwede Probleme schon seit -zig Jahren, und zwar
- auf DOS mit GWBasic und Turbo Pascal und Turbo C für die IOs von einem Fischertechnik Interface: keine Probleme mit User-Rechten
- auf Windows mit NQC und NXC per BricxCC auf den Lego Bricks RCX und NXT: keine Probleme mit User-Rechten
- auf Windows per BricxCC und gpp + CSLite Toolchains auch ansatzweise den Lego EV3: keine Probleme mit User-Rechten (komplett gescheitert allerdings bin ich mit Linux auf dem EV3 selber, das war der einzige Grund, weswegen ich mich Arduino zugewandt habe...)
- auf Windows dann mit der Arduino-IDE für meine AVRs (Nano, Mega) und meinen ARM (Due): keine Probleme mit User-Rechten

Für mich ist der Raspi eben ein Roboter-Bastel-Board für mich alleine und kein Mainframe mit Dutzenden von seriellen User-Terminals und einem 40-Jahre alten Monster-OS mit dem Bedienkomfort einer Keilschrift- oder Hieroglyphensteintafel - NIIIIEEE im Leben hätte ich jemals einen Linux PC angerührt
Entsprechend dem Einsatzbereich muss man also die Software wählen und konfigurieren können.
Soviel zu Linux, dass ich ansonsten hasse wie der Teufel das Weihwasser. :mad:

Mein Motto zu Betriebssystemen ist:
Ein gutes Betriebssystem erkennt man ganz leicht daran, dass man es bei der Benutzung gar nicht wahrnimmt... ;)
/OT

Ich hab mir ein ähnliches USB Soundkarten-Teil jetzt bestellt, das du auch hast, unterm Strich müsste aber jedes gleich laufen und kosten tun auch alle etwa das gleiche.

Das Wesentliche ist jetzt aber,
1.) ein paar Muster-wav-Sounds erstellen
JA
NEIN
ABBRUCH
MENÜ
VOR
ZURÜCK
LINKS
RECHTS
STOPP
AN
AUS

Dann
2.) diese "Datenbank-wavs" von Rauschen befreien und von der Dynamik her aufpeppen und "normalisieren" für die folgenden FFTs.

Dann
3.) einen Testsound aufnehmen, als test.wav speichern, und ebenfalls "normalisieren" für die folgenden FFTs.

und dann
4.) audio cross correlation wie hier beschrieben darauf anwenden
http://dsp.stackexchange.com/questions/736/how-do-i-implement-cross-correlation-to-prove-two-audio-files-are-similar

hirnfrei
01.06.2016, 19:00
OT

Die Diskussion können wir Jahre führen. Hatte damals noch unter BTX in verschiedenen Foren die mega Diskussionen was besser ist. Amiga oder Dos/Windows. Da konnte man mit allen stichhaltigen Argumenten kommen, keine Chance. Ich denke genau so wenig würde ich es jetzt schaffen dich von Linux zu überzeugen. Kommt dann noch ein Apple User ist es ganz vorbei. Von daher, ich denke mal wir belassen es dabei ;).

/OT

Ich nehme an, heute Abend kriege ich es noch hin etwas aufzunehmen. Ob ich es speichere weiss ich noch nicht. Ist eigentlich nicht in meiner Planung drin. Da ich erst einmal sehen will wie die Daten überhaupt aussehen die vom Mikro kommen. Dann sehe ich weiter.

HaWe
01.06.2016, 19:44
OT
mit einem Apple User würde ich mich blendend verstehen, was intuitive Benutzung angeht (auch wenn inzwischen Unix druntersteckt) - Steve Jobs hatte da genau die richtige Idee (und wurde erfolgreich von Gates kopiert). So gesehen ist Apple gar nicht so weit von Windows entfernt. Ich verabscheue allerdings Apples Beutelschneiderei und ihre "high walls and holy gardens".
Was allerdings Roboter-Programmierung angeht, liegt für mich Arduino meilenweit vorn.
Das eine ist halt das OS (von dem man gar nichts merken sollte),
und das andere ist die C-Programmierumgebung, mit der man einfach und (ebenfalls) intuitiv arbeiten können muss.
/OT

Bin sehr gespannt, wie die wav Daten aufbereitet werden können. Für die FFT muss man sie ja in einen Array mit einer bestimmten Dimension zwängen, und da denke ich: je kleiner die Bit-Rate und Sample-Frequenz, desto schneller und besser insgesamt - 8bit und 11kHz müssten dicke reichen, oder?

hirnfrei
01.06.2016, 20:05
Das weiss ich eben noch nicht. Hab so gesehen noch nie ein Wav in roher Form gesehen. In meinem Programm ist es ja ein char, aber um es abzuspielen muss man jedes einzelne char einzeln übergeben. Genau genommen ist es also ein dickes int array. Jetzt muss ich eben mal schauen wie das so aussieht. vielleicht in eine csv Datei speichern und mal durch LibreOffice plotten lassen, ob da dann schon das eigentliche Audiomuster heraus kommt. Das wäre dann ja einfach ;). Auch denke ich, wenn speichern wird es ja eigentlich nicht unbedingt ein bekanntes format sein müssen. Es sei denn man will es auch mit einem normalen Player abspielen. Rohdaten sollten da eigentlich reichen, dann muss man nicht noch Header erzeugen und auslesen.

HaWe
01.06.2016, 20:33
normalerweise wird ja mit 44kHz in 16 bit gesampelt.
Der Header muss dann raus.
d.h. du hast in 1 sek 44000 int16_t
das muss per 11kHz auf rund 11 000 Werte runter,
die FFT rechnet aber mit floats (da wäre es sogar egal, ob 16 oder 8 Bit Auflösung)

Geben wir dem gesprochenen Wort 2-3 sec, wären es dann 22000-33000 float-Zellen.
Zur cross correlation muss man mindestens noch mal die gleiche Länge Nullwerte hinten dran hängen,
und die FFT rechnet dabei mit Komplexen Zahlen (2 float arrays).
da wäre also eine insgesamte Arraygröße von

float fx_[fxyLen], fy_[fxyLen]
mit
#define fxyLen USHRT_MAX // == 2^16

passend.

Damit hätte man was für den Anfang...


ps,
hier ist mal ein FFT Programm, das ich für den Lego NXT geschrieben habe.
War damals aber zu langsam für cross correlation auf dem Bytecode-Interpreter.
Anfangs war es komplettes Neuland für mich als Lego-Programmierer, aber mit der Zeit habe ich ein wenig "Gefühl" für die Sache entwickelt.
Ist schon sehr beeindruckend, was die FFT leistet.

http://www.mindstormsforum.de/viewtopic.php?f=70&t=6665&p=56262#p56262

hirnfrei
02.06.2016, 00:31
Also es funktioniert schon zum Teil. Ich kann aufnehmen, die Bitrate und Frequenz einstellen und es wieder abspielen. Aber das rauscht abartig!

HaWe
02.06.2016, 11:28
könntest du mal bitte deinen Aufnehme-Code posten?
Und dabei v.a. auch, wo und wie du die Parameter für Empfindlichkeit / Aufnahmelautstärke etc. justierst?

Ist das Rauschen konstantes "weißes Rauschen" (so wie konstant lautes Meeresrauschen) oder "bunt" oder sogar von wechselnder Art oder Lautstärke (so wie wenn durch Hintergrundgeräusche wie Autos, Stimmen, Motore, Küchengeräusche oder Radio "verrauscht") ?

hirnfrei
02.06.2016, 11:55
Hier die Erweiterung meiner Header-Datei, die ich dir ja schon geschickt habe.



#include <alsa/asoundlib.h>

#include <iostream>
#include <vector>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

using namespace std;

typedef struct _FILE_head
{
unsigned char ID[4];
unsigned int Length;
unsigned char Type[4];
} FILE_head;

typedef struct _FORMAT
{
short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
} FORMAT;

typedef struct _CHUNK_head
{
unsigned char ID[4];
unsigned int Length;
} CHUNK_head;

snd_pcm_t *soundKarte;

bool Init(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

unsigned int resample = 1;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

err = snd_pcm_hw_params_set_rate_resample(soundKarte, hw_params, resample);

if(err < 0)
{
cout << "Init: Resampling kann nicht eingeschaltet werden " << snd_strerror(err) << endl;

return err;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}
snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool InitCapture(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_CAPTURE, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_CAPTURE, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}

snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool UnInit()
{
snd_pcm_close(soundKarte);

return true;
}

int playwave(string waveDatei, string name)
{
FORMAT format;
FILE_head head;
CHUNK_head chead;

char *wave;

register snd_pcm_uframes_t count, frames;

int datei;

unsigned int WaveSize;

datei = open(waveDatei.c_str(), 00);

read(datei, &head, sizeof(FILE_head));

read(datei, &chead, sizeof(CHUNK_head));

read(datei, &format, sizeof(FORMAT));

wave = (char *) malloc(head.Length);

read(datei, wave, head.Length);

WaveSize = head.Length * 8 / ((unsigned int)format.wBitsPerSample * (unsigned int)format.wChannels);

close(datei);

Init(name, format.wChannels, format.dwSamplesPerSec, format.wBitsPerSample);

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if (frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if (frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

free(wave);

UnInit();

return 0;
}

vector<int> audioCapture(int sek, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err, zielzeit;

char *puffer;

vector<int> input;

time_t t;

t = time(NULL);

zielzeit = t + sek;

puffer = (char *) malloc(1);

cout << "Beginne Aufnahme von " << sek << " Sekunden!" << endl;

if(InitCapture(name, channels, actualRate, WaveBits))
{
while(t < zielzeit)
{
err = snd_pcm_readi(soundKarte, puffer, 1);

input.push_back(puffer[0]);

t = time(NULL);
}

UnInit();
}
else cout << "Bei der Initialisierung ist ein Fehler aufgetreten!" << endl;

cout << "Aufnahme beendet!" << endl;

return input;
}

void playCaptured(string wave, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
register snd_pcm_uframes_t count, frames;

unsigned int WaveSize = wave.length();

Init(name, channels, actualRate, WaveBits);

count = 0;

// cout << "Hier" << endl;
do
{
frames = snd_pcm_writei(soundKarte, wave.c_str() + count, WaveSize - count);

if (frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if (frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

UnInit();
}


und hier meine main.cpp



#include <iostream>
#include <vector>

#include <stdio.h>

#include "diaSound.hpp"

int main()
{
vector<int> input;

string wave;

input = audioCapture(5, "plughw:1,0", 1, 44100, 16);

for(int i=0;i<input.size();i++) wave += input[i];

playCaptured(wave, "plughw:1,0", 1, 44100, 16);

return 1;
}


Es hört sich an als wäre das Mikro massiv übersteuert.

HaWe
02.06.2016, 12:30
was ist der Unterschied zwischen playWave und playCaptured ?
könnte man das ggf vereinheitlichen?

hirnfrei
02.06.2016, 13:46
bei playWave(); übergibst du ein Dateiname. Dieser wird geladen, die Header und Chunk ausgelesen, damit die Soundkarte geöffnet und das dann abgespielt.

Bei playCaptured(); übergibst du nur die Rohdaten und gibst die Daten für die Soundkarte vor.

Vereinheitlichen dürfte schwierig werden, da man dann für playWave ebenfalls die Vorgaben für die Soundlarten treffen müsste, auch wenn diese dann von der Datei überschrieben werden.

HaWe
02.06.2016, 15:01
achso, playCaptured spielt einen array ab, keinen File, richtig?
vlt wäre dann
playSoundarray
besser, denn nicht alles ist ja "gecaptured"... 8-)

aber was ist denn
string wave
?

wieso string?

hirnfrei
02.06.2016, 15:03
Das kommt aus der Funktion snd_pcm_writei();. Die erwartet ein char *. Warum auch immer.

HaWe
02.06.2016, 15:11
dann mach doch auch
char * wave;
da gibts sonst immer so hässliche *WARNINGS* vom Compiler ;)

char ist allerdings komisch bei int16 (16bit encoding)

hirnfrei
02.06.2016, 15:36
Ich mag String aber lieber da ich mich da nicht um den Speicher kümmern muss. Zu meinen reinen Ansi-C Zeiten war das immer mein grösstes Problem. Hier mal zu wenig allociert, peng Absturz usw. Das macht String viel viel einfacher.

Und Warnungen stören mich wenig ;)

HaWe
02.06.2016, 15:59
wenn du deinen wave string gesäubert und entrauscht hast -
dann muss er ja in einen komplexen array kopiert werden (type casting (float), in reelen Teil, imaginärer bleibt leer == 0 ).
Geht das mit string genauso einfach wie mit char[] ?

und wieso jetzt char[] bei int16 (16bit encoding) ?

ps,
übrigens musst du dafür sowieso die array-Größe deines wave arrays festlegen, um es der FFT zu unterwerfen.
char (oder int16_t) wave[SHRT_MAX] // == 32767
wäre also eh die richtige Array-Größe, ohne \0 am Schluss wie bei strings.

hirnfrei
02.06.2016, 16:24
Du siehst ja, es kommt ein vector<int> bei der Aufnahme heraus. Was man damit dann letzten Endes macht ist ziemlich egal. Man muss es ja auch nicht mehr aus geben. Es geht ja darum das der Bot versteht was man von ihm will, nicht das er wiederholt was man ihm sagt.

Sagen wir so. Du brauchst für Spracherkennung letzten Endes nur das audioCapture();. Da kommen die Daten und dann wird damit gearbeitet. Da ich noch keinen Plan habe wie FFT überhaupt funktioniert kann ich dazu aber auch nichts sagen. Da du aber da schon Erfahrung hast, wie würdest du denn aus dem ankommenden vector<int> passende Daten für FFT machen?

Hast du eigentlich eine Idee wie man den Scheiss entrauschen kann?

HaWe
02.06.2016, 17:03
ich kenne mich mit vector <int> gar nicht aus, ich verwende grundsätzlich nur explizite C11 Datentypen
int16_t wavarray[SHRT_MAX]


für die komplexen Zahlen brauchst du die beiden floatarrays

float fx_[USHRT_MAX], fy_[USHRT_MAX]

die muss man erst auf Null setzen

memset(fx_ , 0, sizeof(fx_) ); // reel-Teil
memset(fy_ , 0, sizeof(fy_) ); // imaginär-Teil

dann die ints konvertieren und in die 1. Hälfte der fx_ arrays reinkopieren

for(int i=0; i<SHRT_MAX; ++i) fx_[i] = (float)wavarray[i]; // evtl sogar ein bisschen weniger, damit die 2. Hälfte überwiegt mit 0-Werten,

dann ist der ganze Rest der Komplex-Arrays schon auf Null, wie man es für die cross-correlation braucht.
Jetzt kann man die FFT darauf anwenden.

Bis dahin habe ich es gemacht, und dann abgebrochen, wegen irre langer Laufzeit auf dem NXT.

Übrigens:

für den Raspi würde ich double statt float nehmen, weil er intern auf 64bit double schneller rechnet als mit 32bit float.... 8-)

Die echte Cross-Correlation ist damit ebenfalls Neuland für mich!

entrauschen ist mein Lieblingsthema, das ist über Schwellwerte, Lowpass - und Highpass-Filter möglich, außerdem Spikes abfangen per gleitendem Medianfilter 8-)
(da gibt es aber bestimmt schon super Links dazu im www ;) )



ps,
allerdings geht NICHTS über ein sauber aufgenommenes original-Micro-Signal ! 8-)

hirnfrei
02.06.2016, 17:20
vector<int> ist nicht wirklich was anderes wie ein int array. Ich benutze vector sehr gerne da man dann einfach mit push_back() die nächste Variable dran hängen kann. Aber genau genommen ist es einfaches int.

Klar geht nichts über ein sauberes Mikro. Bliebe nur die Frage, was muss ich ändern damit das Mikro nicht rauscht?

HaWe
02.06.2016, 17:39
- ein wirklich gutes Micro mit der richtigen Impedanz verwenden ;)
- Aufnahmeparameter für den record-Teil optimieren (Empfindlichkeit, Aussteuerung)
- gucken, dass man bei Mono-Aufnahme auch nur die Mono-Spur bekommt zur Weiterverarbeitung (d.h. keine verrauschte ungenutze Stereo-Spur dabei ist)

- die Hintergrund-Umgebungsgeräusche kann man meist ja nicht beeinflussen...

Der Rest ist Signal Glättung mit Statistik-Filtern, da kann man selber experimentieren (s.o.) oder wirklich erstmal schnelle erprobte Lösungen im web suchen.


ps,
wir brauchen kein push-back, aber definierte Arrays mit definierter Größe, ich würde wirklich den definierten int16_t array vorziehen!

hirnfrei
02.06.2016, 18:37
Ich nehme mal an, die eigentliche Einstellungen für das Mikrofon muss ich über den Alsa-Mixer machen. Denke ich zumindest mal. Habe jetzt mal noch nicht gesehen wo ich das in meinem Programm beeinflussen können sollte. Mal schauen.

Was ich im Moment erkennen kann, es kommen Zahlen von 0 bis 255 an. Ich fange ich jetzt damit an? Bislang arbeite ich mit 1 Kanal. Sind das dann die Daten von nur diesem einen Kanal?

Was die Variablen angehen, es kommt also ein vector<int> zurück. Da kannst du jetzt alles draus machen was du machen willst.




int16_t waveArray[xxx];

for(i=0;i<input.size();i++)
{
waveArray[i] = input[i];
}


Mal als Beispiel. Hab das jetzt nicht versucht, aber dürfte so machbar sein.

- - - Aktualisiert - - -

So ich habe gebastelte!

Die neue Header Datei:



#include <alsa/asoundlib.h>

#include <iostream>
#include <vector>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

using namespace std;

typedef struct _FILE_head
{
unsigned char ID[4];
unsigned int Length;
unsigned char Type[4];
} FILE_head;

typedef struct _FORMAT
{
short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
} FORMAT;

typedef struct _CHUNK_head
{
unsigned char ID[4];
unsigned int Length;
} CHUNK_head;

snd_pcm_t *soundKarte;

bool Init(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

unsigned int resample = 1;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

err = snd_pcm_hw_params_set_rate_resample(soundKarte, hw_params, resample);

if(err < 0)
{
cout << "Init: Resampling kann nicht eingeschaltet werden " << snd_strerror(err) << endl;

return err;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}
snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool InitCapture(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_CAPTURE, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_CAPTURE, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}

snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool UnInit()
{
snd_pcm_close(soundKarte);

return true;
}

int playwave(string waveDatei, string name)
{
FORMAT format;
FILE_head head;
CHUNK_head chead;

char *wave;

register snd_pcm_uframes_t count, frames;

int datei;

unsigned int WaveSize;

datei = open(waveDatei.c_str(), 00);

read(datei, &head, sizeof(FILE_head));

read(datei, &chead, sizeof(CHUNK_head));

read(datei, &format, sizeof(FORMAT));

wave = (char *) malloc(head.Length);

read(datei, wave, head.Length);

WaveSize = head.Length * 8 / ((unsigned int)format.wBitsPerSample * (unsigned int)format.wChannels);

close(datei);

Init(name, format.wChannels, format.dwSamplesPerSec, format.wBitsPerSample);

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if (frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if (frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

free(wave);

UnInit();

return 0;
}

vector<int> audioCapture(int sek, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err, zielzeit;

char *puffer;

vector<int> input;

time_t t;

t = time(NULL);

zielzeit = t + sek;

puffer = (char *) malloc(1);

cout << "Beginne Aufnahme von " << sek << " Sekunden!" << endl;

if(InitCapture(name, channels, actualRate, WaveBits))
{
while(t < zielzeit)
{
err = snd_pcm_readi(soundKarte, puffer, 1);

if(err < 0) cout << "Fehler bei der Aufnahme!" << endl;

input.push_back(puffer[0]);

t = time(NULL);
}

UnInit();
}
else cout << "Bei der Initialisierung ist ein Fehler aufgetreten!" << endl;

cout << "Aufnahme beendet!" << endl;

return input;
}

void playCaptured(char *wave, unsigned int WaveSize, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
register snd_pcm_uframes_t count, frames;

Init(name, channels, actualRate, WaveBits);

WaveSize = WaveSize * 8 / WaveBits * channels;

count = 0;

// for(int i=0;i<WaveSize-2;i++) printf("%d/%d -> %d\n", i, WaveSize, wave[i]);

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if(frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if(frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

UnInit();
}


Die neue Main



#include <iostream>
#include <vector>

#include <stdio.h>

#include "diaSound.hpp"

int main()
{
vector<int> input;

unsigned int i;

char *wave;

input = audioCapture(5, "plughw:1,0", 1, 44100, 8);

wave = (char *) malloc(input.size());

for(i=0;i<input.size();i++)
{
wave[i] = input[i];
}

playCaptured(wave, input.size(), "plughw:1,0", 1, 44100, 8);

free(wave);

return 1;
}


Habe es jetzt extra für dich auf char * umgestellt ;).

Interessant ist, bei 16 Bit rauscht es ohne Ende. Bei 32 Bit kommt überhaupt kein Sound, bei 8 Bit ist leichtes Rauschen und man hört das was man in das Mikro rein spricht ^^. Wieder ein Punkt erledigt.

Jetzt geht es weiter!

HaWe
02.06.2016, 18:47
wie wav files kodiert sind, weiß ich nicht, ich habe ja nur mit Lautstärke-Schwankungen experimentiert.
Dass Werte von 0...255 ankommen wundert mich nicht, aber ich vermute, die sind gruppiert in int16-Blöcke (bei 16-bit-sampling), einmal rechter und einmal linker Kanal.

wenn du allerdings mit 8 bit sampelst, sind es sicher einzelne Bytes, nicht int16.

Bei ausschließlicher Verwendung von expliziten Datentypen hat man übrigens noch einen enormen Geschwindigkeitsvorteil
(int32_t ist auch hier beim Raspi schneller als int16_t).

sind nämlich sowohl wave als auch input vom selben Typ (switchen wir ruhig mal auf int32_t, das ist ja mit int auf ARMs meist identisch ...)
int32_t wave[FESTE_LAENGE], input[FESTE_LAENGE];

dann ist das hier nämlich -zigmal schneller als iterativ einzelne Zellen rüberzukopieren...:
memcpy(wave, input, sizeof(input) );

hirnfrei
02.06.2016, 19:11
Was mir aufgefallen ist, wenn das Mikro wenig oder gar keine Geräusche wahr nimmt ist der Wert in der Regel um die 128 herum. Gehe ich davon aus das es sich dabei so gesehen um die 0 Linie handelt?

Interessant dürfte aber auch sein, kann man mit einer Soundkarte auch wirklich von zwei Mikros Stereo aufnehmen? Das wäre bei einem Bot schon sinnvoll da man damit auch mehr oder weniger orten könnte von wo das Geräusch kommt.

HaWe
02.06.2016, 19:26
vielleicht kann wer anderes helfen?
Ich weiß auch nicht mehr als dieses hier:

https://de.wikipedia.org/wiki/RIFF_WAVE#.E2.80.9EData.E2.80.9C-Abschnitt

hirnfrei
02.06.2016, 20:54
Das kenne ich doch irgendwo her ;).

Ich werde nachher mal ein bisschen mit der Eingabe rum spielen. Lernen beim tuen oder wie das heisst ^^

- - - Aktualisiert - - -

Ich habe die Werte mal in einer CSV gespeichert und in LibreOffice geplottet. Sieht in der Tat so aus wie ich es erhofft hatte. Der grosse Ausschlag ist ein von mir gesprochenes Hallo und ich nehme an, diese dicke Linie davor und danach stellt das Rauschen dar. Ich denke das kann man raus filtern ;) und das Wort ist ja auch sehr gut zu erkennen finde ich.

HaWe
02.06.2016, 22:06
die FFT wird dir die Frequenzen sehr genau zeigen, die das Rauschen verursachen.
Dazu müsstest du den FFT-Plot von reinem Rauschen betrachten und dann den Plot, der in deinem Wort vorkommt.
Die Spikes, die in beiden in identischer Weise vorkommen, sind unspezifisch, tragen keine Information.
Ob man sie einfach isoliert aus dem FFT-Array herauslöschen kann, wäre ein Versuch wert: erzeugt man dann die FFT-Rücktransformation, müsste man nämlich ein bereinigtes Wave Signal hören können.

das funktioniert natürlich nur bei identischen, reproduzierbaren Rauschquellen/-Arten und ist daher kein allgemeiner Lösungsweg.
Aber immerhin kannst du die Rausch-Frequenz-Daten als Basis für statistische Rauschfilter verwenden wie ich sie oben genannt habe.

hirnfrei
02.06.2016, 23:10
Ich bin mir nur noch nicht ganz sicher, ob deine Idee den Input erst zu speichern eine gute Idee ist. Das Verzögert die Geschichte schon ganz Ordentlich!

Ich versuche als Nächstes mal, was passiert wenn ich nur Daten über 138 und unter 118 an die Ausgabe gebe. Das sollte auf jeden Fall die Geschichte auf das Wesentliche verkürzen.

Ein Gedanke der mir auch kam, letzten Endes muss sich das ja nicht mehr so anhören wie es original war. Solang es sich immer auf die selbe Art verändert sollte es ja egal sein. Hauptsache der Bot weiss was er damit anzufangen hat.

Nächste Frage, wie sehr ähneln sich stimmen? Wenn ich ein Wort spreche und er es erkennt. Würde er es auch bei dir erkennen?

HaWe
03.06.2016, 09:53
nein, Speichern muss nicht sein, es dient nur zum Debuggen / zur Verlaufskontrolle.
Später machst du den Vergleich "on the fly".
Aber das Anhören zwischendurch in der Entwicklungsphase ist schon wichtig, damit man weiss, was man verändert hat, ob es besser oder schlechter ist, und wo man jetzt steht.

"hallo Google" beweist, dass es in weiten Bereichen möglich ist, Spracherkennung auch Sprecher-unabhängig zu programmieren.
Die FT/FFT analysiert Schwingungen und Schwebungen als Summe mehrerer Grundfrequenzen (sinus/cosinus als Komplexe Funktionen), und wenn du die richtigen (niederfrequenten Sprach-/Wort/Laut-Grundfrequenzen analysierst und nicht die Stimm-Oberwellen, dann bist du sicher schon recht nah dran. Das klappte ja sogar mit dem Lego Lautstärkesensor ;)

Bei unserer "Spracherkennung" (zumindest wie ich es mir vorstelle) wird ja auch gar nicht ein Wort "richtig erkannt" (Laut für Laut, Pause für Pause), sondern es wir das am besten passendste unter (relativ wenigen) Mustern in der Datenbank gesucht. wenn also Sprecher A "ja " sagt und Sprecher "B" auch, dann wird aller Wahrscheinlicheit nach der Raspi nicht bei B ein "rückwärts" beim Mustervergleich herauslesen.
Aber es gibt nichts, was es nicht gibt, shit happens, siehe Telekom Sprachmenü ("...dann sagen Sie jetzt bitte 'ja'.... :-/ )

hirnfrei
03.06.2016, 10:52
Na ja, ich habe schon vor das der Bot möglichst viel versteht, vielleicht sogar lernt (das wird aber die Zukunft zeigen, das mache ich davon abhängig wie gut es funktioniert). Also lernen nicht wie ein Mensch sondern eher banal. Wenn er ein Wort nicht erkennt das man es dann rein speichern kann.

Aber erst einmal schauen wie ich das eingehende Signal verbessern kann. Theoretisch stört es nicht so viel das Rauschen. Also das Hallo kann man schon absolut gut erkennen. Aber irgendwie muss ich ihm ja bei bringen was ein Wort und was eine Pause ist. Ich denke, wenn das soweit klappt können wir uns dran machen das in ein Format zu bringen das FT versteht.

HaWe
03.06.2016, 12:26
Aber irgendwie muss ich ihm ja bei bringen was ein Wort und was eine Pause ist.
bring ihm erstmal einzelne Wörter oder fixe Wortgruppen bei, und die dann sicher zu erkennen.
Auch das Lernen neuer Muster kommt erst im 2. Schritt und ist nur eine Frage des bedien-Interfaces.

Alles andere ist für den Anfang zu kompliziert.

hirnfrei
03.06.2016, 13:31
Nichts desto weniger sollte er erkennen was ein Wort ist und was eine Pause. Denn wie man an meinem Beispiel ja gesehen hat da ist lange vor und hinten dran nichts. Er sollte schon erkennen was relevant ist und was nicht.

HaWe
03.06.2016, 15:01
glaub mir, das ist erstmal irrelevant. Ich habe lange genug Zeit mit Mustererkennung, statistischen und stochastischen Filtern und lernfähigen Neuronalen Netzen gearbeitet, um zu wissen, was jetzt vorrangig ist.
Erst muss er ein Muster im ganzen erkennen können.
Ob du in einem Muster dann später eine Pausenerkennung vorschaltest und dann in Unter-Muster aufteilst, ist für das grundsätzliche Problem ohne Belang.

Ich will auch gerne weiter dranbleiben mit FFT und cross-correlation, aber dazu ist GANZ ENORM WICHTIG für die Filter zur Signal-Verarbeitung:
Bitte keine Vectoren von variabler Größe, sondern ausschließlich arrays mit fixer Größe.
Was nicht reinpasst vom Sound, muss abgeschnitten werden, und was übrig bleibt, mit Nullen aufgefüllt.
Das gilt sowohl für alle char-arrays als auch für alle int- und float (besser: double) arrays, und bitte alle in expliziten C11-Datentypen, nicht char, nicht int, sondern uint8_t und int32_t (ist besser als int16_t).

Wenn man das nicht macht, funktioniert das nicht mit Filtern und Transformationen, so wie ich es vorhabe und auch schon z.T. gemacht habe - wenn du die Vektoren behältst, muss ich leider aussteigen.

Die Dimensionen habe ich dir ja bereits hier genannt:



int32_t wavarray[SHRT_MAX];
int32_t input[SHRT_MAX];

uint8_t wave[SHRT_MAX];

double fx_[USHRT_MAX], // FFT reell-Teil
fy_[USHRT_MAX]; // FFT imaginär-Teil

es muss genau so und nicht anders sein, damit man die FFT-Dimensionen und die wav-Dimensionen zueinander passen und die FFT keinen Unsinn transformiert.

ps,
er muss den Start eines Signals (Worts) erkennen, und das geht über ein Lautstärke-Bias:
er fängt erst an, aufzunehmen bzw. das wav zu schreiben, wenn die Lautstärke über einer bestimmten Grenze ist.
Das Signal wird dann über einen Threshold "geschnitten" (cut vorher, cut nachher).


Wie sieht denn jetzt ein wav File aus?
Kannst du die reinen wav Schwingungs-Daten daraus isolieren und in den wavarray packen?

hirnfrei
03.06.2016, 17:42
Dann nenn mir mal ein sinnvolles SHRT_MAX. Der Rest sollte ja nicht so schwer sein.

Ich nehme an



int32_t input[SHRT_MAX];


ist das was das Mikro aufgenommen hat?

- - - Aktualisiert - - -

Habe ich noch nicht versucht, sollte aber funktionieren, denn die Daten die ich abspiele sind letzten Endes die Gleichen wie die die ich vom Mikro aus abspiele.

Genau genommen ist ja der Unterschied zwischen playWave und playCaptured nur der, dass man anstatt einer Datei die puren Daten übergibt und die Werte für Bit, Frequenz usw. vor gibt, während bei playWave die Daten aus den Headern gelesen werden und dann aber die selben Daten an die Ausgabefunktion übergeben werden. Also das kann ich machen, das sollte kein Problem sein.

HaWe
03.06.2016, 18:14
das ist das input, das ich u.a. hier gefunden habe:

vector<int> audioCapture(int sek, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err, zielzeit;
char *puffer;
vector<int> input;


was du intern nur in den Funktionen verwendest, ist für mich natürlich nicht wichtig, aber alles was weiterverarbeitet wird (gefiltert, extrahiert, geglättet, geschnitten, transformiert) muss feste array-Größen haben.

Das Ziel ist ja (ich hatte es schon erwähnt):

du hast aufgenommenene Rohdaten (die können länger oder kürzer sein als 2^15 Werte oder "packages") als bytes (strings) oder ints, keine Ahnung,
die müssen in einen int32_t array überführt werden, wo sie nur max. 32767==SHRT_MAX Zellen belegen dürfen
(sample rate 11-12kHz, also ca. 3 sec.),
dann kommt der double-Array von doppelter Länge (65535==USHRT_MAX) ins Spiel, dessen (mindestens) 2. Hälfte mit Nullen aufgefüllt wird.

Länger als 3 sec dürfen also die gesprochenen Kommandos nicht werden (egal ob 1 oder mehrere Wörter)


wav enthält dann im "Roh-array" 2 Kanäle, und wir verarbeiten aber nur 1 Kanal, also muss jedes 2. Datenpaket raus geschmissen werden.
Es sind dann wohl 16-bit ints, oder wie ist das, das ist schließlich wichtig, denn das muss ja exakt in floats (double) überführt werden.
Also immer 2 bytes als int16 lesen und in den Bearbeitungs-Array als int32 kopieren (li Kanal), dann 2 bytes verwerfen (re Kanal), dann wieder 2 lesen+kopieren (li Kanal) usw. ?
Oder andersrum? Oder andere Datenblöcke? Da kommts jetzt drauf an.

hirnfrei
03.06.2016, 18:18
So ich habe dann etwas rum gebastelt. Jetzt nimmt er genau SHRT_MAX auf. Hast recht, bei 12Khz sind es um die 3 Sekunden, rauscht dafür aber auch mehr.



#include <alsa/asoundlib.h>

#include <iostream>
#include <vector>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

using namespace std;

typedef struct _FILE_head
{
unsigned char ID[4];
unsigned int Length;
unsigned char Type[4];
} FILE_head;

typedef struct _FORMAT
{
short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
} FORMAT;

typedef struct _CHUNK_head
{
unsigned char ID[4];
unsigned int Length;
} CHUNK_head;

snd_pcm_t *soundKarte;

bool Init(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

unsigned int resample = 1;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

err = snd_pcm_hw_params_set_rate_resample(soundKarte, hw_params, resample);

if(err < 0)
{
cout << "Init: Resampling kann nicht eingeschaltet werden " << snd_strerror(err) << endl;

return err;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}
snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool InitCapture(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_CAPTURE, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_CAPTURE, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}

snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool UnInit()
{
snd_pcm_close(soundKarte);

return true;
}

int playwave(string waveDatei, string name)
{
FORMAT format;
FILE_head head;
CHUNK_head chead;

char *wave;

register snd_pcm_uframes_t count, frames;

int datei;

unsigned int WaveSize;

datei = open(waveDatei.c_str(), 00);

read(datei, &head, sizeof(FILE_head));

read(datei, &chead, sizeof(CHUNK_head));

read(datei, &format, sizeof(FORMAT));

wave = (char *) malloc(head.Length);

read(datei, wave, head.Length);

WaveSize = head.Length * 8 / ((unsigned int)format.wBitsPerSample * (unsigned int)format.wChannels);

close(datei);

Init(name, format.wChannels, format.dwSamplesPerSec, format.wBitsPerSample);

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if (frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if (frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

free(wave);

UnInit();

return 0;
}

vector<int> audioCapture(int SHRT_MAX, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err, i=0;

char *puffer;

vector<int> input;

puffer = (char *) malloc(1);

cout << "Beginne Aufnahme" << endl;

if(InitCapture(name, channels, actualRate, WaveBits))
{
while(i < SHRT_MAX)
{
err = snd_pcm_readi(soundKarte, puffer, 1);

if(err < 0) cout << "Fehler bei der Aufnahme!" << endl;

input.push_back(puffer[0]);

i++;
}

free(puffer);

UnInit();
}
else cout << "Bei der Initialisierung ist ein Fehler aufgetreten!" << endl;

cout << "Aufnahme beendet!" << endl;

return input;
}

void playCaptured(char *wave, unsigned int WaveSize, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
register snd_pcm_uframes_t count, frames;

Init(name, channels, actualRate, WaveBits);

WaveSize = WaveSize * 8 / WaveBits * channels;

count = 0;

// for(int i=0;i<WaveSize-2;i++) printf("%d/%d -> %d\n", i, WaveSize, wave[i]);

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if(frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if(frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

UnInit();
}


Das ist der neue Header und hier kommt das neue main:



#include <iostream>
#include <vector>
#include <fstream>

#include <stdio.h>

#include "diaSound.hpp"

bool debug = false;

int main()
{
fstream datei;

vector<int> input;

unsigned int i, SHRT_MAX = 32767;

char *wave;

input = audioCapture(SHRT_MAX, "plughw:1,0", 1, 12000, 8);

wave = (char *) malloc(input.size());

if(debug) datei.open("test.csv", ios::out);

for(i=0;i<input.size();i++)
{
wave[i] = input[i];
if(debug) datei << input[i] << endl;
}

if(debug) datei.close();

playCaptured(wave, input.size(), "plughw:1,0", 1, 12000, 8);

free(wave);

return 1;
}


Zufrieden soweit?

HaWe
03.06.2016, 18:30
stelle gerade fest:

die float (double) arrays müssen USHRT_MAX+1 groß sein ( == exakt 2^16 ),
für SHRT_MAX soll es so bleiben.

SHRT_MAX und USHRT_MAX sind C-Konstanten, die brauchst du nicht mehr definieren
(oder wir verwenden explizit 32767, 65536)

http://www.cplusplus.com/reference/climits/


und bitte auch möglichst nirgends int oder unsigned int verwenden, immer nur int32_t und uint32_t

das gleiche gilt für char und unsigned char (stattdessen möglichst int8_t, uint8_t)



in main steht immer noch

vector<int> input;

das müsste dann auch zu
int32_t input[SHRT_MAX]
werden

hirnfrei
03.06.2016, 18:35
Und nochmal bisschen umgestrickt.



#include <iostream>
#include <vector>
#include <fstream>

#include <stdio.h>

#include "diaSound.hpp"

bool debug = false;

int main()
{
fstream datei;

vector<int> input;

unsigned int i, SHRT_MAX = 32767;

char *wave;

int32_t inputWave[SHRT_MAX];

input = audioCapture(SHRT_MAX, "plughw:1,0", 1, 12000, 8);

wave = (char *) malloc(input.size());

if(debug) datei.open("test.csv", ios::out);

for(i=0;i<input.size();i++)
{
wave[i] = input[i];

inputWave[i] = input[i];
if(debug) datei << input[i] << endl;
}

if(debug) datei.close();

playCaptured(wave, input.size(), "plughw:1,0", 1, 12000, 8);

free(wave);

return 1;
}


Nun liegt also ein Array als int32_t vor mit exakt der Länge SHRT_MAX (wenn ich SHRT_MAX nicht deklariere bekomme ich eine Fehlermeldung! Fehlt mir da eine Headerdatei?).


und bitte auch möglichst nirgends int oder unsigned int verwenden, immer nur int32_t und uint32_t

das gleiche gilt für char und unsigned char (stattdessen möglichst int8_t, uint8_t)

Was hat das für einen Vorteil?

HaWe
03.06.2016, 19:43
int ist von der Größe her nicht festgelegt (compiler- und Zielplattform-abhängig, manchmal 16bit, manchmal 32bit (z.Zt Raspi 2) und manchmal 64bit), und char ist auf manchen Plattformen signed und auf anderen unsigned.
int32_t ist aber auf allen Plattformen identisch groß und int8_t überall signed.
(Ntl kannst du überall vor jedes char auch ein signed davorschreiben, aber das ist wieder unnötige Schreiberei - die int Länge allerdings ist damit immer noch nicht von identischer Länge -
wir dürfen nicht vergessen:
ich habe einen 32bit Pi 2, aber du hast einen 64bit Pi 3...!
(und wer weiß, ob wir den Code mal auf nem Raspi4 mit Win10-2020 verwenden werden...? ;) )

die limits stehen in <limits.h>
aber auch da bin ich fast schon wieder bei den echten Zahlen 32767 und 65536, auch wegen künftiger Portierbarkeit.

es stellt sich auch die Frage, ob wir lieber Geschwindigkeit wollen oder Speicher sparen:
32767 int16 sind etwas langsamer, aber ein schnellerer 32767er array von int32_t ist schon ein Wort.
Noch interessanter wirds dann bei 65536er arrays von float vs. double ...

naja, wir haben ja 1 GB RAM (hatte mich grade schon verrechnet... ;) )

hirnfrei
03.06.2016, 20:19
Du stellst einen schon vor Herausforderungen! Denn Seit ich von char * auf int32_t * umgestellt habe schmiert das Programm sofort bei Aufruf von playCaptured ab. Da werde ich also bei char * bleiben. Das dürfte aber letzten Endes egal sein, denn solltest du das irgendwann mal auf Windows zum Laufen bringen wollen wirst du eh für Aufnahme und Ausgabe andere Funktionen brauchen.

ps:

Die Chance das ich irgendwann mal freiwillig mit Windows arbeite halte ich für sehr fraglich ;).

HaWe
03.06.2016, 20:34
zu den ints: du wirst (irgendwann) 64-bit ints haben (Pi 3 = 64bit µC), ich aber weiterhin 32bit (Pi 2 = 32bit µC):
dann stimmen die Arraygrößen auf unseren System nicht mehr überein.

zu den char[] arrays:
ich weiß nicht, was dein char[] array macht bzw was drin steht.
Wenn die Aufnahmefunktion die Rohdaten in char[] speichern muss, muss man es dort erst so lassen, dann aber als nächstes in int32 arrays umkopieren, bevor man weitermacht.

Aber dazu muss man wissen, wie die Datenpakete pro Einzelpaket kodiert sind.
sind es immer 4 bytes, erst 2 bytes für li, dann 2 bytes für rechts?

hirnfrei
03.06.2016, 21:38
audioCapture() läuft schon auf int32_t. So kommen auch die Daten an, eben als Vector, das ändert aber auch an der Grösse nichts.

was mit playCaptured ist verstehe ich nicht. Das stürzt sofort ab wenn ich es aufrufe! In die Funktion kommt es gar nicht. Das stürzt beim Aufrufen schon ab.

HaWe
04.06.2016, 10:40
gings denn vorher mit playCaptured?

zum Daten-Record und Aufbereiten -
vielleicht hast du mich falsch verstanden, und ich bin ja nicht auf denem Programmier-Level:

Ich hatte mir vorgestellt, dass anstelle von
string oder char* wasauchimmer
jetzt
uint8_t (!!) wasauchimmer (nicht int32_t !!)
verwendet werden soll, aber mit festen array Grenzen;

das gleiche gilt für vector<int> input etc,
stattdesen
int32_t input[FESTE_GROESSE]


Das hätte den Sinn, dass man es immer mit identisch großen records zu run hat, was das Aufbereiten der Daten vereinfacht.
Wenn du lieber erst mit Vektoren variabler Größe arbeitest, auch ok, dann muss es im nächsten Schritt eben in arrays von konstanter Größe verfrachtet werden, und zwar nur MONO (1 Kanal).

Weil es nur MONO sein darf, muss man wissen, welche der ganzen Bytes genau die richtigen Töne darstellen, ohne die andere Spur mitzukopieren.

Die konstante Größe ist wichtig, denn wie du sicher weißt, arbeitet ja die FFT nur mit arrays, die eine Länge haben, die sich als 2^n (hier: 2^16 == 65536) darstellen lässt.

Für die Cross Correlation darf dafür nur höchstens die erste Hälfte mit Daten gefüllt sein, mindestens die 2. Hälfte ausschließlich mit Nullen.

Daher bleibt für Daten dann maximal ((2^n)/2) -1 (== 2^15 -1 == 32767 ) übrig, und das muss die exakte Länge der int32-MONO-Records sein, die man erst cutted und filtert und dann (zu floats konvertiert) der FFT übergibt.


Wann du also anfängst mit fixen arrays, ist prinzipiell egal, aber Vektoren taugen dazu nicht, und daher muss man vektoren auch erst wieder in fixe arrays umwandeln, bevor man mit der eigentlichen Arbeit anfangen kann, und daher: je früher, je besser.

Ist dir klar, wie ich das meine?

hirnfrei
04.06.2016, 17:47
Ich nehm es doch mal an ^^.

Ich bastel das nachher um. Allerdings kann es ein wenig dauern. Ich musste vorhin zum Essen meinen Bot (Also die Holzplatte auf dem die Platinen drauf geschraubt sind ^^) vom Tisch runter holen, bin hängen geblieben und hab mir da Kabel abgerissen. Demnach hat der arme Raspi zur Zeit keinen Strom :(. Sobald das geregelt ist baue ich das Programm nochmal etwas um.

Mir kam da aber auch ein Gedanke. Du sprachst davon, die Aufnahme soll immer nur dann beginnen wenn eine gewisse Lautstärke überschritten ist. Da kam mir der Gedanke, ich habe noch ein Soundmodul für den Arduino. Das ist ja dafür da bei einer gewissen Lautstärke ein Signal zu geben. Das könnte man da ja für nutzen um das Ganze zu Automatisieren. Derzeit ist es ja so, das Programm startet, nimmt sofort auf und fertig. Mit dem Teil könnte mein Aruduino ja ein Signal an den Raspi schicken, die Lautstärke ist überschritten und der nimmt dann automatisch die feste Grösse lang auf.

- - - Aktualisiert - - -

So, der gute Raspi hat wieder Saft und wie versprochen hier eine char und vector bereinigte Main



#include <iostream>
#include <vector>
#include <fstream>

#include <stdio.h>
#include <limits.h>

#include "diaSound.hpp"

bool debug = false;

int main()
{
fstream datei;

int32_t input[SHRT_MAX];

int i;

uint8_t *wave;

audioCapture(input, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

wave = (uint8_t *) malloc(SHRT_MAX+1);

if(debug) datei.open("test.csv", ios::out);

for(i=0;i<SHRT_MAX;i++)
{
wave[i] = input[i];

cout << i << " -> " << input[i] << endl;

if(debug) datei << input[i] << endl;
}

if(debug) datei.close();

playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

free(wave);

return 1;
}


und die Header dazu



#include <alsa/asoundlib.h>

#include <iostream>
#include <vector>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

using namespace std;

typedef struct _FILE_head
{
unsigned char ID[4];
unsigned int Length;
unsigned char Type[4];
} FILE_head;

typedef struct _FORMAT
{
short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
} FORMAT;

typedef struct _CHUNK_head
{
unsigned char ID[4];
unsigned int Length;
} CHUNK_head;

snd_pcm_t *soundKarte;

bool Init(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

unsigned int resample = 1;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

err = snd_pcm_hw_params_set_rate_resample(soundKarte, hw_params, resample);

if(err < 0)
{
cout << "Init: Resampling kann nicht eingeschaltet werden " << snd_strerror(err) << endl;

return err;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}
snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool InitCapture(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_CAPTURE, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_CAPTURE, 0);
}

if(err < 0)
{
cout << "Init: Kann die Soundkarte nicht öffnen! " << name << " (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
cout << "Init: Parameter können nicht initialisiert werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameter können nicht ermittelt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "Init: Zugriffstyp kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
cout << "Init: Sample-Format kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
cout << "Init: Anzahl der Kanäle kann nicht gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
cout << "Init: Sample-Rate kann nicht auf " << actualRate << " gesetzt werden (" << snd_strerror (err) << ")" << endl;

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
cout << "Init: Parameters können nicht gesetzt werden(" << snd_strerror (err) << ")" << endl;

return false;
}

snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
cout << "Init: Audio kann nicht zur Nutzung vorbereitet werden (" << snd_strerror (err) << ")" << endl;

return false;
}

return true;
}

bool UnInit()
{
snd_pcm_close(soundKarte);

return true;
}

int playwave(string waveDatei, string name)
{
FORMAT format;
FILE_head head;
CHUNK_head chead;

char *wave;

register snd_pcm_uframes_t count, frames;

int datei;

unsigned int WaveSize;

datei = open(waveDatei.c_str(), 00);

read(datei, &head, sizeof(FILE_head));

read(datei, &chead, sizeof(CHUNK_head));

read(datei, &format, sizeof(FORMAT));

wave = (char *) malloc(head.Length);

read(datei, wave, head.Length);

WaveSize = head.Length * 8 / ((unsigned int)format.wBitsPerSample * (unsigned int)format.wChannels);

close(datei);

Init(name, format.wChannels, format.dwSamplesPerSec, format.wBitsPerSample);

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if (frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if (frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

free(wave);

UnInit();

return 0;
}

void audioCapture(int32_t *input, int max, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err, i=0;

int32_t *puffer;

puffer = (int32_t *) malloc(1);

cout << "Beginne Aufnahme" << endl;

if(InitCapture(name, channels, actualRate, WaveBits))
{
while(i < max)
{
err = snd_pcm_readi(soundKarte, puffer, 1);

if(err < 0) cout << "Fehler bei der Aufnahme!" << endl;

input[i] = puffer[0];

i++;
}

free(puffer);

UnInit();
}
else cout << "Bei der Initialisierung ist ein Fehler aufgetreten!" << endl;

cout << "Aufnahme beendet!" << endl;
}

void playCaptured(uint8_t *wave, unsigned int WaveSize, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
register snd_pcm_uframes_t count, frames;

Init(name, channels, actualRate, WaveBits);

WaveSize = WaveSize * 8 / WaveBits * channels;

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if(frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if(frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

UnInit();
}


Zudem alles auf entsprechende Grösse SHRT_MAX ausgerichtet.

Und jetzt?

HaWe
04.06.2016, 17:57
ok, jetzt muss ich mir ein Bild von den wav-audio-Daten machen.
in welchem array stehen jetzt die wav-Roh-Daten, die du vom Micro aufgenommen hast, und die jetzt weiterverarbeitet werden sollen?

ist es

in32_t input[SHRT_MAX]

und enthält er noch beide Spuren (li+re) ?

hirnfrei
04.06.2016, 18:11
Richtig.

input32_t input[SHRT_MAX];

ist das was vom Mikro kommt. Direkt in diese Variable. Also da wird nicht erst noch was konvertiert oder so.

Und zu besseren Verständnis, wie die audioCapture() funktioniert hier ne kleine Erklärung:



audioCapture(input, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

audoCapture("Variable zum Speichern der Daten", "Aufzeichnungslänge", "Soundkarte", "Kanäle", "Frequenz", "Bit");


Ich jammere rum weil tinyalsa keine gute Dokumentation hat und biete dann für meine Funktion ebenfalls keine Erklärung. Tztztztz.

Wie du siehst, ich nehme nur mit einem Kanal auf.

Okay, habe versucht eine CSV hoch zu laden, hat natürlich nicht geklappt :(. Versuch es mal hier http://www.kipperei.de/test.csv

HaWe
04.06.2016, 18:19
ok, hab ich mir angeschaut.
Es sind jetzt alles Zahlen in Byte-Größe.

Betrachten wir jetzt mal diesen Array input, N == SHRTR_MAX Zellen mit jeweils 4 Bytes:


|----,----,----,----,----,----,----,----,----,----,---- .... ----,----,----,----,----,----,----,----,----,----,----|
0 1 2 3 4 5 6 7 8 9 10 .... N=SHRT-MAX-1

wie ist jetzt jede 4er-Bytes-Gruppe aufgebaut?

hirnfrei
04.06.2016, 18:27
Wie meinst du das?

Normalerweise 3 Zeichen und ein Terminationszeichen.

HaWe
04.06.2016, 18:30
was für zeichen an welcher Array-Stelle?
Wir brauchen lowByte und HighByte des Ton-Peaks, oder etwas, was wir dazu umwandeln können

oder hast du in 8 bit gesampelt?

dann brauchen wir die byte-Nr-Stelle, wo dieses 8bit-Tonsignal steht.
Immer im LowByte des int32-Zellenwertes?

hirnfrei
04.06.2016, 18:41
Siehe meine Erklärung. Ja ich habe in 8-Bit gesampelt.


dann brauchen wir die byte-Nr-Stelle, wo dieses 8bit-Tonsignal steht.
Immer im LowByte des int32-Zellenwertes?

Was das angeht, da stehe ich gerade wie ein Esel und warte auf Heu :(

HaWe
04.06.2016, 18:46
du meintest doch:
3 Zeichen und ein Trennzeichen.

ich verstehe das als
3 Bytes und 1 Trennzeichen, immer bezogen auf jede int32-Array-Zelle.

Meintest du das so?

dann enthält ja nur 1 Byte wirklich den Tonpeak, der Rest ist für uns nicht zu gebrauchen.

in welcher Reihenfolge steht jetzt also was drin:

1.Byte=
2.Byte=
3.Byte=
4.Byte=Trennzeichen

oder beziehen sich deine "3 Zeichen" auf was anderes?

hirnfrei
04.06.2016, 19:01
Es mag sein das ich mich irre, aber ich denke zum Beispeil der Mittelwert wäre

Byte 0 = 1
Byte 1 = 2
Byte 2 = 8
Byte 3 = \n

HaWe
04.06.2016, 19:35
wie ? die Dezimalstellen in einzelne Bytes gepackt?
Das kann ich nicht gauben.
Zahlen von 0-255 passen doch in 1 Byte, da müssen doch keine ASCII Ziffern mit #10 oder #13 als Trennzeichen einzeln abgespeichert weden...?


das fällt mir schwer zu glauben.

ich würde eher so etwas erwarten (es sind ja unsigned char (0...255) , und die Null ist ja als ein echter Messwert möglich:

Byte 0 = linker Kanal 0...254
Byte 1 = rechter Kanal 0...254
Byte 2 = ??? was könnte das sein ???
Byte 3 = 255 als Trennzeichen

aber das muss man jetzt wirklich 100%ig wissen, bevor man weitermacht.

hirnfrei
04.06.2016, 19:50
Ich habe stark den Verdacht, bei uns hat sich ein Kommunikationsproblem eingeschlichen.

Die Datei wo ich dir geschickt habe enthält exakt das was ich vom Mikro bekomme. Entsprechend kann ich es auch genau auf diese Art auch wieder aus gebe. Was da jetzt wie wo wann welcher Kanal ist kann ich dir so gar nicht sagen :(

- - - Aktualisiert - - -

Die Alsa Funktion sieht so aus:



snd_pcm_sframes_t snd_pcm_readi ( snd_pcm_t * pcm,
void * buffer,
snd_pcm_uframes_t size
)

Read interleaved frames from a PCM.

Parameters:
pcm PCM handle
buffer frames containing buffer
size frames to be read

Returns:
a positive number of frames actually read otherwise a negative error code

Return values:
-EBADFD PCM is not in the right state (SND_PCM_STATE_PREPARED or SND_PCM_STATE_RUNNING)
-EPIPE an overrun occurred
-ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)

If the blocking behaviour was selected and it is running, then routine waits until all requested frames are filled. The returned number of frames can be less only if a signal or underrun occurred.

If the non-blocking behaviour is selected, then routine doesn't wait at all.


Vielleicht sagt dir das mehr.

HaWe
04.06.2016, 20:08
nein, da muss ich passen, ich verstehe von diesem low level Kram nichts ;)

was wir brauchen ist eine Serie von Sounddaten für nur 1 Micro-Kanal, ohne Header und ohne Trennzeichen.

das muss in einen
int32_t wavbuffer[SHRT_MAX]
rein

Da alles bei 8bit nur 1 Datenbyte ist, passt alles ins lowbyte des wavbuffers, also (angenommen Mono==links)
( alle 4 Bytes einer jeden int32 Arrayzelle einzeln betrachtet) :

0 0 0 128, 0 0 0 129, 0 0 0 129, 0 0 0 128, 0 0 0 127, 0 0 0 132, 0 0 0 134, .... (Phantasiewerte)
(betrachtet in Big Endian Schreibweise)

usw. bis alle echten einzelnen Microwerte in diesem Array drin sind, ohne Steuerdaten.

Kriegst du das hin?

hirnfrei
04.06.2016, 20:11
Ich muss also jede Zahl, die vom Mikro kommt, in ein Bit vom int32_t array schreiben?

HaWe
04.06.2016, 20:24
nein.
nicht "jede Zahl", sondern nur den spezifischen Micro-Sound-Byte-Wert für nur 1 Ton-Spur.

der Micro-Wert muss ein Byte sein (0...255),
und gespeichert wird genau dieses Byte als int32_t Wert in einem Array.

Also nochmal:
wo in deiner input-array-Zelle ist dieser spezifische Micro-Sound-Byte-Wert zu finden?

hirnfrei
04.06.2016, 20:24
Ich bin gerade komplett durcheinander. Eigentlich müsste jedes input[i] ein solcher Byte-Wert sein, da ja jedes Frame das ankommt in ein neues Feld vom Array geschrieben wird.

Oder bin ich gerade zu dämlich um dich zu verstehen :-(

HaWe
04.06.2016, 20:27
ich weiß nicht mehr, wie ich es erklären soll,
und ich wiederum verstehe nicht, wie was wo gespeichert wird, weil ich ja nichts vom wav file Format verstehe.

Erklär doch einfach mal, welche Werte in jedem einzelnen der rund 32000 int32-Zellen (alle 4 bytes einzeln) deines input Arrays genau stehen.

linke Spur
rechte Spur
was noch
was noch

und welche Spur ist die wesentliche Micro-Spur oder wie kann man sie als Bytes extrahieren?

hirnfrei
04.06.2016, 20:37
Na genau das was vom Mikro kommt.



snd_pcm_readi(soundKarte, puffer, 1);


Die funktion nutze ich um die Daten vom Mikrofon zu bekommen.



int32_t *puffer;

puffer = (int32_t *) malloc(1);


Das ist der Puffer in den die ankommenden Werte geschrieben werden. Diese werden dann in das aktuelle Feld des Arrays gespeichert.



while(i < max)
{
err = snd_pcm_readi(soundKarte, puffer, 1);

input[i] = puffer[0];

i++;
}


max ist in dem Fall natürlich SHRT_MAX und input ein int32_t array.

Mehr mache ich da nicht.

HaWe
04.06.2016, 20:39
kapier ich nicht, ich verstehe den Code nicht.

also nochmal:
wenn es einzelne byte-(8bit-)Werte sind, die vom Micro kommen, wo ist die linke Spur, wo ist die rechte Spur, und was steht sonst noch wo im input Puffer?

hirnfrei
04.06.2016, 20:50
Das kann ich dir nicht sagen. Ich sehe im Puffer genau so viel wie du, denn das csv ist exakt das was ich vom puffer aus kopiere.

Ich stelle es mal aus Spas auf zwei Kanäle um und schaue was sich da ändert.

- - - Aktualisiert - - -

Okay das sieht dann völlig komisch aus :(

http://www.kipperei.de/test.csv

HaWe
04.06.2016, 20:51
nein, mach mal folgendes:

for(int i=0; i<SHRT_MAX, ++i) input[i]=input[i] & 0x00ff;

und dann guck, ob du es immer noch abspielen kannst!

(ps, dein letzter Link ist kaputt)

hirnfrei
04.06.2016, 21:13
Habe ich gemacht, ja kann ich noch problemlos abspielen!

und der Link geht jetzt auch. Hatte mich vertippt.

- - - Aktualisiert - - -

Nachdem was ich bis jetzt so gelesen habe kommen bei Stereo die Daten immer abwechselnd. Erst Links dann Rechts.

- - - Aktualisiert - - -

Hast du das schon gesehen? http://www.fftw.org/

HaWe
04.06.2016, 21:14
ja, das mit Stereo weiß ich, die Frage ist: alle in derselben int32 Zelle oder in 2 aufeinanderfolgenden?

aber das ist jetzt nicht mehr wichtig.

Wir wissen jetzt:
die Sound-Daten im Input-Array entsprechen ganz exakt den Micro-sound(wav)-Schwingungsdaten, die wir weiterverarbeiten können.

also speichern wir jetzt am besten input in einem wavbuffer (um input zu schonen):

int32_t wavbuffer[SHRT_MAX];
memcpy(wavbuffer, input, sizeof(input) );
for(int i=0; i<SHRT_MAX, ++i) wavbuffer[i] &= 0x00ff;


wav buffer müssen wir jetzt analysieren:

Bandbreite der Werte/Dynamik:
min-Wert=?
max-Wert=?






bis wo geht der Vorspann,
d.h. wo fängt das eigentliche Wort an,
und wo beginnt der Nachspann, d.h. wo hört das Wort auf.

dann:
wie groß ist die Schwingungsbreite (Dynamik) im Vor/Nachspann?
126-130? oder größer?

hirnfrei
04.06.2016, 21:21
Warum ein neues Array anlegen? Können wir nicht das nutzen was wir schon haben?

min-Wert ist 0
max-Wert ist 255

Ja die Schwinungsbreite von vor und nach müsste so zwischen 120 und 132 liegen.

Wo nun vor und nachspann anfangen, wie ich das kläre weiss ich nicht.

HaWe
04.06.2016, 21:43
Mist, der blöde Editor hat mich rausgeschmissen...

nein, ich will das Original behalten, zur Sicherheit, deshalb zusätzlicher wavbuffer.

max-Wert und Minwert htte ich grade gepostet, wurde aber von dem blöden editor gelöscht weil mich das Forum ausgeloggt hat. Abrer das kannst du selber.... ;)

- - - Aktualisiert - - -

hierfür eine Funktion mit Rückgabewert anlegen:

maximum = wavbuffer[0];
int32_t maxpos;
for (i = 1; i < size; i++)
{
if (wavbuffer[i] > maximum)
{
maximum = wavbuffer[i];
maxpos= i+1;
}
}

Minimum analog

- - - Aktualisiert - - -

wir müssten für Vorspann und Nachspann wissen, ob wirklich immer einer existiert, oder ob alles der komplette aufgenommene Sound ist.
Nur wenn künftig überall Vor-oder Nachspann vorkommen, kann man das also analysieren und verwenden

- - - Aktualisiert - - -

wenn wir aufpassen, dass unsere Kommandos immer kürzer sind als die etwa 3 sec., kann man die Dynamik der letzten 100 Zellen verwenden (~10 ms):

minr = minimum der letzten 100 zellen (optimiert: ebenfalls zu heftige Spikes nach unten rausschneiden)
maxr = maximum der letzten 100 zellen (optimiert: ebenfalls zu heftige Spikes nach oben rausschneiden)

baseline = arithmet. Mittel der letzten 100 zellen (baseline)

bias = min(baseline-minr, maxr-baseline); // <=== Bandbreite um Mittelwert

- - - Aktualisiert - - -

wenn wir dann die Ober- und Untergrenzen des Grundrauschens kennen, können wir das bias abschneiden:


for(int i=0; i<SHRT_MAX; ++i) {
if( (wavbuffer[i]>baseline ) && (wavbuffer[i]< baseline + bias)) wavbuffer[i] = baseline ; // kleine Schwankung => auf Baseline
else
if( (wavbuffer[i]<baseline ) && (wavbuffer[i]> baseline - bias)) wavbuffer[i] = baseline ; // kleine Schwankung => auf Baseline


oh mann, wenn wir doch nur IMMER codetags im Editor hätten....!

(ich hoffe, ich habe mich jetzt nicht verkalkuliert mit dem "Entrauschen"...)

hirnfrei
04.06.2016, 21:48
Schneiden wir dann nicht auch diese Teile aus dem Wort raus?

Hier mal ein Update von der main



#include <iostream>
#include <vector>
#include <fstream>

#include <stdio.h>
#include <limits.h>

#include "diaSound.hpp"

bool debug = false;

void analyse(int32_t *waveBuffer)
{
int32_t maximum = waveBuffer[0];
int32_t minimum = waveBuffer[0];

int32_t maxpos, minpos;

int i;

for(i=0;i<SHRT_MAX;i++)
{
if(waveBuffer[i] > maximum)
{
maximum = waveBuffer[i];

maxpos = i + 1;
}

if(waveBuffer[i] < minimum)
{
minimum = waveBuffer[i];

minpos = i + 1;
}
}
}

int main()
{
fstream datei;

int32_t input[SHRT_MAX], waveBuffer[SHRT_MAX];

int i;

uint8_t *wave;

audioCapture(input, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

wave = (uint8_t *) malloc(SHRT_MAX+1);

if(debug) datei.open("test.csv", ios::out);

for(i=0;i<SHRT_MAX;i++)
{
wave[i] = input[i] & 0x00ff;;

waveBuffer[SHRT_MAX] = input[i] & 0x00ff;

if(debug) cout << i << " -> " << input[i] << endl;

if(debug) datei << input[i] << endl;
}

analyse(waveBuffer);

if(debug) datei.close();

playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

free(wave);

return 1;
}

HaWe
04.06.2016, 22:23
wenn wir soweit sind, können wir probeweise dieses Veränderte Array einmal abspielen und horchen, wie es klingt...
dann gehts weiter...
(ich habe schon eine Idee)


kleine Optimierung für analyse():


void analyse(int32_t *waveBuffer)
{
int32_t maximum = waveBuffer[0];
int32_t minimum = waveBuffer[0];

int32_t maxpos, minpos;

uint32_t i; // falls man mal aus Versehen 1 zu weit zählt...

for(i=0; i<SHRT_MAX; ++i) {
if(waveBuffer[i] > maximum) {
maximum = waveBuffer[i];
maxpos = i ; // genaue Zellen-Nummer
}

if(waveBuffer[i] < minimum) {
minimum = waveBuffer[i];
minpos = i ;
}
}
}


heute abend kann ich selber nicht am Raspi programmieren, du könntest mir aber mal deinen input als zipfile hochladen, dann kann ich morgen mal dran arbeiten.

- - - Aktualisiert - - -


Schneiden wir dann nicht auch diese Teile aus dem Wort raus?
was sind das für Teile, die du meinst ?


und wofür braucht du noch wave und diese Zeilen...?

wave = (uint8_t *) malloc(SHRT_MAX+1);
wave[i] = input[i] & 0x00ff;;

hirnfrei
04.06.2016, 22:54
wave ist für die Ausgabe. Hat eigentlich nur Kontrollfunktion ob im Gerät auch das ankommt was man rein geredet hat. Das kann im letztendlichen Code raus.

Was ist der Unterschied zwischen i++ und ++i?????

Und was genau soll ich hoch laden? Die Daten vom Mikro? Falls ja, dass ist das was in test.cvs drin steht.

- - - Aktualisiert - - -

Und mit Teile raus schneiden, wenn wir alles raus schneiden was die Werte von Vor- und Abspann hat dann schneiden wir das ja auch aus dem Wort heraus. Da kommen doch auch solche Werte drin vor.

- - - Aktualisiert - - -

Ah noch etwas. Was genau macht & 0x00ff?

HaWe
05.06.2016, 10:45
erstmal: wie setzen nur die Bereiche auf Null, die innerhalb eines bestimmten Ausschlags von der Basislinie liegen.

im Abspann, bei den fiktiven Werten

128 129 125 130 127 131 126 128
wären:

baseline 128
maxr 131
minr 125
bias 3

(zur Ermittlung arbeite ich grade an eine optimierten Funktion, die auch singuläre Ausreißer-Spikes unberücksichtigt lässt! )(

das (laute) Wort davor soll mal haben


135 138 120 123 180 150 115 112

kurzer Vorspann (optional):


127 129 128 128


jetzt gucken wir uns den gesamten Scan an:
(Punkte zur Markierung der Bereiche)


127 129 128 128 . 135 138 120 123 180 150 115 112. 128 129 125 130 127 131 126 128

nun bereinigen wir den Scan, indem wir alle kleinen Ausschläge ( von der Baseline <= bias inkl.) auf baseline gesetzt werden, d.h. alle zwischen 125 und 131

128 128 128 128 . 135 138 120 123 180 150 115 112. 128 128 128 128 128 128 128 128


du siehst: jetzt ist das "Wort" in der Mitte scharf markiert.

Den Kram mit dem Bias abziehen vom gesamten Scan, wie gestern "angedacht",können wir uns evtl schenken, denn das macht wschl. die FFT für und automatisch wie ichs momentan sehe.

- - - Aktualisiert - - -


Ah noch etwas. Was genau macht & 0x00ff?
& 0x00ff
"anded" bitweise einen Int-Wert aus mehreren (hier: 4) Bytes und errechnet daraus den Wert des Lowbytes.
0x00ff ist die hex-Schreibweise von 255, wo die 8 kleinen bits auf 1 und alle höheren bits Null sind.

das heisst:
7 & 0x00ff ergibt 7 // nur das lowByte enthält Werte
127 & 0x00ff ergibt 127 // dto...
255 & 0x00ff ergibt 255
256 & 0x00ff ergibt 1 // 2.-unterstes Byte enthält Stör-Wert, der wird herausmaskiert, nur Lowbyte bleibt übrig.
262 & 0x00ff ergibt 7 // dto...
382 & 0x00ff ergibt 127
511 & 0x00ff ergibt 255 .
1024 & 0x00ff ergibt 1
32768 & 0x00ff ergibt 1 // 3.-unterstes Byte enthält Stör-Wert, der wird herausmaskiert, nur Lowbyte bleibt übrig.

Der Sinn war: alle int-Werte, die sonst noch als unnütze Bytes in den 4-Byte-int32_t array-zellen auftauchen sollten, zu nullen (z. B. Steuer-Bytes / Begrenzer oder der Wert einer 2. Spur), damit nur das kleinste Byte übrig bleibt, wenn das wirklich die echte Micro-Mono-Spur enthält.
So wie es momentan aussieht, sind allerdings keine weiteren Stör-Bytes drinnen, aber das werden wir noch austesten.


Was ist der Unterschied zwischen i++ und ++i?????

++i ist das pre-increment, das führt das Increment sofort aus und steht für i=i+1.
i++ ist das post-increment, das führt das Increment erst aus, nachdem weitere Kommandos in der selben Kommando-Zeile bereits ausgeführt worden sind.

++i ist in diesem speziellen Falle zwar identisch mit i++,
aber ++i ist das logisch richtigere, denn i++ kann möglicherweise immer "side effects" haben.

Siehe die Erklärung hier:
http://www.embedded.com/design/programming-languages-and-tools/4410601/Pre-increment-or-post-increment-in-C-C-

HaWe
05.06.2016, 14:03
so, ich habe mal auf nem Arduino die Rauschglättung simuliert (geht dort einfacher mit Grafik etc):

Simulations-Code (automatische Erkennung der Rauschbandbreite) :



// ver 0005

void arrayinit(int16_t * array, int16_t arrlen) {
char sbuf[128];
int16_t bias, minr, maxr, baseline;


// simulated noisy word-command or signal
for (int i=0; i<arrlen; ++i) {
array[i] = 128 + random(10)- 5; // simulated noise +/- 5;
}
for (int i=33; i<arrlen-101; ++i) { // simulated word/signal +/- 100
array[i] = 128 + random(200) - 100;
}

curlf();
sprintf(sbuf, "arrayinit[0] vorher %d ", array[0] );
Serial.println(sbuf);
lcdprint(sbuf);



// calculate baseline from last 100 array cells:

//init
baseline=array[arrlen-90];
minr=array[arrlen-90]-1;
maxr=array[arrlen-90]+1;

// auto-adjust:
for (int i=arrlen-100; i<arrlen; ++i) {
// mean baseline
baseline = round ( ( 0.5*(float)array[i] + 0.5*(float)baseline )) ;

// smoothed out max noise
if (array[i] >= baseline) maxr = round ( ( 0.8*(float)array[i] + 0.2*(float)maxr )) ;

// smoothed out min noise
if (array[i] <= baseline) minr = round ( ( 0.8*(float)array[i] + 0.2*(float)minr )) ;
}

bias = max(baseline-minr-2, maxr+2-baseline) ; //

curlf();
sprintf(sbuf, "baseline %d ", baseline );
Serial.println(sbuf);
lcdprint(sbuf);

curlf();
sprintf(sbuf, "bias %d ", bias );
Serial.println(sbuf);
lcdprint(sbuf);


// mit /ohne Rauschglättung (auskommentieren)

for (int i=0; i<arrlen; ++i) {
if( (array[i]>baseline ) && (array[i] <= baseline + bias)) array[i] = baseline ; // little higher value => drop to baseline
else
if( (array[i]<baseline ) && (array[i] >= baseline - bias)) array[i] = baseline ; // little lower value => rise to baseline
}



curlf();
sprintf(sbuf, "arrayinit[0] nachher %d ", array[0] );
lcdprint(sbuf);
Serial.println(sbuf);


}



klappt! :cool:

leider ist das Bildhochladen hier im Forum extremst kompliziert und vom Speicherplatz zu eingeschränkt,
ich habe es daher hier zum direkten Vergleich hochgeladen:

http://www.mindstormsforum.de/viewtopic.php?f=78&t=8899&p=69409#p69409

- - - Aktualisiert - - -

deine lib habe ich jetzt noch etwas in ifndef eingekapselt, um doppeltes Linken zu vermeiden:




#ifndef __DIASOUND_HPP__
#define __DIASOUND_HPP__



/// dein diaSound-lib Code



#endif

hirnfrei
05.06.2016, 16:10
Was ist denn das für ein Display wo du da am Arduino hängen hast?

Ich merke immer wieder, dass seit ich C gelernt habe sich doch einiges weiter entwickelt hat. Oder es liegt daran das ich erst seit nem halben Jahr mit C++ arbeite.

Danke für deine Erklärungen. Hab ich bisher so noch nie gebraucht und auch i++ hat bisher noch nie Schwierigkeiten gemacht, aber man lernt ja nie aus!

Was dein Simulationscode angeht, sobald ich mein Gentoo auf systemd umgestellt habe bastel ich da gerne was in den Code rein.

ich nehme an es geht in der Hauptsache um diesen Teil



// calculate baselind from last 100 arraay cells:

//init
baseline=array[arrlen-50];
minr=array[arrlen-50]-1;
maxr=array[arrlen-50]+1;

// auto-adjust:
for (int i=arrlen-100; i<arrlen; ++i) {
// mean baseline
baseline = round ( ( 0.7*(float)array[i] + 0.3*(float)baseline ) ) +1;

// smoothed out max noise
if (array[i] >= baseline) maxr = round ( ( 0.7*(float)array[i] + 0.3*(float)maxr ) ) +1;

// smoothed out min noise
if (array[i] <= baseline) minr = round ( ( 0.7*(float)array[i] + 0.3*(float)minr ) ) -1;

}

bias = max (baseline-minr, maxr-baseline); //

// mit /ohne Rauschglättung (auskommentieren)

for (int i=0; i<arrlen; ++i) {
if( (array[i]>baseline ) && (array[i] <= baseline + bias)) array[i] = baseline ; // kleine Schwankung => auf Baseline
else
if( (array[i]<baseline ) && (array[i] >= baseline - bias)) array[i] = baseline ; // kleine Schwankung => auf Baseline
}

HaWe
05.06.2016, 16:54
hallo,

ja, genau, das ist der Teil mit dem Rauschen wegschneiden. Inzwischen ist der Code schon wieder ein wenig optimiert...

was C angeht: da hat sich bei ANSI C gar nicht so viel verändert, eher schon C++11 und C++14, aber ich selber programmiere nur in ANSI C99 (und nutze die C++ libs ntl, soweit verfügbar).

C/C++ sind aber derart mächtig, dass man sicher immer wieder noch mal was neues entdeckt, egal wie lange man schon dabei ist ... ;)

das Arduino-Display ist dieses hier:
http://www.mindstormsforum.de/viewtopic.php?f=78&t=8491&p=68170#p68170

- - - Aktualisiert - - -

wenn du die Wav-Rausch-Entfernung vorher/nachher gemacht hast, dann spiele doch bitte mal diesen veränderten Array als wav-File ab (PlayCaptured).
Das Rauschen im "Wortsignal" ist ja noch nicht verändert, aber vorher/hinterher müsste man einen Unterschied hören!

hirnfrei
05.06.2016, 18:25
Hast du den Code schon abgeändert? Wenn nicht machst du das noch?

Ja ich höre mir das natürlich an. Nutzt ja nichts wenn man Kaudawelsch versucht zu vergleichen.

Ich hab damals auf dem Amiga mit C Programmieren angefangen. Ganz am Anfang war es der C64 mit Basic und auch der C128. Danach dann kurz MS-DOS (VHS da hatte ich keine Wahl) und das war mir schon unangenehm. Zum Glück bekam ich kurz darauf einen Amiga und da ging es dann los mit dem Programmieren. Erst in ARexx. Eine Skript-Sprache die mir heute auf eigentlich allen Betriebssystemen fehlt. Auf dem Amiga hat eigentlich jedes Programm eine ARexx Schnittstelle wo man sie dann mit ARexx unter einander verbinden und steuern kann. Da habe ich komplette Mailinglisten drüber programmiert und auch sonst vieles Automatisiert. Dann kam Ansi-C. Da hab ich alles Mögliche mit programmiert. Angefangen von einfachen Programmen zur Fuhrparktüberwachung bis hin zu FTP Clienten und OpenGL. Auch einen AIM Client habe ich geschrieben. Dann wurde aber das Geld knapp und leider ist Hardware für den Amiga super teuer. Dann kam kurz Windows. Dort habe ich auch programmiert aber Spass hatte ich irgendwie nie dabei. Dann bin ich davon weg zu Linux aber durch Frau und Kinder habe ich das Hobby echt lange brach liegen lassen. Bis letztes Jahr. Ich habe letztes Jahr mit einem Freund angefangen einen Kettcar um zu bauen und einen PocketBike Motor dran zu bauen (Daher Projekt-Hirnfrei, die Idee meiner Frau). Wir waren aber sehr unzufrieden mit dem Bowdenzug für das Gas-Pedal und als Alternative kam dann mein erster Arduino ins Haus und der hat mir derart viel Spass gemacht das ich seit her wieder sehr viel programmiere. Bin dann aber von Ansi-C auf C++ umgestiegen. Zwar finde ich es toll, wenn man wirklich über jede Speicherstelle bescheid weiss, die man zum Programmieren benutzt, aber der Aufwand mit dem Anfordern, Erweitern und löschen war mir dann irgendwie zu viel. Das es C++11 gibt weiss ich auch erst seit ein paar Monaten ;).

So jetzt kennst du meine Geschichte ;). So viel wollte ich gar nicht schreiben.

Aber recht hast du definitiv. C/C++ ist wahrscheinlich nach Assembler das wo man nie auslernen wird.

HaWe
05.06.2016, 19:23
ja, ich habe die Grenzen noch optimiert, nach ein paar Serien sehr ausgefallener random-Waves.
zur Zeit ver 0005-0007.
im Raspy Code mus es ntl als array-Größe SHRT_MAX heißen statt arrlen, und int32_t statt int16_t.

wenn nach dem Rausch-ex vorher/hinterher beim Abhören immer noch alles klar ist, dann kann man jetzt die Grenzen des Signals setzen:

int signalstart=0;
int i=0;
while( (wavbuffer[i]<=baseline+bias) && (i<SHORT_MAX-1) ) ++i;
signalstart=i;

int signalend=SHORT_MAX-1;
i=SHORT_MAX-1;
while( (wavbuffer[i]<=baseline+bias) && (i>signalstart) ) --i;
signalend=i;

jetzt müsten die Grenzen feststehen.
Ich teste es auf dem Arduino aus.
ver 0007c

- - - Aktualisiert - - -

jawohl, klappt! :cool:





// ver 0007c

void arrayinit(int16_t * array, int16_t arrlen) {
char sbuf[128];
int16_t bias, minr, maxr, baseline,
maximum, minimum, maxpos, minpos,
signalstart, signalend;
uint16_t i;


// simulated noisy word-command or signal
for (i=0; i<arrlen; ++i) {
array[i] = 128 + random(11) - 5; // simulated noise +/- 5;
}
for (i=33; i<arrlen-101; ++i) { // simulated word/signal +/- 100
array[i] = 128 + random(201) - 100;
}

curlf();
sprintf(sbuf, "arrayinit[0] vorher %d ", array[0] );
Serial.println(sbuf);
lcdprint(sbuf);

// dynamics: min, max

maximum = array[0];
minimum = array[0];


for(i=0;i<arrlen; ++i)
{
if(array[i] > maximum) {
maximum = array[i];
maxpos = i;
}
if(array[i] < minimum) {
minimum = array[i];
minpos = i;
}
}


// calculate baseline from last 100 array cells:
// init vars
baseline=( array[minpos] + array[maxpos] ) / 2; // init baseline by (min+max)/2
minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for (i=arrlen-100; i<arrlen; ++i) {
// mean baseline
baseline = round ( ( 0.5*(float)array[i] + 0.5*(float)baseline )) ;

// smoothed out max noise
if (array[i] >= baseline) maxr = round ( ( 0.6*(float)array[i] + 0.4*(float)maxr )) +1 ;

// smoothed out min noise
if (array[i] <= baseline) minr = round ( ( 0.6*(float)array[i] + 0.4*(float)minr )) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1; //

curlf();
sprintf(sbuf, "baseline %d ", baseline );
Serial.println(sbuf);
lcdprint(sbuf);

curlf();
sprintf(sbuf, "bias %d ", bias );
Serial.println(sbuf);
lcdprint(sbuf);


// noise reduction start/end
// drop small noise
for (i=0; i<arrlen; ++i) {
if( (array[i]>baseline ) && (array[i] <= baseline + bias)) array[i] = baseline ; // little higher value => drop to baseline
else
if( (array[i]<baseline ) && (array[i] >= baseline - bias)) array[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart=0;
i=0;
while( (array[i]<=baseline + 4*bias/3) && (i<arrlen-1) ) ++i;
signalstart = i;
if (i>0) signalstart -=1;
signalend=arrlen-1;
i=arrlen-1;
while( (array[i]<=baseline + + 4*bias/3) && (i>signalstart) ) --i;
signalend = i;
if (i<arrlen-1) signalstart +=1;

curlf();
sprintf(sbuf, "arrayinit[0] nachher %d", array[0] );
lcdprint(sbuf);
Serial.println(sbuf);

curlf(); _curx_=80;
sprintf(sbuf, "signalstart %d", signalstart );
lcdprint(sbuf);
Serial.println(sbuf);

curlf(); _curx_=80;
sprintf(sbuf, "signalend %d", signalend );
lcdprint(sbuf);
Serial.println(sbuf);

}


jetzt bei 7c: Grenzenerkennung funktioniert sogar bei 20% Rauschen sehr gut.
neuestes Bild ganz unten hier im Post:

http://www.mindstormsforum.de/viewtopic.php?f=78&t=8899&p=69409#p69409

HaWe
06.06.2016, 23:45
@hirnfrei:
lässt sich jetzt wieder playCaptured verwenden?
wenn du meine Array-Veränderungen (analog Arduino-Code) auf wavbuffer anwendest: lässt sich der Sound dann damit abspielen?
Wie klingt es, verglichen mit dem Original (input) ?

Wenn alles ok ist (und auch dein "Wort" sogar schärfer abgegrenzt zu hören ist, wie es ja sein soll):
sollen wir dann die FFT in Angriff nehmen?

hirnfrei
07.06.2016, 20:39
Ich hoffe das ich dir da heute noch eine Antwort drauf geben kann. Habe ja am Sonntag mein Gentoo auf systemd umgestellt und da müssen viele fette Programme neu compiliert werden. Firefox, Thunderbird, LibreOffice usw. Bin leider immer noch nicht fertig damit.

Spätestens morgen geht es weiter!

hirnfrei
07.06.2016, 23:52
So. System erfolgreich umgestellt. Morgen gehts wieder ans Projekt!

HaWe
08.06.2016, 09:26
Gentoo für den Raspi...?

hirnfrei
08.06.2016, 09:54
Später vielleicht. Bislang ist es den Aufwand nicht wert.

Aber nein ich habe mein Desktop mal auf systemd um gestellt und aktualisiert. Das dauert leider ewig und da ich ja ausschliesslich mit ssh auf dem Raspi arbeite ging das eben nicht, solange der updated, sonst hätte es noch viel länger gedauert.

HaWe
08.06.2016, 10:02
willst du nicht langsam mal direkt auf dem Pi programmieren und compilieren?
Einfach einen großen HDMI-Screen + Maus + Tastatur dran, fertig,
Full-HD-HDMI Monitore (oder DVI mit HDMI-Adapter) gibts gebraucht bei Ebay für unter 50 EUR, und kleine HDMIs dann zum einfach-umstecken für den mobilen Einsatz im 7-10".

hirnfrei
08.06.2016, 14:38
Nö wieso? Läuft doch alles prima. Ich werde in naher Zukunft das System nicht wieder umstellen ^^

HaWe
08.06.2016, 14:49
naja, warum ... wegen des PCs, den du dann nicht mehr brauchst, nur deswegen.

hirnfrei
08.06.2016, 15:02
Schon. Aber dann brauche ich wieder Platz für noch einen Monitor, noch eine Tastatur und noch eine Mouse. Von daher.

HaWe
08.06.2016, 17:58
nachdem wir mit den Vorarbeiten eigentlich soweit sind -
hast du dir das hier jetzt mal genauer angesehen...?
http://dsp.stackexchange.com/questions/736/how-do-i-implement-cross-correlation-to-prove-two-audio-files-are-similar

hirnfrei
08.06.2016, 22:23
So. Code eingebaut, etwas angepasst, ausprobiert. Läuft!



#include <iostream>
#include <vector>
#include <fstream>

#include <stdio.h>
#include <limits.h>
#include <math.h>

#include "diaSound.hpp"

bool debug = false;
bool ausgabe = true;

void filtern(int32_t *array)
{
int32_t sbuf[128];
int32_t bias, minr, maxr, baseline,
maximum, minimum, maxpos, minpos,
signalstart, signalend;

uint16_t i;


// dynamics: min, max

maximum = array[0];
minimum = array[0];

for(i=0;i<SHRT_MAX; ++i)
{
if(array[i] > maximum)
{
maximum = array[i];
maxpos = i;
}
if(array[i] < minimum)
{
minimum = array[i];
minpos = i;
}
}


// calculate baseline from last 100 array cells:
// init vars
baseline=(array[minpos] + array[maxpos]) / 2; // init baseline by (min+max)/2
minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for(i=SHRT_MAX-100; i<SHRT_MAX; ++i)
{
// mean baseline
baseline = round((0.5*(float)array[i] + 0.5*(float)baseline)) ;

// smoothed out max noise
if(array[i] >= baseline) maxr = round((0.6*(float)array[i] + 0.4*(float)maxr)) +1 ;

// smoothed out min noise
if(array[i] <= baseline) minr = round((0.6*(float)array[i] + 0.4*(float)minr)) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1;

// noise reduction start/end
// drop small noise

for(i=0;i<SHRT_MAX;++i)
{
if((array[i]>baseline) && (array[i] <= baseline + bias)) array[i] = baseline ; // little higher value => drop to baseline
else
if((array[i]<baseline) && (array[i] >= baseline - bias)) array[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart = 0;

i = 0;

while((array[i]<=baseline + 4 * bias/3) && (i<SHRT_MAX-1)) ++i;

signalstart = i;

if(i > 0) signalstart -= 1;

signalend=SHRT_MAX-1;

i=SHRT_MAX-1;

while((array[i]<=baseline + + 4*bias/3) && (i>signalstart)) --i;

signalend = i;

if(i<SHRT_MAX-1) signalstart +=1;
}

void analyse(int32_t *waveBuffer)
{
int32_t maximum = waveBuffer[0];
int32_t minimum = waveBuffer[0];

int32_t maxpos, minpos;

int32_t i;

for(i=0;i<SHRT_MAX;i++)
{
if(waveBuffer[i] > maximum)
{
maximum = waveBuffer[i];

maxpos = i;
}

if(waveBuffer[i] < minimum)
{
minimum = waveBuffer[i];

minpos = i;
}
}

filtern(waveBuffer);
}

int main()
{
fstream datei;

int32_t input[SHRT_MAX], waveBuffer[SHRT_MAX];

int32_t i;

audioCapture(input, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

if(debug) datei.open("test.csv", ios::out);

for(i=0;i<SHRT_MAX;i++)
{
waveBuffer[SHRT_MAX] = input[i] & 0x00ff;




if(debug) cout << i << " -> " << input[i] << endl;

if(debug) datei << input[i] << endl;
}

analyse(waveBuffer);

if(debug) datei.close();

if(ausgabe)
{
uint8_t *wave;

wave = (uint8_t *) malloc(SHRT_MAX+1);

for(i=0;i<SHRT_MAX;i++) wave[i] = input[i] & 0x00ff;

playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

free(wave);
}

return 1;
}


Rauscht noch ein wenig, aber ich denke es ist schon um einiges besser!

Kannst du es noch nicht testen?

- - - Aktualisiert - - -

HALT! Kommando zurück!

Ich dussel habe vergessen die richtige Variable an playCaptured zu schicken :-(.

Umbauen und testen!

- - - Aktualisiert - - -

Gerade versucht. Gibt nur zwei kurze Knacken :(

HaWe
09.06.2016, 09:35
was ist, wenn du die Zeile
analyse(waveBuffer);
auskommentierst?
Dann bleibt ja der Sound in waveBuffer unverändert und müsste genau wie das Original klingen.

Spiel dafür zum Vergleich kurz mal hintereinander
a) input und
b) wavebuffer
über playCaptured ab.

zum selber ausprobieren:
ich habe keine Idee, wie ich diene csv datei in das Raspi programm als array reinbekommen soll, und ich habe ja auch keine line in Buchse um selber etwas aufzunehmen.

hilfreich wäre auch, einen wave-Plot zu sehen, so wie bei meinen Arduino-array-Plots:
a) input
b) wavebuffer ohne analyse
c) wavebuffer mit analyse

- - - Aktualisiert - - -

ps,
außerdem wäre es IMO sinnvoller, wenn die filter-Algorithmen direkt in analyse() drinstehen, nicht als extra filter Prozedur, und zwar wegen aller dort deklarierten lokalen Variablen.

hirnfrei
09.06.2016, 13:06
Ich bastel dir eine Funktion mir der du die Daten einlesen kannst. Das ist das kleinste Problem ;). Dachte das du vielleicht mittlerweile die Karte bekommen hast.

Gut, Analyse baue ich um, kein Problem.

Ich habe schon input und wavebuffer hintereinander abgespielt. input geht, wavebuffer nicht.

Plots mache ich nachher.

HaWe
09.06.2016, 14:57
geht wavebuffer auch ohne analyse() nicht oder nur mit analyse() nicht ?

hirnfrei
09.06.2016, 15:42
Habe ich auch schon versucht. Das bringt das selber Ergebnis.

HaWe
09.06.2016, 16:48
wenn es mit Input geht, mit wavebuffer nach dem Kopieren aber nicht, heißt das aber, dass plötzlich schon das simple rüberkopieren der Werte nicht mehr abspielbar ist...?!
Hat es nicht früher schon mal funktioniert?

- - - Aktualisiert - - -


Fehler gefunden!


for(i=0;i<SHRT_MAX;i++) {
waveBuffer[SHRT_MAX] = (int32_t) input[i] & 0x00ff; <<<< hier ist ein Fehler!
// richtig:

for(i=0;i<SHRT_MAX;++i) {
waveBuffer[i] = input[i] & 0x00ff; <<<< richtig !


wenn es dann geht:
lass dir bitte mal anzeigen:
bias
maximal
minimal
signalstart
signalend

hirnfrei
09.06.2016, 22:58
for(i=0;i<SHRT_MAX;i++) {
waveBuffer[SHRT_MAX] = (int32_t) input[i] & 0x00ff; <<<< hier ist ein Fehler!
// richtig:

for(i=0;i<SHRT_MAX;++i) {
waveBuffer[i] = input[i] & 0x00ff; <<<< richtig !


Wo du recht hast ;). Ist mir aber auch schon aufgefallen, damit geht es natürlich.

Die Bias... Ausgabe habe ich auch schon eingebaut, genau so wie im Falle eines Debug das Speichern von input und waveBuffer sowie die Möglichkeit eine gespeicherte Datei wieder zu laden.

Mit letzterem habe ich allerdings noch ein paar Schwierigkeiten.

Filtern ist auch in Analyse integriert.

Sobald das Laden funktioniert bau ich den Code hier ein.

- - - Aktualisiert - - -

Sooooo. 200x den Fehler übersehen. Dann doch gefunden ;)



#include <iostream>
#include <vector>
#include <fstream>
#include <string>

#include <stdio.h>
#include <limits.h>
#include <math.h>

#include "diaSound.hpp"

bool debug = false;
bool ausgabe = true;

void analyse(int32_t *waveBuffer)
{
int32_t sbuf[128];
int32_t bias, minr, maxr, baseline,
signalstart, signalend;

int32_t maximum = waveBuffer[0];
int32_t minimum = waveBuffer[0];

int32_t maxpos, minpos;

int32_t i;

for(i=0;i<SHRT_MAX;i++)
{
if(waveBuffer[i] > maximum)
{
maximum = waveBuffer[i];

maxpos = i;
}

if(waveBuffer[i] < minimum)
{
minimum = waveBuffer[i];

minpos = i;
}
}

maximum = waveBuffer[0];
minimum = waveBuffer[0];

for(i=0;i<SHRT_MAX; ++i)
{
if(waveBuffer[i] > maximum)
{
maximum = waveBuffer[i];
maxpos = i;
}
if(waveBuffer[i] < minimum)
{
minimum = waveBuffer[i];
minpos = i;
}
}


// calculate baseline from last 100 waveBuffer cells:
// init vars
baseline=(waveBuffer[minpos] + waveBuffer[maxpos]) / 2; // init baseline by (min+max)/2

minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for(i=SHRT_MAX-100; i<SHRT_MAX; ++i)
{
// mean baseline
baseline = round((0.5*(float)waveBuffer[i] + 0.5*(float)baseline)) ;

// smoothed out max noise
if(waveBuffer[i] >= baseline) maxr = round((0.6*(float)waveBuffer[i] + 0.4*(float)maxr)) +1 ;

// smoothed out min noise
if(waveBuffer[i] <= baseline) minr = round((0.6*(float)waveBuffer[i] + 0.4*(float)minr)) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1;

// noise reduction start/end
// drop small noise

for(i=0;i<SHRT_MAX;++i)
{
if((waveBuffer[i]>baseline) && (waveBuffer[i] <= baseline + bias)) waveBuffer[i] = baseline ; // little higher value => drop to baseline
else
if((waveBuffer[i]<baseline) && (waveBuffer[i] >= baseline - bias)) waveBuffer[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart = 0;

i = 0;

while((waveBuffer[i]<=baseline + 4 * bias/3) && (i<SHRT_MAX-1)) ++i;

signalstart = i;

if(i > 0) signalstart -= 1;

signalend=SHRT_MAX-1;

i=SHRT_MAX-1;

while((waveBuffer[i]<=baseline + + 4*bias/3) && (i>signalstart)) --i;

signalend = i;

if(i<SHRT_MAX-1) signalstart +=1;

if(ausgabe)
{
cout << "Bias: " << bias << endl;
cout << "Maximal: " << maximum << endl;
cout << "Minimal: " << minimum << endl;
cout << "Signalstart: " << signalstart << endl;
cout << "Signalende: " << signalend << endl;
}
}

int main(int argc, char *argv[])
{
fstream datei;

int32_t input[SHRT_MAX], waveBuffer[SHRT_MAX];

int32_t i;

if(argc <= 1) audioCapture(input, SHRT_MAX, "plughw:1,0", 1, 12000, 8);
else
{
datei.open(argv[1], ios::in);

for(i=0;i<SHRT_MAX;++i)
{
datei >> input[i];
}

datei.close();
}

if(debug) datei.open("input.csv", ios::out);

for(i=0;i<SHRT_MAX;i++)
{
waveBuffer[i] = input[i] & 0x00ff;



cout << i << " -> " << input[i] << endl;
if(debug) datei << input[i] << endl;
}

if(debug) datei.close();

analyse(waveBuffer);

if(debug)
{
datei.open("waveBuffer.csv", ios::out);

for(i=0;i<SHRT_MAX;i++) datei << waveBuffer[i] << endl;

datei.close();
}

if(ausgabe)
{
uint8_t *wave;

wave = (uint8_t *) malloc(SHRT_MAX+1);

for(i=0;i<SHRT_MAX;i++) wave[i] = waveBuffer[i];

playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

free(wave);
}

return 1;
}


Jetzt bist du auch in der Lage eine vom programm erstellte Datei wieder zu laden abspielen zu lassen. Gib einfach hinter dem Programmnamen den Namen der Datei ein. Fertig. Dann umgeht er die Aufnahme und nimmt stattdessen den Input der Datei,

- - - Aktualisiert - - -

Plotten kannst du, wenn du selbst experimentierst dann jetzt auch. Geh einfach auf http;//sound.projekt-hirnfrei.de/index.php (http://sound.projekt-hirnfrei.de/index.php), lade die Datei hoch und warte kurz. Schon wird dir das Ergebnis angezeigt.

HaWe
10.06.2016, 10:43
danke, dann mach ich mich mal dran ("ASAP"). ;)
Jetzt am Wochenende bin ich unterwegs und kann wenig mit nem Pi machen, ich nehme ihn trotzdem mit inkl. was zum bedienen dazu gehört.

- Mich interessiert nun, wie es jetzt klingt,
- was misst du an "bias", .. (blödes Wort, ich hatte es zuerst für eine Schwelle benutzt, jetzt eigentlich besser: "noise"),
- Wie groß sind max und min Ausschlag des Wortes,
- welchen Bereich hat er als "Wort" markiert (signal start/end) ?
- und kann man dein Originalwort unverstümmelt (!) noch genau so erkennen wie vorher, nur jetzt eben ohne Rausch- Vor- und Nachspann?



Fürs Plotten muss noch openVG in dein Programm rein, damit man die Plots von Dateien und allen möglichen Arrays direkt auf dem Raspi-Screen anschauen kann, während der Programmierung, zum Debuggen, zur laufenden Kontrolle, und zur Optimierung von Filtern:

http://www.mindstormsforum.de/viewtopic.php?f=78&t=8689&p=67838#p67774

on-line zu plotten macht nur ausnahmsweise mal Sinn, wenn alles andere versagt.

Wir brauchen dann demnächst die Plot-Möglichkeit (einfach senkrechte Linien nebeneinander setzen) vor allem auch für die Graphen der FFT.
(wie das openVG dann mit ssh funktioniert, weiß ich allerdings nicht)


Für die Initialisierung des openVG-Fensters würde ich in mindestens (BxH) 640x480 oder sogar 1024x600 nehmen, dann kann man Koordinatensysteme in 512er Breite (evtl. sogar 1024) darstellen, eben entsprechend "gestaucht" (vgl. Arduino-Plots).


OpenVG benötigt folgende Befehls-Wrapper um die eigentliche Grafik-Ausgabe herum:



#include "VG/openvg.h"
#include "VG/vgu.h"
#include "fontinfo.h"
#include "shapes.h"

int width, height;
initWindowSize(20, 20, 640, 480);
init(&width, &height); // Graphics initialization, cut away overhead
WindowOpacity(220); // define window opacity (0...255)
Background(0, 0, 0); // Black background
Start(width, height); // Start the picture

// >>>> hier kommt der Plot- Code rein fürs aktuelle Bild
Fill(255, 255, 255, 1); // White text (eigentlich besser: "TextColor()" )
StrokeWidth(1.0); // Zeichenstift-Breite (eigentlich besser: "BrushWidth()" )
Stroke(255, 255, 255, 1.0); // Zeichenstift-Farbe (eigentlich besser: "BrushColor()" )
// ... // ( ... more code for graphics)
End(); // Put the picture to the screen (eigentlich: "showPicture()" )
// <<<< Ende des aktuellen Bildes

finish(); // Graphics cleanup (end of program)

hirnfrei
10.06.2016, 11:06
Bias: 3
Maximal: 155
Minimal: 91
Signalstart: 4946
Signalende: 15557


Hab ich ganz vergessen sorry.

Ich schaue mir dann mal das openvg an. Denke das wird sich realisieren lassen. Über ssh ist auch kein Problem, macht der Raspi das Fenster einfach auf meinem Desktop auf. Das ist ja das Schöne an ssh. Geany macht er ja auch auf meinem Rechner auf. Ich häng dir mal ein Bild an wie das bei mir auf dem Desktop aussieht.

Ich muss nachher aber an meiner Stromversorgung noch was machen. Habe gestern zwei Akkus auf unter 2V entladen. Nicht gut! Ist zwar schön zu sehen wie viel Spannung noch in den Akkus ist, aber wenn ich grade andere Sachen am Machen bin und nicht sehe was da angezeigt wird dann nutzt das nichts. Ich programmiere das nachher um so das der Raspi sich runter fährt wenn eine Zelle unter 2,6V fällt. Das ist aber kein grosses Problem.

HaWe
10.06.2016, 11:20
ok, sehr schön!

Von der Aussteuerung ist ja noch etwas Platz nach oben (bis 255), aber nachdem das Rauschen nur etwa 3 ist, entspricht es nur ~ 2%: das ist perfekt!

Wenn allerdings später im Betrieb auch Motoren laufen... die machen teilweise ganz schönen Krach.
Aber dann kann man immer noch was dran drehen.

Und wie klingt jetzt der veränderte Array im Vergleich zum Original?

hirnfrei
10.06.2016, 14:47
Hör es dir am Besten selbst an

http://sound.projekt-hirnfrei.de/input.csv
http://sound.projekt-hirnfrei.de/waveBuffer.csv

Ich habe auch mal krass rum experimentiert und einfach mal in analyse() waveBuffer komplett auf 0 gesetzt um zu sehen ob es auch so wieder in main raus kommt. Also das klappt prima!

Jetzt auf ein Geburtstag, dann mach ich mich an OpenVG.

Wenn ich mir das mit den ganzen Libs die es so gibt überlege ist Programmieren auch nichts anderes wie Lego ^^

HaWe
10.06.2016, 17:11
super, dann kann man jetzt den Code vereinfachen und beschleunigen....


Road-Map:

Dazu brauchen wir eine andere Programm-Struktur, und dazu muss alles mit "debug" raus.
Das macht den Code auch überschaubarer.

1.) Muster-Erstellung

a) du nimmst eine Reihe von Wort-Samples auf:
JA.wav
NEIN.wav
STOPP.wav

b) der Micro- bzw. file- input[] array wird als Plot angezeigt
und dann direkt nach analyse geschickt und noise und Wort-Grenzen ermittelt.

c) jetzt wird NUR das Wort innerhalb der Wort-Grenzen nach wavbuffer kopiert


memset(wavebuffer, 0, sizeof(wavebuffer) );
memcpy(wavebuffer, input+(signalstart*sizeof(char)), (signalend-signalstart)*sizeof(int32_t) );

// ich hoffe, ich habe mich jetzt mit memcpy nicht verhauen
// ansonsten soll das rauskommen:
// for (i=0; i<(signalend-signalstart); ++i) wavebuffer[i]=input[i+signalstart];


d) Der wave-Puffer startet jetzt an Stelle 0 mit dem wav-Wortsignal, der Rest bleibt 0.
Der neue wav-Puffer wird ebenfalls als Plot angezeigt.

e) dann wird das "neue" wavebuffer-Muster als .wav Datei gespeichert.
JA_opt.wav
NEIN_opt.wav
STOPP_opt.wav



Das Plot-Programm könnte so aussehen:




int main() {

// Request a window size of 640*480 with top-left at 20,20
initWindowSize(20, 20, 640, 480);
init(&width, &height); // Graphics initialization

Start(width, height); // Start the picture
Background(0, 0, 0); // Black background
WindowOpacity(220); // Make the window little opaque

// ...

Finish(); // clean up graph window
}

void plotArray(int32_t * array, int32_t arrlength ) {

Fill(255, 255, 255, 1); // White text
StrokeWidth(1.0); // brush width

WindowClear();

Stroke(255, 255, 255, 1.0); // white lines
Line(10, 10, 10+255, 10); // ordinate
Line(10, 10, 10, 512); // abszisse

Stroke(0, 255, 0, 1.0); // green lines: wave pattern
for(uint32_t i=0; i<arrlength; ++i) {
Line(10+(i/64), 10, 10+(i/64), 10+(array[i]) ); // i=len=32768/64=512, array[i]_hi=255
}

End(); // End = paint the the picture

}






Wenn du das hast, lade doch bitte die 6 wav files mal hoch, dass ich sie kontrollieren kann (evtl. zum nachbessern).



wenn alles OK ist, kommt:

2.) Experimente mit der FFT und der cross correlation


- - - Aktualisiert - - -


3.) späteres Anwendungs-Programm:

a) später bekommen die wav-Muster-Files nur durchgehende Nummern plus einen Namenseintrag in einer Extra-Datei, in der Nummer und Namen miteinander verbunden werden, sodass man danach suchen kann, z.B.
0 (reserviert)
1 ja
2 nein
3 stopp
4 vorwärts
5 rückwärts
6 seitwarts
7 ran
...
;)



b) Das abschließende lauffähige Wort-Erkennungs-Programm kann sich von seinem Aufbau her im Prinzip an diesem NXC-Programm orientieren:
http://www.mindstormsforum.de/viewtopic.php?f=70&t=6386&p=53924#p53924

aber vorher müssen noch etliche FFT-Tests laufen....



1.1 Für die Bearbeitung von wave-arrays und wave-files brauchen wir dann 5 Funktionen:



a) record_wave(int32_t * array, int32_t length); // Aufnahme von Tönen über Soundkarte => input-array
ähnlich wie audioCapture(input, SHRT_MAX, "plughw:1,0", 1, 12000, 8 );

b) save_wave2file(int32_t * array, char * filename); // speichern eines arrays als .wav File mit header
=> fehlt noch ?

c) wavefile2Array(int32_t * array, char * filename); // lesen von einem .wav File => in input-array
(extrahiert ohne header, nur data, ohne audio-Wiedergabe über Soundkarte)

d) play_waveArray(int32_t * array, int32_t length); // abspielen eines arrays mit festen Parametern
ähnlich wie playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8 );

e) play_waveFile(char * filename); // abspielen eines wav-files mit Parametern laut Header-Daten,
ohne kopieren in einen temporären Array, ähnlich wie playwave(string waveDatei, string name)
Soundkarten-Daten getrennt konfigurieren


In die Sound-Lib sollte man dann noch zusätzlich aufnehmen diese 2 globalen #defines für Soundkarten:

#define SoundCard_Intern "plughw:0,0"
#define SoundCard_Extern "plughw:1,0"



diese Funktionen müssten dann genau so in deine wav-lib mit rein.


einverstanden?

hirnfrei
10.06.2016, 18:47
Hilf mir mal... ich kriege openvg nicht installiert.

HaWe
10.06.2016, 19:52
du bist doch auf dieser Seite...?

https://github.com/paeryn/openvg/



HALT STOPP !

- - - Aktualisiert - - -

dies ist der richtige Installationsbefehl, es wurde von Paeryn vlt nicht aktualisiert!




sudo apt-get install libjpeg8-dev indent libfreetype6-dev ttf-dejavu-core

git clone git://github.com/paeryn/openvg

cd openvg
make
make library
sudo make install




Demo-Programm:


cd openvg
cd client
make test

- - - Aktualisiert - - -

falls das nicht klappt, schreib mal hier rein:
https://www.raspberrypi.org/forums/viewforum.php?f=69

ich habe meine Installation ja schon vor 1/2 Jahr gemacht, vielleicht wurde was verändert, aber nicht dokumentiert!
(Die Pi docus sind echt manchmal zum Kot***

hirnfrei
10.06.2016, 19:54
Ok?

Ich hatte es von da geclont -> git clone git://github.com/ajstarks/openvg

Das könnte das erklären ;)

Vielleicht auch nicht :(



pi@raspi:~/Entwicklung/openvg $ sudo make install
install -m 755 -p font2openvg /usr/bin/
install -m 755 -p libshapes.so /usr/lib/libshapes.so.1.0.0
strip --strip-unneeded /usr/lib/libshapes.so.1.0.0
ln -f -s /usr/lib/libshapes.so.1.0.0 /usr/lib/libshapes.so
ln -f -s /usr/lib/libshapes.so.1.0.0 /usr/lib/libshapes.so.1
ln -f -s /usr/lib/libshapes.so.1.0.0 /usr/lib/libshapes.so.1.0
install -m 644 -p shapes.h /usr/include/
install -m 644 -p fontinfo.h /usr/include/
pi@raspi:~/Entwicklung/openvg $


da scheint sich kein openvg.h dabei zu befinden.

HaWe
10.06.2016, 20:02
ja, so wars früher!

- - - Aktualisiert - - -

kannst du alle deinstallieren und von Paeryn neu installieren?

KEINE PFADE ÄNDERN!


sudo apt-get install libjpeg8-dev indent libfreetype6-dev ttf-dejavu-core

git clone git://github.com/paeryn/openvg

cd openvg
make
make library
sudo make install



wenns nicht geht:
im raspi.org Forum/openvg nachfragen!

https://www.raspberrypi.org/forums/viewforum.php?f=69

hirnfrei
10.06.2016, 20:06
Habe ich schon. Habe es danach genau so wie du geschrieben hast installiert, dabei kam genau das raus.

HaWe
10.06.2016, 20:23
wie gesagt, poste es mal hier, und schreibe @Paeryn drüber ;)

https://www.raspberrypi.org/forums/viewforum.php?f=69

hirnfrei
10.06.2016, 20:55
Interessant. Der installiert das in:



#include "/opt/vc/include/VG/openvg.h"

HaWe
10.06.2016, 20:56
und geht's denn jetzt?

hirnfrei
10.06.2016, 21:14
Natürlich nicht weil es kein bekannter Pfad ist. Andere Header finden es immer noch nicht. Ich muss schauen das muss ich wo anders hin kopieren.

So okay so hat er es jetzt wohl gefunden. Habe einfach den Inhalt von /opt/vc/include nach /usr/include kopiert.

- - - Aktualisiert - - -

Allerdings klappt das Linken jetzt nicht :(

HaWe
10.06.2016, 21:15
hast du richtig gelinkt?

gcc -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads anysource.c -o anysource -lshapes
./anysource



entsprechende Settings für Geany - bisherige Einstellungen abändern! - :

Geany settings for compile:

g++ -Wall -pthread -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -c "%f" -lshapes -lwiringPi -lrt


Geany settings for make/build:

g++ -Wall -pthread -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads -o "%e" "%f" -lshapes -L/opt/vc/lib -lOpenVG -lEGL -lwiringPi -lrt

hirnfrei
10.06.2016, 23:44
Hast du eine Ahnung was



main: oglinit.c:123: oglinit: Assertion `success >= 0' failed.


bedeutet? Das tritt auf nach init(&width, &height);

HaWe
11.06.2016, 12:23
hallo,
keine Ahnung, hatte ich noch nie. Ich hatte überhaupt noch nie Installationsprobleme auf meinen Pi 2, ich habe es 4 oder 5 mal auf verschiedenen SDs für meine Pis einfach exakt nach Vorschrift installiert - und dann liefs (direkt, ohne ssh), ohne irgendwas an den voreingestellten Pfaden vorher oder hinterher zu ändern.

Hast du vorher apt-get update/upgrade gemacht?

Hast du als Basis-Verzeichnis für die Installation /home/pi/ ?

Im Raspi-org Forum sind aber auch Paeryn und AjStarks selber dabei, um Fragen dazu zu beantworten.

- - - Aktualisiert - - -

hier habe ih im Github-Dschungel eine aktuelle Anleitung gefunden:

https://github.com/paeryn/openvg/tree/windowsave





pi@raspberrypi ~ $ sudo apt-get install libjpeg8-dev indent libfreetype6-dev ttf-dejavu-core libfontconfig1-dev

Next, build the library and test:

pi@raspberrypi ~ $ git clone git://github.com/paeryn/openvg
pi@raspberrypi ~ $ git checkout newfonts
pi@raspberrypi ~ $ cd openvg
pi@raspberrypi ~/openvg $ make

./shapedemo demo 10 # run through the demo, pausing 10 seconds between each one; contemplate the awesome.


To install the shapes library as a system-wide shared library

pi@raspberrypi ~/openvg $ make library
pi@raspberrypi ~/openvg $ sudo make install

hirnfrei
11.06.2016, 15:16
Warum hackst du immer auf ssh rum? Ich benutze ssh auf vielen verschiedenen Rechnern seit Jahren aktiv ohne das es jemals Probleme gemacht hat!

Mein Problem war nicht die Installation sondern die Einträge in Geany. Compilieren geht ja prima, nur eben oben genannter Fehler tritt auf.

HaWe
11.06.2016, 15:35
die letzten Infos die ich von dir hatte, waren:

Hilf mir mal... ich kriege openvg nicht installiert.
da scheint sich kein openvg.h dabei zu befinden.
Habe einfach den Inhalt von /opt/vc/include nach /usr/include kopiert
Allerdings klappt das Linken jetzt nicht
main: oglinit.c:123: oglinit: Assertion `success >= 0' failed.- bedeutet? Das tritt auf nach init(&width, &height);


Nachdem ich dir die Geany settings kopiert habe, die bei mir schon ewig ohne probs funktionieren, kann es nur an Unterschieden zwischen unseren Setups liegen:
Pi 3 statt 2
ssh statt lokal
dabei installiert von /home/pi ?
Pfade verändert?
shapes library als system-wide shared library installiert?
openvg lib / fork identisch?
Jessie identisch geupdated?


Einer dieser Punkte muss es sein, und ssh ist lediglich einer von ihnen, allerdings einer, den ich (ebenso wie Pi3, im Gegensatz zu den anderen) nicht kontrollieren kann, auf mehr wollte ich nicht hinweisen.

Letzter Stand für mich also hieß:
es funktionert immer noch nicht bei dir.

Außerdem habe ich auch nichts gefunden, wo du die Fehler im Raspi.org Forum angefragt hättest.

hirnfrei
11.06.2016, 16:10
Gehen wir das mal durch:

Pi 3 statt 2 <- Möglich, aber halte ich für unwahrscheinlich
ssh statt lokal <- Geprüft, fällt ebenfalls raus
dabei installiert von /home/pi ? <- Ja, aber das dürfte prinzipiell keinen Unterschied machen
Pfade verändert? <- nein
shapes library als system-wide shared library installiert? <- nehme ich doch mal an, da ich es genau so installiert habe wie du sagtest
openvg lib / fork identisch? <- habe exakt das genommen wo du sagtest
Jessie identisch geupdated? <- ist aktuell

WIe gesagt, compilieren funktioniert ohne Fehler, aber dann kommt eben diese Meldung

OT

Benutzt du ausser dem Raspi noch andere Rechner mit Linux und falls nein, benutzt du den von Rasbian vorgegebenen LXDE als Desktop?

HaWe
11.06.2016, 17:31
es wird ein Unter-Verzeichnis relativ zum Startverzeichnis des Benutzers angelegt, von dem aus die Installtion erfolgt,
also bei Installationsbeginn von
/home/pi/
jetzt ein Verzeichnis
/home/pi/openvg

heißt also:
anderes Startverzeichnis => Pfade sind anders

Systemweit freischalten, heißt dann
pi@raspberrypi ~/openvg $ make library
pi@raspberrypi ~/openvg $ sudo make install
ok?

Ich habe übrigens nicht die allerneueste openvg Version, meine ist 6 Monate alt und seitdem nicht neu installiert (ist mir zuviel Aufwand mit allem und jedem).

Auch Jessie ist VOR 2016-Apr-10, weil danach einige Libs nicht mehr liefen. Schei** Linux.

außer meinen Pi 2 habe ich (gottseidank) keine weiteren Linux Rechner, und auf allen Pis läuft der Jessie Standard-GUI-Desktop, wie auch immer der heißen mag :-/

Bitte poste deine Fage zur Fehlermeldung mal im Raspi.org Forum, und zwar im englischen openvg-Unterforum, denn v.a. dort guckt auch Paeryn ab und zu.

hirnfrei
11.06.2016, 18:04
Werde ich tun.

Wenn die Installation wirklich abhängig vom Ordner ist, dann hat sich da aber einer nicht wirklich Mühe gegeben. Normalerweise werden sollte Ding automatisch von / ab also dem absoluten Wurzelordner installiert. Da ist es egal wo du die Installationsdatei hin legst. Aber habe ich in meinem Home Verzeichnis gemacht, daran liegt es also auch nicht.

Auch make library und install habe ich gemacht.

Aber wenn du wirklich den Desktop von Raspbian als Massstab anlegst wundert es mich nicht das du Windows besser findest :). Habe eben ne halbe Stunde dran gearbeitet und neeee. Direkt Mate installiert. Das flitzt genau so schnell und ist um Klassen besser!

HaWe
11.06.2016, 18:21
Der Desktop ist gar nicht sooo schlecht.
Mein Problem mit Linux ist, dass ich nichts mit Kommandozeilen zu tun haben will und nichts mit dem editieren von irgendwelchen config-Files und dem schauderhaften "sudo nano", sondern alles mit grafischen benutzerfreundlichen Menüs direkt über Links und Menüs vom Desktop aus erledigen will.
Ein Programm will ich installieren können, indem ich irgendwo auf ein "install.exe" doppelklicke, und ich will es wieder loswerden per Doppelklick auf "uninstall.exe".
Und vor allem will ich auch nichts mehr mit dem grauenhaften "sudo" zu tun haben müssen. Aber das ist eben Linux pur.

Egal.

Kannst du jetzt einen wave-array per openvg plotten? (mein Code dazu siehe weiter oben!)

hirnfrei
11.06.2016, 18:52
Nein noch nicht. Ich sag bescheid wenns klappt.

Das ist Linux pur? Dann scheine ich ein anderes OS zu haben ^^.

- - - Aktualisiert - - -

Muhaha ich hab den Fehler!

Ich hatte den experimentellen GL Treiber aktiv!

HaWe
11.06.2016, 19:01
ach du schei***.

Und du hast Linux ohne Kommandozeile, ohne Terminalfenster und ohne sudo?

Super, das will ich auch.
Ich glaube, das heißt aber anders... Mac oder XP oder so... ;)

hirnfrei
11.06.2016, 19:10
So ich hab jetzt noch was zu erledigen, dann schaue ich mir das mit dem Plotten an.

Ohne sudo ja. Aber Gentoo ist ganz sicher nichts für dich ^^.

Meine Frau hat Linux Mint und die hat noch nie den Terminal oder irgendeine Kommandozeile gesehen.

HaWe
11.06.2016, 19:29
hat deine Frau jemals config- oder andere Systemfiles ändern, löschen oder verschieben müssen/können ohne sudo?
Und wie installiert sie Programmier-Libs mit git-clone oder apt-get ohne Terminal?
Wie führt sie gpio-Zugriffe aus ohne sudo (Hardware-pwm, i2c-0 freischalten)?
Wie ändert sie die HDMI-Anzeige, indem sie bestimmte Auflösungen "erzwingt", und das ohne Neustart?

hirnfrei
11.06.2016, 20:06
hat deine Frau jemals config- oder andere Systemfiles ändern, löschen oder verschieben müssen/können ohne sudo?

Nein. Alles mit GUI

Und wie installiert sie Programmier-Libs mit git-clone oder apt-get ohne Terminal?

Synaptic Packetverwaltung. Ebenfalls alles GUI.

Wie führt sie gpio-Zugriffe aus ohne sudo (Hardware-pwm, i2c-0 freischalten)?

Gar nicht, sowas macht sie nicht.

Wie ändert sie die HDMI-Anzeige, indem sie bestimmte Auflösungen "erzwingt", und das ohne Neustart?

Neustart? Was ist das? Indem sie entsprechende Auflösung im entsprechenden Fenster anwählt. Aber ich glaube seit der Installation musste sie das nicht mehr.

HaWe
11.06.2016, 20:42
da scheint es ja immerhin Linux zu geben, das dem Jessie ein wenig vorraus hat. Wahrscheinlich läuft es aber dann doch nicht mit dem device tree, den man für wiringPi und die kernel-kontrollierten gpios braucht. Die Auflösung ändern muss ich halt öfters für meine 5, 7, 10 und 24" HDMIs, und auf den kleinen läuft nichts automatisch richtig - immer muss man erst wieder /boot/config.txt patchen, mit sudo-Rechten ntl.

Egal: zurück zum wav-Problem 8-)

hirnfrei
11.06.2016, 22:20
Geiler Scheiss, wenn ich das so sagen darf...

Komme heim, mein Computer aus. Ich will ihn starten, geht er kurz an und sofort wieder aus und seither macht er nichts mehr anderes. Super! Aus Mangel an Ersatz muss jetzt der Raspi als Desktop herhalten bis ich nen Neuen hab :(

HaWe
11.06.2016, 22:42
herzliches Beileid!

:cry:

hirnfrei
11.06.2016, 22:49
Überraschenderweise läuft das ganz gut. Drucker installation war easy, okay der Labeldrucker macht bisschen doof, aber ansonsten bin ich sehr positiv überrascht. Hätte nicht gedacht das man mit dem Teil so gut arbeiten kann!

HaWe
11.06.2016, 22:59
ich sach ja, das Linux auf dem Raspi ist echt klasse! :rolleyes:

hirnfrei
12.06.2016, 02:46
Ich gehe aber mal stark davon aus das sich mein Raspbian was den Desktop angeht nicht mehr viel mit deinem gemeinsam hat ^^^^^

So aber um das Projekt mal weiter zu treiben, hier jetzt mit Plot. Unten ist input, oben ist waveBuffer. Die beiden Linien in waveBuffer zeigen Start und Ende des Signals. Scheint ja ganz gut zu passen.

Einfach Enter drücken dann läuft das Programm weiter.



#include <iostream>
#include <vector>
#include <fstream>
#include <string>

#include <stdio.h>
#include <limits.h>
#include <math.h>

#include <VG/openvg.h>
#include <VG/vgu.h>
#include <fontinfo.h>
#include <shapes.h>

#include "diaSound.hpp"

bool ausgabe = true;

int32_t sStart, sEnde;

void plotten(int32_t *input, int32_t *waveBuffer)
{
int width, height, i, x = 800;

float schritt = x / (float)SHRT_MAX;

char s[3];

initWindowSize(20, 20, x, 511);
init(&width, &height);

Start(width, height);
Background(0, 0, 0);
Fill(0, 255, 255, 1);

StrokeWidth(1.0);

for(i=0;i<SHRT_MAX;i++)
{
Stroke(255, 255, 255, 1.0);
Line((VGfloat) schritt*i, (VGfloat) input[i], (VGfloat) schritt*(i+1), (VGfloat) input[i+1]);
Stroke(255, 255, 0, 1.0);
Line((VGfloat) schritt*i, (VGfloat) waveBuffer[i]+256, (VGfloat) schritt*(i+1), (VGfloat) waveBuffer[i+1]+256);
}

Line((VGfloat) schritt*sStart, (VGfloat) 255+256, (VGfloat) schritt*sStart, (VGfloat) 0+256);
Line((VGfloat) schritt*sEnde, (VGfloat) 255+256, (VGfloat) schritt*sEnde, (VGfloat) 0+256);


End();

fgets(s, 2, stdin);

finish();


}

void analyse(int32_t *waveBuffer)
{
int32_t sbuf[128];
int32_t bias, minr, maxr, baseline,
signalstart, signalend;

int32_t maximum = waveBuffer[0];
int32_t minimum = waveBuffer[0];

int32_t maxpos, minpos;

int32_t i;

for(i=0;i<SHRT_MAX;i++)
{
if(waveBuffer[i] > maximum)
{
maximum = waveBuffer[i];

maxpos = i;
}

if(waveBuffer[i] < minimum)
{
minimum = waveBuffer[i];

minpos = i;
}
}

maximum = waveBuffer[0];
minimum = waveBuffer[0];

for(i=0;i<SHRT_MAX; ++i)
{
if(waveBuffer[i] > maximum)
{
maximum = waveBuffer[i];
maxpos = i;
}
if(waveBuffer[i] < minimum)
{
minimum = waveBuffer[i];
minpos = i;
}
}


// calculate baseline from last 100 waveBuffer cells:
// init vars
baseline=(waveBuffer[minpos] + waveBuffer[maxpos]) / 2; // init baseline by (min+max)/2

minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for(i=SHRT_MAX-100; i<SHRT_MAX; ++i)
{
// mean baseline
baseline = round((0.5*(float)waveBuffer[i] + 0.5*(float)baseline)) ;

// smoothed out max noise
if(waveBuffer[i] >= baseline) maxr = round((0.6*(float)waveBuffer[i] + 0.4*(float)maxr)) +1 ;

// smoothed out min noise
if(waveBuffer[i] <= baseline) minr = round((0.6*(float)waveBuffer[i] + 0.4*(float)minr)) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1;

// noise reduction start/end
// drop small noise

for(i=0;i<SHRT_MAX;++i)
{
if((waveBuffer[i]>baseline) && (waveBuffer[i] <= baseline + bias)) waveBuffer[i] = baseline ; // little higher value => drop to baseline
else
if((waveBuffer[i]<baseline) && (waveBuffer[i] >= baseline - bias)) waveBuffer[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart = 0;

i = 0;

while((waveBuffer[i]<=baseline + 4 * bias/3) && (i<SHRT_MAX-1)) ++i;

signalstart = i;

if(i > 0) signalstart -= 1;

signalend=SHRT_MAX-1;

i=SHRT_MAX-1;

while((waveBuffer[i]<=baseline + + 4*bias/3) && (i>signalstart)) --i;

signalend = i;

if(i<SHRT_MAX-1) signalstart +=1;

sStart = signalstart;
sEnde = signalend;

if(ausgabe)
{
cout << "Bias: " << bias << endl;
cout << "Maximal: " << maximum << endl;
cout << "Minimal: " << minimum << endl;
cout << "Signalstart: " << signalstart << endl;
cout << "Signalende: " << signalend << endl;
}
}

int main(int argc, char *argv[])
{
fstream datei;

int32_t input[SHRT_MAX], waveBuffer[SHRT_MAX];

int32_t i;

if(argc <= 1) audioCapture(input, SHRT_MAX, "plughw:1,0", 1, 12000, 8);
else
{
datei.open(argv[1], ios::in);

for(i=0;i<SHRT_MAX;++i)
{
datei >> input[i];
}

datei.close();
}

for(i=0;i<SHRT_MAX;i++)
{
waveBuffer[i] = input[i] & 0x00ff;
}

analyse(input);

plotten(input, waveBuffer);

if(ausgabe)
{
uint8_t *wave;

wave = (uint8_t *) malloc(SHRT_MAX+1);

for(i=0;i<SHRT_MAX;i++) wave[i] = waveBuffer[i];

playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

free(wave);
}

return 1;
}

HaWe
12.06.2016, 10:14
moin!
schön, dass du weitergekommen bist!

Leider kann ich unmöglich auf meinem Pi z.Zt programmieren, ich habe hier nur einen 7" Miniscreen zur Verfügung -
ich habe die Funktionen daher auf dem PC ein wenig "offline" umgearbeitet.

Zu den Editor-Einrückungen:
könntest du sie bitte immer auf 3 setzen? sonst passen große Funktions- und Loop-Verschachtelungen nicht mehr in 1 Zeile, da zu weit eingerückt.




zur Plot-Funktion - die musste abgeändert werden.

zuerst, dieser ganze Teil zur Initialisierung von OpenVG gehört in eine Funktion mit fester Fenstergröße, z.B. 1024x600

dabei müssen
int _width_=1024, _height_=600;
globale Variablen werden;

Ich habe sie jetzt mit Unterstrichen im Namen definiert, damit sie besser als globale Variablen erkennbar sind.



int _width_=1024, _height_=600;

initOpenVG() {
initWindowSize(0, 0, _width_, _height_);
init(&_width_, &_height_);
Start(_width_, _height_);
Background(0, 0, 0);
StrokeWidth(1.0);
WindowOpacity(200);
WindowClear();
}
initOpenVG() wird in main() oder dem Task aufgerufen, der später für den Screen zuständig ist - in unserem Fall also z.Zt. noch main().


StrokeWidth(1.0);
braucht dann nie mehr neu aufgerufen werden, außer man ändert es
dasselbe gilt für alle anderen Variablen hier in initOpenVG().

finish() gehört als letzter Befehl in die aufrufende Graph-Init-Funktion,
d.h. z.Zt in main() also kurz for exit(0).

Weiter schlage ich vor, in den Funktionen nicht die Bezeichner der globalen Arrays oder anderer Funktionen zu verwenden, das verwirrt -

stattdessen einfach * array, samt Übergabe der Länge.
Denn später ändert sich ja auch noch die Plot-Länge aufs doppelte.


Auch darf die Plot-Funktion immer nur den übergebenen Array plotten, nicht 2 auf einmal.
Deine plot Funktion lautet dann fürs erste:



// **SNIP**
for(i=0;i<arrlength;i++)
{
Line((VGfloat) schritt*i, (VGfloat) input[i], (VGfloat) schritt*(i+1), (VGfloat) input[i+1]); // <<<< Fehler ????
}

Hier verstehe ich aber dein 3. + 4. Argument von Line() nicht: wieso i+1 ?

das Koordinatensystem hat die x-Achse an der Basislinie y0 im Fenster,
die Linien werden also gezeichnet von
xi, y0
nach
xi, (y0 + array[i])

(xi ist der i-te Schritt auf der x-Achse;
der Skalierungsfaktor xscale für die Schritte der xi-Werte kann von plotArray an Hand der array-Länge ausgerechnet werden.


Willst du 2 Koordinatensysteme untereinander malen, kannst du noch eine y0-Postion als Basislinie mit übergeben an plotArray



void plotArray((int32_t *array, int32_t arrlength, int y0) {

float xscale=1.0, border=100.0; // border anpassen !
xscale = (float) (arrlength-border)/_width_; // Ausschöpfung der gesamten openVG-Window-Breite width samt Rand!

Fill(0, 255, 255, 1);
for(i=0;i<arrlength;i++)
{
Line((VGfloat)xscale*i, (VGfloat) y0, (VGfloat)xscale*i, (VGfloat)(y0+input[i]) );
}
End();
}


die Steuerung mit getch() dagegen gehört in die aufrufende Funktion, also analyse, da wo man einen array gerade fertig gerechnet hat.
WindowClear();
muss dann ggf. direkt vor den plotArray-Aufruf.





Auch in analyse solle nicht input oder wavebuffer stehen, sondern nur allgemein array.

analyse wird dann wiederum allgemein von main() oder einer anderen Funktion aufgerufen, ebenfalls mit arrlength als allgemeinen Längenparameter, damit man flexibel bleibt für verschieden lange arrays zum Analysieren:



void analyse(int32_t *array, int32_t arrlength) // leicht verändert !!
{
int32_t sbuf[128];
int32_t bias, minr, maxr, baseline,
signalstart, signalend;

int32_t maximum = array[0];
int32_t minimum = array[0];
int32_t maxpos, minpos;
int32_t i;

char s[3];

for(i=0;i<arrlength;i++)
{
if(array[i] > maximum)
{
maximum = array[i];

maxpos = i;
}

if(array[i] < minimum)
{
minimum = array[i];

minpos = i;
}
}

maximum = array[0];
minimum = array[0];

for(i=0;i<arrlength; ++i)
{
if(array[i] > maximum)
{
maximum = array[i];
maxpos = i;
}
if(array[i] < minimum)
{
minimum = array[i];
minpos = i;
}
}


// calculate baseline from last 100 array cells:
// init vars
baseline=(array[minpos] + array[maxpos]) / 2; // init baseline by (min+max)/2

minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for(i=arrlength-100; i<arrlength; ++i)
{
// mean baseline
baseline = round((0.5*(float)array[i] + 0.5*(float)baseline)) ;

// smoothed out max noise
if(array[i] >= baseline) maxr = round((0.6*(float)array[i] + 0.4*(float)maxr)) +1 ;

// smoothed out min noise
if(array[i] <= baseline) minr = round((0.6*(float)array[i] + 0.4*(float)minr)) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1;

// noise reduction start/end
// drop small noise

for(i=0;i<arrlength;++i)
{
if((array[i]>baseline) && (array[i] <= baseline + bias)) array[i] = baseline ; // little higher value => drop to baseline
else
if((array[i]<baseline) && (array[i] >= baseline - bias)) array[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart = 0;

i = 0;

while((array[i]<=baseline + 4 * bias/3) && (i<arrlength-1)) ++i;

signalstart = i;

if(i > 0) signalstart -= 1;

signalend=arrlength-1;

i=arrlength-1;

while((array[i]<=baseline + + 4*bias/3) && (i>signalstart)) --i;

signalend = i;

if(i<arrlength-1) signalstart +=1;

sStart = signalstart;
sEnde = signalend;

WindowClear();
plotArray(array, arrlength, 30); // 30 = baseline ordinate // <<<<<<<<<<< neu

// >>>> Tabelle stattdessen ins Graphic window ?

cout << "Bias: " << bias << endl;
cout << "Maximal: " << maximum << endl;
cout << "Minimal: " << minimum << endl;
cout << "Signalstart: " << signalstart << endl;
cout << "Signalende: " << signalend << endl;


}


ich habe auch noch openVG-Hilfsfunktionen für Farbe und zur Textausgabe im Grafikfenster eingearbeitet (z.B. zur Beschriftung der Charts oder weiterer Tabellenwete), das kann dann anstelle der obigen initOpenVG komplett folgendermaßen in das Spracherkennungsprogramm übernommen werden:


#include "VG/openvg.h"
#include "VG/vgu.h"
#include "fontinfo.h"
#include "shapes.h"


//************************************************** ***********
// openVG
//************************************************** ***********

// color name constants
#define BLACK 0, 0, 0
#define WHITE 255, 255, 255
#define MAGENTA 255, 0, 255
#define RED 255, 0, 0
#define YELLOW 255, 255, 0
#define LIME 0, 255, 0
#define GREEN 0, 128, 0
#define CYAN 0, 255, 255
#define AQUA 102, 204, 204
#define BLUE 0, 0, 255

int _width_=1024, _height_=600;
int _fontsize_ = 10;

#define cls() WindowClear()


//************************************************** ***********

inline void lcdprintxy( VGfloat x, VGfloat y, char * buf ) {
Text( (x+_fontsize_), (y+_fontsize_*1.2), buf, MonoTypeface , _fontsize_);
}

//************************************************** ***********

void initOpenVG() {
initWindowSize(0, 0 , _width_ , _height_ );
init(&_width_, &_height_);
Start(_width_, _height_);
Background(BLACK);
Stroke(WHITE, 1);
StrokeWidth(1.0);
Fill(WHITE, 1);
WindowOpacity(200);

cls();
}

//************************************************** ***********

void plotArray((int32_t *array, int32_t arrlength, int y0) {

float xscale=1.0, border=100.0; // border anpassen !
xscale = (float) (arrlength-border)/_width_; // Ausschöpfung der gesamten openVG-Window-Breite width samt Rand!

Fill(0, 255, 255, 1);
for(i=0;i<arrlength;i++)
{
Line((VGfloat)xscale*i, (VGfloat) y0, (VGfloat)xscale*i, (VGfloat)(y0+input[i]) );
}
End();
}

//************************************************** ***********

hirnfrei
12.06.2016, 16:02
Hast du irgendwelche weiterführende Pläne mit dem Programm? Du strickst das so munter um das ich langsam das Gefühl kriege das Programm hat längerfristig eine Bedeutung. Ich dachte bisher aber eigentlich es handelt sich um eine Art Versuchsanordnung um die Grundlagen zu erarbeiten.

Was ich jetzt nicht verstehe, ich rufe die Funktion zum Plotten genau einmal auf. Das heisst sie wird nur einmal abgearbeitet. Was macht es da für einen Sinn das Ganze in Main zu arbeiten?

Warum ich i+1 mache? Ich mache eine Linie von Punkt 1, also aktuelle x und y hin zum nächsten Punkt. Also x (bzw. i) +1 für den nächsten Eintrag im Array. Funktioniert ja auch.

Wo liegt das Problem zwei Arrays zu Plotten? Dadurch verändert sich nichts.

Wenn du nicht direkt am Raspi arbeiten kannst? Warum machst du es dann nicht über ssh?

HaWe
12.06.2016, 18:04
ich kann kein ssh und werde es auch nie nutzen. Ist so ein Linux Ding, das ich einfach nicht mag. ;)

wir werden plotArray noch öfter brauchen, aber immer nur einfach, eins nach dem anderen, das oben war ja nur der Anfang.

1. Aufruf: array unverändert
2. Aufruf: Rauschen raus und Wort Grenzen ermittelt.
3. Aufruf: Wort herausschneiden und ganz an den Anfang eines neuen Arrays setzen

Probier mal bitte meine Funktion plotArray zu verwenden, deine ist mir völlig unklar, auch wegen der x-Achsen-Skalierung und der gezielten Ausgabe an bestimmten Stellen im Fenster, alleine oder zu mehreren.


Dann geht es weiter wie hier skizziert:

https://www.roboternetz.de/community/threads/69238-wav-Dateien-Programieren-von-Wiedergabe-und-Aufnahme?p=628055&viewfull=1#post628055


wir können dann bald die veränderten wav files mit den unveränderten per FFT vergleichen.

- - - Aktualisiert - - -

ps,
eigentlich würde es sogar reichen, nur Punkte zu setzen, keine Linien zu malen:

statt
void Line(VGfloat x1, VGfloat y1, VGfloat x2, VGfloat y2)
jetzt
void Point(VGfloat x, VGfloat y)

ich finde aber keine entsprechende API Funktion.

hirnfrei
12.06.2016, 19:35
So ich bin schon am umbauen. Funktioniert soweit. Nur habe ich irgendwie so das Gefühl das man bei deiner Variante nicht so gut sieht wie bei meiner.

Generell, warum ist die dieser von unten nach Oben zeichen Variante lieber wie die die ich genommen hate? Nach meiner Variante wird es doch auch in anderen Soundprogrammen angezeigt.



#include <iostream>
#include <vector>
#include <fstream>
#include <string>

#include <stdio.h>
#include <limits.h>
#include <math.h>

#include <VG/openvg.h>
#include <VG/vgu.h>
#include <fontinfo.h>
#include <shapes.h>

#include "diaSound.hpp"

bool ausgabe = true;

int _width_=1024, _height_=600;

void initOpenVG()
{
initWindowSize(0, 0, _width_, _height_);
init(&_width_, &_height_);
Start(_width_, _height_);
Background(0, 0, 0);
StrokeWidth(1.0);
WindowClear();
}

void plotArray(int32_t *array, int32_t arrlength, int y0)
{
float xscale=1.0, border=100.0;
xscale = (float) (arrlength-border)/_width_;

int i;

Fill(0, 255, 255, 1);
Stroke(0, 255, 255, 1);

for(i=0;i<arrlength;i++)
{
Line((VGfloat)xscale*i, (VGfloat) y0, (VGfloat)xscale*i, (VGfloat)(y0+array[i]));
}
End();
}

void analyse(int32_t *array, int32_t arrlength)
{
int32_t sbuf[128];
int32_t bias, minr, maxr, baseline,
signalstart, signalend;

int32_t maximum = array[0];
int32_t minimum = array[0];

int32_t maxpos, minpos;

int32_t i;

for(i=0;i<arrlength;i++)
{
if(array[i] > maximum)
{
maximum = array[i];

maxpos = i;
}

if(array[i] < minimum)
{
minimum = array[i];

minpos = i;
}
}

maximum = array[0];
minimum = array[0];

for(i=0;i<arrlength; ++i)
{
if(array[i] > maximum)
{
maximum = array[i];
maxpos = i;
}
if(array[i] < minimum)
{
minimum = array[i];
minpos = i;
}
}


// calculate baseline from last 100 array cells:
// init vars
baseline=(array[minpos] + array[maxpos]) / 2; // init baseline by (min+max)/2

minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for(i=arrlength-100; i<arrlength; ++i)
{
// mean baseline
baseline = round((0.5*(float)array[i] + 0.5*(float)baseline)) ;

// smoothed out max noise
if(array[i] >= baseline) maxr = round((0.6*(float)array[i] + 0.4*(float)maxr)) +1 ;

// smoothed out min noise
if(array[i] <= baseline) minr = round((0.6*(float)array[i] + 0.4*(float)minr)) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1;

// noise reduction start/end
// drop small noise

for(i=0;i<arrlength;++i)
{
if((array[i]>baseline) && (array[i] <= baseline + bias)) array[i] = baseline ; // little higher value => drop to baseline
else
if((array[i]<baseline) && (array[i] >= baseline - bias)) array[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart = 0;

i = 0;

while((array[i]<=baseline + 4 * bias/3) && (i<SHRT_MAX-1)) ++i;

signalstart = i;

if(i > 0) signalstart -= 1;

signalend=arrlength-1;

i=arrlength-1;

while((array[i]<=baseline + + 4*bias/3) && (i>signalstart)) --i;

signalend = i;

if(i<arrlength-1) signalstart +=1;

if(ausgabe)
{
cout << "Bias: " << bias << endl;
cout << "Maximal: " << maximum << endl;
cout << "Minimal: " << minimum << endl;
cout << "Signalstart: " << signalstart << endl;
cout << "Signalende: " << signalend << endl;
}
}

int main(int argc, char *argv[])
{
fstream datei;

int32_t waveBuffer[SHRT_MAX];

int32_t i;

char s[3];

initOpenVG();

if(argc <= 1) audioCapture(waveBuffer, SHRT_MAX, "plughw:1,0", 1, 12000, 8);
else
{
datei.open(argv[1], ios::in);

for(i=0;i<SHRT_MAX;++i)
{
datei >> waveBuffer[i];
}

datei.close();
}

for(i=0;i<SHRT_MAX;i++)
{
waveBuffer[i] = waveBuffer[i] & 0x00ff;
}

plotArray(waveBuffer, SHRT_MAX, 0);

analyse(waveBuffer, SHRT_MAX);

plotArray(waveBuffer, SHRT_MAX, 255);

if(ausgabe)
{
uint8_t *wave;

wave = (uint8_t *) malloc(SHRT_MAX+1);

for(i=0;i<SHRT_MAX;i++) wave[i] = waveBuffer[i];

playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

free(wave);
}

fgets(s, 2, stdin);

finish();

return 1;
}


Was ssh angeht. Ja es mag eine Linux Geschichte sein, nur verstehe ich dein Problem damit nicht ganz. Ich nehme mal stark an du startest das compilierte Programm jetzt auch in einem Terminal auf dem Raspi. Dann ist der Unterschied eigentlich super gering. Mittels Putty kannst du eine Verbindung zum Raspi aufbauen- Dabei hast du dann ja einen Terminal der eben nur auf deinem Windows läuft anstatt auf dem Raspi. Nicht einmal auf deinem Windows läuft sondern nur auf diesem angezeigt wird. Wenn du dort dann



geany &


startet geany auf dem Raspi und leitet seine GUI auf dein Windows um. Es läuft also prinzipiell Alles genau so wie auf dem Raspi.Wäre ja zumindest für den Übergang sinnvoll.

HaWe
12.06.2016, 20:02
ja, ich glaube ich habe inzwischen auch verstanden, was du machst, du verbindest die Oberkanten der Ausschläge, das gibt eine Art "Obere Umrandungslinie".
Ich habe die Spikes verwendet, weil sie den Hintergrund komplett einfärben und damit kontrastreicher sind (siehe Arduino Plots).

Zum Starten:
nein, ich starte das programm entweder über Geany F5 oder ich starte es im Filemanager (was bei wiringPi nicht geht, wegen sudo-Pflicht) -
oder ich lege einen Link auf den Desktop (Ausführen per sudo).
Solange ich aber damit viel noch rumprogrammiere: immer nur per Geany F5.

Ein Terminal verwende ich nie, diese Un-Toten lasse ich ruhen, und Putty ist für mich Teufelswerk ;)

Ab Mittwoch spätestens habe ich meinen großen Raspi Screen wieder und kann wieder lokal kompilieren :)

hirnfrei
12.06.2016, 20:53
Du willst also lieber die oberen Ausschläge haben?

Für einen C Programmierer bist du aber ziemlich unflexibel ;-).

Ich sage es dir auch nur ungerne aber die Meisten die aktiv programmieren, sei es bei Google oder Microsoft oder wo auch immer, benutzen diese Un-Toten ^^.

HaWe
13.06.2016, 09:28
moin!
ich schätze die C-Syntax sehr, weil sie ungeheuer mächtig und perfekt prozedural strukturierbar ist, aber ich bin Hobby-Programmierer und IDEs und USB-p'n'p gewöhnt (wie bei der Arduino IDE oder der Lego BricxCC IDE), und ich würde mich niemals als "typischen" C-Programmierer bezeichnen (C++ beherrsche ich selber gar nicht). Mit der ganzen Linkerei und make, makefile, .so files, logins und IPs will ich nichts zu tun haben, auch nichts mit Terminals (außer automatisch per USB verbinden ohne weitere Einstellungen wie bei Arduino und Serial).
IDE komplett installieren per setup.exe etc.
Programm schreiben in einer IDE
Compilieren per Tastendruck
Hochladen per Tastendruck (falls nötig)
Starten per Doppelklick oder Tastendruck
- so einfach muss Hobbyprogrammieren gehen, das ist mein Level, und genau so funktioniert ja auch Arduino.

was die Anzeige angeht: so wie bei meinem Code-Voschlag oben wäre es eigentlich perfekt, da auch auf kleinen Screens (5", 7") dann skalierbar und gut erkennbar.



void plotArray((int32_t *array, int32_t arrlength, int y0) {

float xscale=1.0, border=100.0; // border anpassen !
xscale = (float) (arrlength-border)/_width_; // Ausschöpfung der gesamten openVG-Window-Breite width samt Rand!

Fill( CYAN, 1 );
for(i=0;i<arrlength;i++)
{
Line((VGfloat)xscale*i, (VGfloat) y0, (VGfloat)xscale*i, (VGfloat)(y0+input[i]) );
}
End();
}

hirnfrei
13.06.2016, 14:10
Du teilst falsch. Du musst die Bildbreite durch die array Länge teilen, nicht umgekehrt.



#include <iostream>
#include <vector>
#include <fstream>
#include <string>

#include <stdio.h>
#include <limits.h>
#include <math.h>

#include <VG/openvg.h>
#include <VG/vgu.h>
#include <fontinfo.h>
#include <shapes.h>

#include "diaSound.hpp"

#define BLACK 0, 0, 0
#define WHITE 255, 255, 255
#define MAGENTA 255, 0, 255
#define RED 255, 0, 0
#define YELLOW 255, 255, 0
#define LIME 0, 255, 0
#define GREEN 0, 128, 0
#define CYAN 0, 255, 255
#define AQUA 102, 204, 204
#define BLUE 0, 0, 255

int _width_=1024, _height_=600;
int _fontsize_ = 10;



bool ausgabe = true;

void initOpenVG()
{
initWindowSize(0, 0, _width_, _height_);
init(&_width_, &_height_);
Start(_width_, _height_);
Background(0, 0, 0);
StrokeWidth(1.0);
WindowClear();
}

void plotArray(int32_t *array, int32_t arrlength, int y0)
{
float xscale=1.0, border=100.0;
xscale = _width_ / (float) (arrlength-border);

int i;

Fill(CYAN, 1);
Stroke(0, 255, 255, 1);

for(i=0;i<arrlength;i++)
{
Line((VGfloat)xscale*i, (VGfloat) y0, (VGfloat)xscale*i, (VGfloat)(y0+array[i]));
}
End();
}

void analyse(int32_t *array, int32_t arrlength)
{
int32_t sbuf[128];
int32_t bias, minr, maxr, baseline,
signalstart, signalend;

int32_t maximum = array[0];
int32_t minimum = array[0];

int32_t maxpos, minpos;

int32_t i;

for(i=0;i<arrlength;i++)
{
if(array[i] > maximum)
{
maximum = array[i];

maxpos = i;
}

if(array[i] < minimum)
{
minimum = array[i];

minpos = i;
}
}

maximum = array[0];
minimum = array[0];

for(i=0;i<arrlength; ++i)
{
if(array[i] > maximum)
{
maximum = array[i];
maxpos = i;
}
if(array[i] < minimum)
{
minimum = array[i];
minpos = i;
}
}


// calculate baseline from last 100 array cells:
// init vars
baseline=(array[minpos] + array[maxpos]) / 2; // init baseline by (min+max)/2

minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for(i=arrlength-100; i<arrlength; ++i)
{
// mean baseline
baseline = round((0.5*(float)array[i] + 0.5*(float)baseline)) ;

// smoothed out max noise
if(array[i] >= baseline) maxr = round((0.6*(float)array[i] + 0.4*(float)maxr)) +1 ;

// smoothed out min noise
if(array[i] <= baseline) minr = round((0.6*(float)array[i] + 0.4*(float)minr)) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1;

// noise reduction start/end
// drop small noise

for(i=0;i<arrlength;++i)
{
if((array[i]>baseline) && (array[i] <= baseline + bias)) array[i] = baseline ; // little higher value => drop to baseline
else
if((array[i]<baseline) && (array[i] >= baseline - bias)) array[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart = 0;

i = 0;

while((array[i]<=baseline + 4 * bias/3) && (i<SHRT_MAX-1)) ++i;

signalstart = i;

if(i > 0) signalstart -= 1;

signalend=arrlength-1;

i=arrlength-1;

while((array[i]<=baseline + + 4*bias/3) && (i>signalstart)) --i;

signalend = i;

if(i<arrlength-1) signalstart +=1;

if(ausgabe)
{
cout << "Bias: " << bias << endl;
cout << "Maximal: " << maximum << endl;
cout << "Minimal: " << minimum << endl;
cout << "Signalstart: " << signalstart << endl;
cout << "Signalende: " << signalend << endl;
}
}

int main(int argc, char *argv[])
{
fstream datei;

int32_t waveBuffer[SHRT_MAX];

int32_t i;

char s[3];

initOpenVG();

if(argc <= 1) audioCapture(waveBuffer, SHRT_MAX, "plughw:1,0", 1, 12000, 8);
else
{
datei.open(argv[1], ios::in);

for(i=0;i<SHRT_MAX;++i)
{
datei >> waveBuffer[i];
}

datei.close();
}

for(i=0;i<SHRT_MAX;i++)
{
waveBuffer[i] = waveBuffer[i] & 0x00ff;
}

plotArray(waveBuffer, SHRT_MAX, 0);

analyse(waveBuffer, SHRT_MAX);

plotArray(waveBuffer, SHRT_MAX, 255);

if(ausgabe)
{
uint8_t *wave;

wave = (uint8_t *) malloc(SHRT_MAX+1);

for(i=0;i<SHRT_MAX;i++) wave[i] = waveBuffer[i];

playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

free(wave);
}

fgets(s, 2, stdin);

SaveEnd("plott.raw");

finish();

return 1;
}



sind noch weitere Änderungen im Code drin.

HaWe
13.06.2016, 15:13
klar, stimmt ntl mit dem Teilen! :rolleyes:
(das ist der Nachteil vom offline Programmieren ;) )

mit dem if (ausgabe) und dem if(argc) komme ich allerdings nicht klar, denn ich starte ja aus geany heraus und nie von der Kommandozeile aus.

Vielleicht könnten wir später ein Menü einbauen, im Moment ist es aber eh nicht wichtig, denn die nächsten Schritte wären ja laut
https://www.roboternetz.de/community/threads/69238-wav-Dateien-Programieren-von-Wiedergabe-und-Aufnahme?p=628055&viewfull=1#post628055


a) du nimmst eine Reihe von Wort-Samples auf:
JA_raw.wav
NEIN_raw.wav
STOPP_raw.wav

b) der Micro- bzw. file- input[] array wird als Plot angezeigt
und dann direkt nach analyse geschickt und noise und Wort-Grenzen ermittelt.

c) jetzt wird NUR das Wort innerhalb der Wort-Grenzen nach wavbuffer kopiert
Code:

memset(wavebuffer, 0, sizeof(wavebuffer) );
memcpy(wavebuffer, input+(signalstart*sizeof(char)), (signalend-signalstart)*sizeof(int32_t) );

d) Der wave-Puffer startet jetzt an Stelle 0 mit dem wav-Wortsignal, der Rest bleibt 0.
Der neue wav-Puffer wird ebenfalls als Plot angezeigt.

e) dann wird das "neue" wavebuffer-Muster als .wav Datei gespeichert.
JA_opt.wav
NEIN_opt.wav
STOPP_opt.wav


da wir das Plotten ja inzwischen mit variabler Basislinie machen können, könnte man alle 2-3 Plots für den Anfang immer untereinander im selben Fenster anzeigen:
a) unverändert: y0=300-400
b) ent-rauscht: y0=200 (kann entfallen)
c) geschnitten: y0=1

Das dient zur in-Prozess-Kontrolle bei den vorhergehenden und den folgenden Schritten...
denn dann käme ja als nächstes eine kleine Erweiterung der alsa-API mit den vereinfachten Funktionen zum schnelleren abspielen, aufnehmen und um-kopieren von files und arrays nacheinander und ineinander:

a) record_wave(int32_t * array, int32_t length); // Aufnahme von Tönen über Soundkarte => input-array
ähnlich wie audioCapture(input, SHRT_MAX, "plughw:1,0", 1, 12000, 8 );

b) save_wave2file(int32_t * array, char * filename); // speichern eines arrays als .wav File mit header
=> fehlt noch ?

c) wavefile2Array(int32_t * array, char * filename); // lesen von einem .wav File => in input-array
(extrahiert ohne header, nur data, ohne audio-Wiedergabe über Soundkarte)

d) play_waveArray(int32_t * array, int32_t length); // abspielen eines arrays mit festen Parametern
ähnlich wie playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8 );

e) play_waveFile(char * filename); // abspielen eines wav-files mit Parametern laut Header-Daten,
ohne kopieren in einen temporären Array, ähnlich wie playwave(string waveDatei, string name)
Soundkarten-Daten getrennt konfigurieren

In die Sound-Lib sollte man dann noch zusätzlich aufnehmen diese 2 globalen #defines für Soundkarten:

#define SoundCard_Intern "plughw:0,0"
#define SoundCard_Extern "plughw:1,0"

hirnfrei
13.06.2016, 16:37
Du bist echt stur mit deinem Geany ;-). Lieber ein Menü rein stricken wie ein Terminal öffnen ^^

Ich bau dir was.

Warum willst du das Ganze als Wave mit Header speichern??

HaWe
13.06.2016, 18:00
:D

wenn man als Menüpunkt/Aktion wählt "aufnehmen", dann nimmt man einen Sound ja über die Soundkarte als reinen Array auf.

Wenn man ihn jetzt als Muster später wieder benutzen will, muss man ihn ja als .wav speichern.

JA.wav
NEIN.wav
STOPP.wav

Nur dann kann man ja später auch aktuelle Wörter mit allen bekannten gespeicherten Mustern vergleichen, um festzustellen, wie denn das Wort nun heißen mag...

und wenn nichts annähernd identisches vorhanden ist an Mustern, z.B. bei
VORWÄRTS!
dann kann man es gleich als neues .wav Muster speichern, für künftige Vergleiche.
VORWAERTS.wav

und somit hat man dann ein weiteres, neues Wort in seinem Wortschatz.
Und man kann jetzt auch direkt einzelne Wörter seines Wortschatzes abhören, wie sie klingen, und ob sie ggf. noch etwas von der Qualität her verbessert werden können/müssen.




nach jeder Aktion im Programm könnte man also zum Menü zurückkehren:

Kommando-Center:
==============
0 Soundfile *.csv in Programm öffnen / laden (FileOpenDialog)
1 Soundfile *.wav in Programm öffnen / laden (FileOpenDialog)
2 Soundfile *.wav öffnen / abspielen + Plot (FileOpenDialog)
3 Sound aufnehmen per Micro / SoundCard
4 akt. Sound (Array im RAM) abspielen + Plot
5 akt. Sound optimieren (noise/cut)
6 akt. Sound (im RAM) als File speichern unter... (FileSaveDialog)
7 akt. Sound an FFT + Plot
8 akt. Sound cross correlation mit 1 wav File (FileOpenDialog)
9 akt. Sound cross correlation mit allen *.wav Files (auto)
...

hirnfrei
13.06.2016, 18:26
Hier erstmal das Menü.



#include <iostream>
#include <vector>
#include <fstream>
#include <string>

#include <stdio.h>
#include <limits.h>
#include <math.h>

#include <VG/openvg.h>
#include <VG/vgu.h>
#include <fontinfo.h>
#include <shapes.h>

#include "diaSound.hpp"

#define BLACK 0, 0, 0
#define WHITE 255, 255, 255
#define MAGENTA 255, 0, 255
#define RED 255, 0, 0
#define YELLOW 255, 255, 0
#define LIME 0, 255, 0
#define GREEN 0, 128, 0
#define CYAN 0, 255, 255
#define AQUA 102, 204, 204
#define BLUE 0, 0, 255

int _width_=1024, _height_=600;
int _fontsize_ = 10;

string version = "2.0";

bool ausgabe = true;

void initOpenVG()
{
initWindowSize(0, 0, _width_, _height_);
init(&_width_, &_height_);
Start(_width_, _height_);
Background(0, 0, 0);
StrokeWidth(1.0);
WindowClear();
}

void plotArray(int32_t *array, int32_t arrlength, int y0)
{
float xscale=1.0, border=100.0;
xscale = _width_ / (float) (arrlength-border);

int i;

Fill(CYAN, 1);
Stroke(0, 255, 255, 1);

for(i=0;i<arrlength;i++)
{
Line((VGfloat)xscale*i, (VGfloat) y0, (VGfloat)xscale*i, (VGfloat)(y0+array[i]));
}
End();
}

void analyse(int32_t *array, int32_t arrlength)
{
int32_t sbuf[128];
int32_t bias, minr, maxr, baseline,
signalstart, signalend;

int32_t maximum = array[0];
int32_t minimum = array[0];

int32_t maxpos, minpos;

int32_t i;

for(i=0;i<arrlength;i++)
{
if(array[i] > maximum)
{
maximum = array[i];

maxpos = i;
}

if(array[i] < minimum)
{
minimum = array[i];

minpos = i;
}
}

maximum = array[0];
minimum = array[0];

for(i=0;i<arrlength; ++i)
{
if(array[i] > maximum)
{
maximum = array[i];
maxpos = i;
}
if(array[i] < minimum)
{
minimum = array[i];
minpos = i;
}
}


// calculate baseline from last 100 array cells:
// init vars
baseline=(array[minpos] + array[maxpos]) / 2; // init baseline by (min+max)/2

minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for(i=arrlength-100; i<arrlength; ++i)
{
// mean baseline
baseline = round((0.5*(float)array[i] + 0.5*(float)baseline)) ;

// smoothed out max noise
if(array[i] >= baseline) maxr = round((0.6*(float)array[i] + 0.4*(float)maxr)) +1 ;

// smoothed out min noise
if(array[i] <= baseline) minr = round((0.6*(float)array[i] + 0.4*(float)minr)) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1;

// noise reduction start/end
// drop small noise

for(i=0;i<arrlength;++i)
{
if((array[i]>baseline) && (array[i] <= baseline + bias)) array[i] = baseline ; // little higher value => drop to baseline
else
if((array[i]<baseline) && (array[i] >= baseline - bias)) array[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart = 0;

i = 0;

while((array[i]<=baseline + 4 * bias/3) && (i<SHRT_MAX-1)) ++i;

signalstart = i;

if(i > 0) signalstart -= 1;

signalend=arrlength-1;

i=arrlength-1;

while((array[i]<=baseline + + 4*bias/3) && (i>signalstart)) --i;

signalend = i;

if(i<arrlength-1) signalstart +=1;

if(ausgabe)
{
cout << "Bias: " << bias << endl;
cout << "Maximal: " << maximum << endl;
cout << "Minimal: " << minimum << endl;
cout << "Signalstart: " << signalstart << endl;
cout << "Signalende: " << signalend << endl;
}
}

int main(int argc, char *argv[])
{
fstream datei;

int32_t waveBuffer[SHRT_MAX];

int32_t i;

char s[3], m[3];

initOpenVG();

system("clear");

cout << endl << "HaWe und Hirnfreis's Sound Experiment V " << version << endl << endl;
cout << " 1. Audio aufnahme" << endl;
cout << " 2. input.csv abspielen" << endl;
cout << " 3. waveBuffer.csv abspielen" << endl << endl;
cout << " Bitte wählen: ";

fgets(m, 2, stdin);

if(strcmp(m, "1") == 0)
{
cout << endl << "Starte Aufnahmne... " << endl << endl;

audioCapture(waveBuffer, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

cout << endl << "Aufnahmne beendet! " << endl << endl;
}

if(strcmp(m, "2") == 0)
{
cout << endl << "Spiele input.csv ab!" << endl << endl;

datei.open("input.csv", ios::in);

for(i=0;i<SHRT_MAX;++i)
{
datei >> waveBuffer[i];
}

datei.close();
}

if(strcmp(m, "3") == 0)
{
cout << endl << "Spiele waveBuffer.csv ab!" << endl << endl;

datei.open("waveBuffer.csv", ios::in);

for(i=0;i<SHRT_MAX;++i)
{
datei >> waveBuffer[i];
}

datei.close();
}
for(i=0;i<SHRT_MAX;i++)
{
waveBuffer[i] = waveBuffer[i] & 0x00ff;
}

plotArray(waveBuffer, SHRT_MAX, 0);

analyse(waveBuffer, SHRT_MAX);

plotArray(waveBuffer, SHRT_MAX, 255);

if(ausgabe)
{
uint8_t *wave;

wave = (uint8_t *) malloc(SHRT_MAX+1);

for(i=0;i<SHRT_MAX;i++) wave[i] = waveBuffer[i];

playCaptured(wave, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

free(wave);
}

fgets(s, 2, stdin);

SaveEnd("plott.raw");

finish();

return 1;
}


- - - Aktualisiert - - -

Ich sehe aber keinen Grund da ein wave draus zu bauen. speichern und abspielen kann man auch mit meiner Variante und muss nicht erst noch Header erstellen und auslesen# Wäre in meinen Augen nur unnötiger Aufwand.

HaWe
13.06.2016, 18:37
das ging ja super schnell! :)



allerdings soll nicht csv geladen / abgespielt / gespeichert werden, sondern *.wav Dateien (ggf. ~auswahl wie im Filebrowser).

Gespeichert werden sollen nur echte .wav Formate, keine reinen Arrays.



das Konzept ist:

im RAM befindet sich immer ein Array mit den reinen sound data,
auf der SD befinden sich viele (!) .wav Dateien: die bilden den Wortschatz bekannter (zu erkennender) Wortbefehle.
Sie müssen während der Testphase und auch später im Automatik-Betrieb nacheinander geöffnet, gespielt, bearbeitet und verglichen werden können.

Zum Bearbeiten werden .wav Files geöffnet und die reinen sound data in einen array ins RAM kopiert,
Per Micro wird auch nur ein Array im RAM belegt,
bei Speichern wird aber wieder der komplette wav-Header davor geschrieben.



dazu dienen unsere Werkzeuge, und die sind:

a) record_wave(int32_t * array, int32_t length);
b) save_wave2file(int32_t * array, char * filename);
c) wavefile2Array(int32_t * array, char * filename);
d) play_waveArray(int32_t * array, int32_t length);
e) play_waveFile(char * filename);
sowie
f) plotArray(int32_t * array, int32_t length, int y0);
g) analyseArray(int32_t * array, int32_t length);
h) array2FFT(int32_t * array, int32_t length, double * fx, double * fy, int32_t FFT_length);
i) cross_corr(int32_t * array, int32_t * pattern, int32_t length);
...usw...

hirnfrei
13.06.2016, 18:48
Wie im Filebrowser??????????????????????????? Der Aufwand wird etwas gross! Je nachdem wie viele Dateien das sind muss dann auch noch aufteilen und scrollen usw. rein. Also das ist mir dann doch zu viel des Guten.

Aber nochmal. Was spricht gegen das Speichern des Array? Ich sehe keinen Sinn darin ein ganzes Wave da zu bauen.

HaWe
13.06.2016, 18:54
wir sollten jederzeit immer einfach einen File im Browser abspielen können, um ihn jederzeit hören zu können.

Und stell dir vor, wir haben einen Wortschatz von ein paar Dutzend Wörtern, und die Worterkennung klappt am Anfang nicht (wie sicher zu erwarten ist), dann muss man die Files einfach auswählen können, einzeln laden, und einfach mit anderen cross-correlieren.

Wenn der Dateizugriff da nicht einfach per Auswahlmenü möglich ist (einfache Liste mit scroll-bar, wie früher im Norton Commander), dann tippt man sich ja nen Wolf und sitzt nächstes Jahr mit der paarweisen cross-correlation noch dran ;)

wenn du allerdings nur 2 oder 3 Wort-Befehle programmieren willst, und dabei bleibts dann auch, dann ist die Sache einfacher zu händeln...... aber sonst, mit mehr, wächst der Verwaltungsaufwand eben exponentiell....

hirnfrei
13.06.2016, 19:00
Hast du so ein Menü schon einmal selbst gebaut? In Secondlife habe ich solche Menüs mit Auswahlmenü gegen Geld programmiert,weil es sonst keiner machen wollte. Das ist schon ein fetter Aufwand. Wenn du das übernehmen möchtest freue ich mich auf das Ergebnis ;).

Ich verstehe dich aber richtig. Eine Eingabe über das Mikro wird mit verschiedenen Dateien verglichen. Bzw. Eine ausgewählte Aufnahmen wird mit den Anderen verglichen. Soweit bin ich da noch richtig oder?

HaWe
13.06.2016, 19:14
ich hatte damals bei NXC etwa 10 Wörter zu verwalten, und da war der Aufwand überschaubar, und es ging ja ohne FFT.
Hast du dir das Programm einmal angeschaut?
Auch einen Filebrowser ähnlich dem Norton Commander habe ich schon einmal selber programmiert, mit Maus- und Tastatursteuerung, auf der Basis von Pascal-File-Funktionen
FindFirst(pattern),
FindNext()
unabhängig davon, in Turbo Pascal.

Unterschätze den Aufwand nicht, der für Spracherkennung betrieben werden muss, dass ist kein Spaziergang im Park.
Ohne bedienerfreundliche (programmiererfreundliche) Entwickler-, Hilfs- und insb. Dateizugriffswekzeuge, die wir alle selber erstellen müssen, sehe ich da kein Land am Horizont.


Eine Eingabe über das Mikro wird mit verschiedenen Dateien verglichen. Bzw. Eine ausgewählte Aufnahmen wird mit den Anderen verglichen. Soweit bin ich da noch richtig oder?
ja, das stimmt,
dann wird der jeweilige Fehler ermittelt,
und das mit dem kleinsten Fehler ist das am besten passendste Wort.
Wenn der Fehler überall zu groß ist (bestimmter Schwellwert), oder das vorgeschlagene Wort falsch ist, dann kann das aktuelle Wort als neues Wort gespeichert werden.


Bevor unser Programm irgendwann so weit ist, dass es die ganzen Analysen automatisch selber machen kann, müssen wir sie erstmal mühsam alle einzeln manuell durchführen, dann testen, optimieren, und erneut durchführen können.

hirnfrei
13.06.2016, 19:20
Ich schau mir das mal an. Werde aber so oder so bis Mittwoch, im ungünstigen Fall sogar erst Donnerstag nicht wirklich was am Programm arbeiten können. Wundere dich also nicht wenn es im Moment nicht wirklich voran geht.

HaWe
13.06.2016, 19:50
kein Problem, das mit der Spracherkennung ist ja eh dein Projekt, ich will hier ja nur mit der FFT samt cross-correlation helfen (und weil mich das mit der FFT im Vergleich zu meiner alten NXC-Methode ntl auch selber interessiert) ... 8)

In jedem Fall wären diese File- und Steuer-Menüs aber sowieso erst der 3. Schritt, nach dem alles erstmal mit
Ja.wav, NEIN.wav, STOPP.wav
grob vorgetestet wurde.

Eventuell klappen die File- und Steuer-Menüs aber auch schon z.B. mit Zenity und popen ich bin da dran.

HaWe
18.06.2016, 14:11
jawohl, File Open-Menüs klappen wunderbar!
Können einfach ins Programm mit eingebunden werden, siehe hier:
(Dank an peterfido + sisor !)
https://www.roboternetz.de/community/threads/69369-Raspbian-C-C-fertige-OpenFileDialog-oder-SaveFileDialog-Komponenten?p=628328&viewfull=1#post628328

hirnfrei
18.06.2016, 14:17
So. Hat länger gedauert wie ich dachte :-(.

Bin jetzt aber wieder am Start, spätestens morgen gehts weiter!

Die Idee mit Zenity hatte ich auch schon ^^

HaWe
18.06.2016, 16:29
alles klar!
zum hiesigen Thema *.wav files spielen/lesen/speichern fehlt ja dann nur noch, wie man einen Array als kompletten *.wav lädt und speichert (beides mit Zenity),
im Gegensatz zu Menüpunkt
als *.csv speichern
müsste dann dafür ja nur zusätzlich noch der wav Header davor.


Und dann kommt so langsam der Teil mit der FFT - hier ist ein Link zur Einführung, samt FFT Code:

http://paulbourke.net/miscellaneous/dft/

(für unsere Anwendung: int64_t statt long! )



- - - Aktualisiert - - -

noch eine Bitte:

könntest du bitte nochmal die aktuelle Version deiner alsa-lib
"diaSound.hpp"
posten, ohne Verwendung von "string" in den Funktionen, stattdessen ausschließlich mit " char * " falls möglich ?

hirnfrei
18.06.2016, 18:50
Ich schau mal wie ich das umgebaut kriege. String ist doch schon ne deutlich einfachere Sache wie char *. Keine Speicherreservierung usw. Aber ich schaue.

Ich würde sagen, anstelle von zenity verwenden wir ncurse. Sobald ich am Raspi bin (jetzt wieder mit ssh ^^) mach ich mich dran.

HaWe
18.06.2016, 19:49
ok -
nur wenn du string nimmst, müssen wir ihn sowieso erst in einen Array mit fester Dimension [SHRT_MAX] umkopieren, bevor wir ihn verarbeiten können.
Deine Funktionen bleiben dann aber evtl sogar flexibler.

Nachdem die ersten wav-Tests ja erfolgreich abgeschlossen sind, brauchen wir in Zukunft nur noch mit array input[SHRT_MAX] zu arbeiten, und der zusätzliche array wavebuffer[SHRT_MAX] wird nur noch für wenige Zwischenschritte hilfsweise benötigt.

Die wichtigsten nächsten Regie-Funktionen wären also also:



Menü (1) wav file lesen
*.wav-file ---> (über OpenFileDialog) --> string variabel --> fester array input[SHRT_MAX] mit wav-Header
edit: --> wav-Header herausschneiden (--> array wavebuffer[SHRT_MAX] als Puffer),
--> array input[SHRT_MAX]==reine sound-data zur Weiterverarbeitung


Menü (2) wav file abspielen
*.wav-file ---> (über OpenFileDialog) --> ohne Veränderung direkt über SoundCard abspielen


Menü (3) per Micro aufnehmen
Micro --> direkt --> fester array input[SHRT_MAX]==reine sound-data zur Weiterverarbeitung



Menü (4) akt. Sound (Array im RAM) abspielen + Plot
array input[SHRT_MAX]==reine sound-data direkt über Soundcard abspielen



Menü (5) optimieren
array input[SHRT_MAX] --> noise glätten --> def.Wortgrenzen --> Wort rausschneiden und ganz an den Anfang setzen
(--> array wavebuffer[SHRT_MAX] als Puffer), dann wieder zurückkopieren in
--> array input[SHRT_MAX]==reine sound-data zur Weiterverarbeitung


Menü (6) Sound als *.wav File speichern unter:
array wavebuffer[SHRT_MAX]: an den Anfang den wav-Header, dann direkt anschließend input[SHRT_MAX]
--> wavebuffer[SHRT_MAX] an SaveFileDialog ---> neuen Speichernamen eingeben
--> als *.wav file speichern


Damit wäre es sofort auch am einfachsten für dich, Wortmuster für den FFT-Vergleich aufzunehmen (Micro), zu bearbeiten, und neu zu speichern. Oder bereits vorhandene *.wav Dateien zu öffnen, nachzubearbeiten oder zu überschreiben.



0 Soundfile *.csv in Programm öffnen / laden (FileOpenDialog)
1 Soundfile *.wav in Programm öffnen / laden (FileOpenDialog)
2 Soundfile *.wav öffnen / abspielen + Plot (FileOpenDialog)
3 Sound aufnehmen per Micro / SoundCard
4 akt. Sound (Array im RAM) abspielen + Plot
5 akt. Sound optimieren (noise/cut)
6 akt. Sound (im RAM) als *.wav File speichern unter... (FileSaveDialog)
7 akt. Sound an FFT + Plot
8 akt. Sound cross correlation mit 1 wav File (FileOpenDialog)
9 akt. Sound cross correlation mit allen *.wav Files (auto)

hirnfrei
20.06.2016, 21:11
Was machst du nur mit mir? Ich wollte doch nur eine Spracherkennung und bastele jetzt wie ein kleines Kind zu Weihnachten ein altes DOS Menü nach ^^. Es geht voran! Ich habs auch bald. Ein Bild häng ich mal dran.

HaWe
20.06.2016, 22:34
für die Spracherkennung brauchst du doch jede Menge Vergleichsdaten, und die müssen nunmal möglichst optimal sein.
Die FFT kann ja auch nur vergleichen, was in etwa vergleichbar ist.
Schlechte Basisdaten => falsche Ergebnisse.

Da du ja deine Vergleichsdatenbank erstmal selber per Micro aufnehmen und dann analysieren und optimieren musst, und das Wort für Wort mit wiederkehrenden Algorithmen, alle nach dem selben Muster erstellt, bietet sich doch ein Menü mit diesen festen Regiefunktionen geradezu an, oder nicht? 8)

hirnfrei
20.06.2016, 23:05
Ich sage es mal so. Als jemand der gerne im Terminal arbeitet wäre meine Antwort eigentlich






da sich das Menü sehr einfach durch Startparameter umgehen lässt. Wie der Zufall aber nun mal so spielt musste ich, um mein System auf dem neuen Motherboard wieder ins laufen zu bringen, eine RescueCD verwenden. Da ich keine hatte und CDs ja echt out sind, habe ich das Ganze natürlich auf einen USB-Stick gemacht. Das mache ich normalerweise mit dd, wo ich dann einfach das iso auf den Stick schiebe. Aber bei der war ein install dabei. Okay versuche ich mal. Schau an, da war ein Menü im Terminal und sofort musste ich an dich denken. Mal kurz nach geforscht wie das erstellt wurde und so auf Ncurses gestolpert.

Und das hat mich so gereizt das ich da jetzt am bauen bin. So kommt die Jungfrau zum Kind, nichwahr?

Ah um das zu compilieren brauchst du libncurses-dev (glaube ich). Kannst ja mal im Packetmanager schauen. In geany muss noch



-lncurses


dazu.

HaWe
20.06.2016, 23:14
ogott - bitte nicht ncurses! O.o

Zenity ist doch perfekt!

und das Menü sind doch nur 10 zeilen, zur Abfrage einfach auf Tasteneingabe mit Enter warten!

Und ich will doch nicht jedesmal das Programm neu starten mit verschiedenen Parametern, für einfache wiederkehrende Funktionen - viel zu viel Aufwand und viel zu unflexibel !
wav sound bearbeiten muss interaktiv im Programm selber möglich sein!

hirnfrei
21.06.2016, 01:07
Ich bin ja schon am Bauen also ganz ruhig! Und ja ncurses. Find ich grade voll lustig. Macht mir Spass.

Es gibt aber grundlegende Unterschiede in der Auffassung. Was du Aufwand nennst finde ich einfach schneller und praktischer.

HaWe
21.06.2016, 09:48
ich habe einfach Berührungsängste was Monsterlibs angeht und möchte so was nur in äußersten, unumgänglichen Notfällen installieren - die sehe ich hier allerdings nicht wirklich: was soll ncurses können, was normale printf/sprintf-Befehle nicht können?

- - - Aktualisiert - - -

Die Menüabfrage ist ja auch total simpel:


// Terminal: move cursor
#define cursup "\033[A"
#define curshome "\033[0;0H"
#define cls_cons "\033[2J"

FILE * fp;
int32_t input[];

void printMenu() {
printf( cls_cons );
printf( curshome );

printf(" \n
0 Soundfile *.csv in Programm öffnen / laden \n
1 Soundfile *.wav in Programm öffnen / laden \n
2 Soundfile *.wav öffnen / abspielen + Plot \n
3 Sound aufnehmen per Micro / SoundCard \n
4 akt. Sound (Array im RAM) abspielen + Plot \n
5 akt. Sound optimieren (noise/cut) \n
6 akt. Sound (im RAM) als *.wav File speichern unter... \n
7 akt. Sound an FFT + Plot \n
8 akt. Sound cross correlation mit 1 wav File \n
9 akt. Sound cross correlation mit allen *.wav Files (auto) \n\n

q Quit \n\n"
}

int main() {
//...init dies
//...init das
int ch='\0';

while (ch!='q') {
ch='\0';
printMenu();

ch=getchar();

if ch=='q' exit(0);

else
if ch=='0' {fp=FileOpenDialog("*.csv"); // wrapper um popen mit Zenity
getArrayfromFile(input, SHRT_MAX, fp); }

else
if ch=='1' { fp=FileOpenDialog("*.wav"); // wrapper um popen mit Zenity
getArrayfromFile(input, SHRT_MAX, fp);
eraseWavHeader(input, SHRT_MAX); }

else
if ch=='2' { fp=FileOpenDialog("*.wav");
playWaveFile( fp ); // wrapper um popen mit Zenity
plotArray(input, SHRT_MAX ); }
else
if ch=='3' {recordArray(input, SHRT_MAX ); } // von Micro

else
if ch=='4' {playArray(input, SHRT_MAX );
plotArray(input, SHRT_MAX ); }
else
if ch=='5' {optimizeArray(input, SHRT_MAX); // (Nachfolger von analyseArray)
plotArray(input, SHRT_MAX ); }

else
if ch=='6' {buildWavFileArray(input, waveBuffer, SHRT_MAX ); // wav Header vor das Wort setzen
FileSaveDialog(fp, "*.wav"); } // wrapper um popen mit Zenity, als wav file speichern

else
if ch=='7' {;} // FFT, folgt

else
if ch=='8' {;} // FFT, folgt

else
if ch=='9' {;} // FFT, folgt

}
}

Ceos
21.06.2016, 10:09
ncurses kann menüs mit tastaturnavigation bereitstellen, schonmal versuch tastatureingaben sauber abzufangen und dann in nagivation umzusetzen !? glaub mir, da bricht man sich deutlich mehr einen dran ab als mit tinyalsa :P

und menüs im stil "eingabe 1-10" die dann den ganzen bildschirm dauernd scrollen lassen sind schon ein augenschmerz, da lob ich mir ein halbwegs festehendes Fenster

ABER .. das ist mal wieder eine Geschmacksfrage und über Geschmack streitet man sich nicht, ich kann zumindest die Argumentation von Hirnfrei insoweit verstehen.

PS: Berührungsängste sind die Ängste die man unbedingt überwinden muss!
Ohne Berühung keine Chance für Erfolg oder Misserfolg,
ohne Erfolg oder Misserfolg keine Erfahrung,
ohne Erfahrung kein Fortschritt

PPS: Auf ewig alten Schienen fahren lohnt sich nicht, man muss auch mal ein wenig anderen Menschen (oder deren Code) vertrauen entgegen bringen, vor allem wenn es zum Standard geworden ist ... auch ich zähle mich mittlerweilen was den Programmierstil angeht zu den ewig alten und muss zusehen dass ich bei den ganzen Biliotheken für Dinge die ich mal Bare Metal programmiert habe anpasse und das Rad nicht jedes mal neu erfinde.

HaWe
21.06.2016, 11:45
ja, wenn wir jetzt Super-Menüs bräuchten: verstehe ich ja.
Brauchen wir aber gar nicht.
ch=getchar() reicht, es müssen ja nur wenige Standardfunktionen wiederholt aufgerufen werden können,
um eine kleine wav-Datenbank zu erstellen und deren sounds zu optimieren,
und um dann, wenn man sie hat,
aktuelle wav-arrays mit dieser Datenbank cross-correlieren zu können.

Die wrapper um popen mit Zenity sind dann allerdings wirklich Gold wert.


ps,
#define cursup "\033[A"
#define curshome "\033[0;0H"
#define cls_cons "\033[2J"


stellt übrigens das Menü auf dem Screen zur Verfügung, ohne zu scrollen (s. Code oben) 8)

und da handle ich nach dem Motto "KISS" ;)

hirnfrei
21.06.2016, 14:55
Monstelibs? Ncurses müsste dir eigentlich gefallen weil es echt super einfach ist!

Du musst da aber keine Berührungsängste haben, ich mach das ja. Du benutzt es dann einfach nur, mehr nicht.


...möchte so was nur in äußersten, unumgänglichen Notfällen installieren - die sehe ich hier allerdings nicht wirklich

Ich sehe gar keine Notwendigkeit für ein Menü, da es auch problemlos über eine Terminaleingabe geht ;).

HaWe
21.06.2016, 15:52
Ich sehe gar keine Notwendigkeit für ein Menü, da es auch problemlos über eine Terminaleingabe geht
ich brauche leider für weitere Versuche auf jeden Fall die integrierte Regiefunktion mit Menü, da ich nie per Kommandozeile arbeite.
Außerdem müssen mehrere Schritte mit demselben Array im Speicher durchgeführt werden, plus zusätzliche Dateioperationen, und die Arrays im RAM sind ja weg beim Neustart - das wirst du auch merken, wenn wir soweit sind.

Das Menü habe ich dir oben aber ja bereits gepostet, es muss nur einfach in dein Programm mit rein.

Ich fürchte nur fast nach deinem Post:
dir ist noch nicht gar nicht klar, was als nächstes alles gemacht werden muss... ;)


Aber zurück zur Roadmap:

hast du denn inzwischen die ersten 3 Musterwörter erstellt (aufgenommen, "bereinigt", Rausch-Grenzen weggeschnitten, und dann gespeichert) ?
Und existiert inzwischen eine Funktion saveArray, die einen Array im RAM als *.wav file samt header speichert?

hirnfrei
21.06.2016, 15:56
Doch ist mir klar nur glaube ich dir ist noch nicht ganz klar wie mächtig die Eingabeaufforderung ist.

Du bekommst ja auch dein Menü, lass mich einfach nur machen.

Nein. Baue noch am Menü. Leider verschlingt die Familie doch ziemlich viel Zeit. Wenn ich ans Programmieren komme ist es oft schon nach 22 Uhr.

HaWe
21.06.2016, 15:56
es ist nicht "mein Menü", mit dem müssen wir beide arbeiten, ggf. synchron oder per "handshake".

hirnfrei
21.06.2016, 16:48
Deshalb mache ich das ja. Wie gesagt, ich selbst könnte ohne, aber da sich das ja mittlerweile zu einem Gemeinschaftsprojekt entwickelt hat bastel ich eben da das Menü. Das mich ncurses da jetzt so heiss gemacht hat konnte ich ja nicht wissen. Finde die Möglichkeiten und die Schlichtheit aber super und kommt so bisschen DOS Feeling auf.

HaWe
21.06.2016, 17:06
das Menü habe ich doch schon gebastelt, ohne ncurses.

hirnfrei
22.06.2016, 18:20
So mein Freund. Mein Spieltrieb ist durchaus entzückt. Hier das Ergebnis:

main.cpp



#include <iostream>
#include <vector>
#include <fstream>
#include <string>

#include <stdio.h>
#include <limits.h>
#include <math.h>

#include <VG/openvg.h>
#include <VG/vgu.h>
#include <fontinfo.h>
#include <shapes.h>

using namespace std;

string version = "2.1";

string _speicher = "leer";
string _status = "ungesichert";
string _datei = "";
string _fehler = "";

#include "diaSound.hpp"
#include "menu.hpp"

#define BLACK 0, 0, 0
#define WHITE 255, 255, 255
#define MAGENTA 255, 0, 255
#define RED 255, 0, 0
#define YELLOW 255, 255, 0
#define LIME 0, 255, 0
#define GREEN 0, 128, 0
#define CYAN 0, 255, 255
#define AQUA 102, 204, 204
#define BLUE 0, 0, 255

int _width_=1024, _height_=600;
int _fontsize_ = 10;

bool ausgabe = true;

void initOpenVG()
{
initWindowSize(0, 0, _width_, _height_);
init(&_width_, &_height_);
Start(_width_, _height_);
Background(0, 0, 0);
StrokeWidth(1.0);
WindowClear();
}

void plotArray(int32_t *array, int32_t arrlength, int y0)
{
float xscale=1.0, border=100.0;
xscale = _width_ / (float) (arrlength-border);

int i;

Fill(CYAN, 1);
Stroke(0, 255, 255, 1);

for(i=0;i<arrlength;i++)
{
Line((VGfloat)xscale*i, (VGfloat) y0, (VGfloat)xscale*i, (VGfloat)(y0+array[i]));
}
End();
}

void analyse(int32_t *array, int32_t arrlength)
{
int32_t sbuf[128];
int32_t bias, minr, maxr, baseline,
signalstart, signalend;

int32_t maximum = array[0];
int32_t minimum = array[0];

int32_t maxpos, minpos;

int32_t i;

for(i=0;i<arrlength;i++)
{
if(array[i] > maximum)
{
maximum = array[i];

maxpos = i;
}

if(array[i] < minimum)
{
minimum = array[i];

minpos = i;
}
}

maximum = array[0];
minimum = array[0];

for(i=0;i<arrlength; ++i)
{
if(array[i] > maximum)
{
maximum = array[i];
maxpos = i;
}
if(array[i] < minimum)
{
minimum = array[i];
minpos = i;
}
}


// calculate baseline from last 100 array cells:
// init vars
baseline=(array[minpos] + array[maxpos]) / 2; // init baseline by (min+max)/2

minr=baseline - 1;
maxr=baseline + 1;

// auto-adjust:
for(i=arrlength-100; i<arrlength; ++i)
{
// mean baseline
baseline = round((0.5*(float)array[i] + 0.5*(float)baseline)) ;

// smoothed out max noise
if(array[i] >= baseline) maxr = round((0.6*(float)array[i] + 0.4*(float)maxr)) +1 ;

// smoothed out min noise
if(array[i] <= baseline) minr = round((0.6*(float)array[i] + 0.4*(float)minr)) -1 ;
}

bias = max(baseline-minr, maxr-baseline) +1;

// noise reduction start/end
// drop small noise

for(i=0;i<arrlength;++i)
{
if((array[i]>baseline) && (array[i] <= baseline + bias)) array[i] = baseline ; // little higher value => drop to baseline
else
if((array[i]<baseline) && (array[i] >= baseline - bias)) array[i] = baseline ; // little lower value => rise to baseline
}


// signalstart, signalend: threshold = bias + (bias/2)
signalstart = 0;

i = 0;

while((array[i]<=baseline + 4 * bias/3) && (i<SHRT_MAX-1)) ++i;

signalstart = i;

if(i > 0) signalstart -= 1;

signalend=arrlength-1;

i=arrlength-1;

while((array[i]<=baseline + + 4*bias/3) && (i>signalstart)) --i;

signalend = i;

if(i<arrlength-1) signalstart +=1;

if(ausgabe)
{
cout << "Bias: " << bias << endl;
cout << "Maximal: " << maximum << endl;
cout << "Minimal: " << minimum << endl;
cout << "Signalstart: " << signalstart << endl;
cout << "Signalende: " << signalend << endl;
}
}

int main(int argc, char *argv[])
{
fstream datei;

string dateiEndung;
string dateiName;

int32_t waveBuffer[SHRT_MAX];

int32_t i, menuRet;

char s[3], m[3];

initOpenVG();

menuRet = menu(0, 0);

while(menuRet != -1)
{
switch(menuRet)
{
case 0:

datei.open(_datei, ios::in);

if(datei)
{
for(i=0;i<SHRT_MAX;++i)
{
datei >> waveBuffer[i];
}

datei.close();

plotArray(waveBuffer, SHRT_MAX, 0);
_speicher = "Wave vorhanden";
_status = "gesichert";
}
else _fehler = "Datei konnte nicht geöffnet werden!";

break;

case 1:

audioCapture(waveBuffer, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

_speicher = "Wave vorhanden";
_status = "ungesichert";
_datei = "";

break;

case 2:

datei.open(_datei, ios::in);

if(datei)
{
for(i=0;i<SHRT_MAX;++i)
{
datei >> waveBuffer[i];
}

datei.close();

_speicher = "Wave vorhanden";
_status = "gesichert";

playCaptured(waveBuffer, SHRT_MAX, "plughw:1,0", 1, 12000, 8);
}
else _fehler = "Datei konnte nicht geöffnet werden!";

break;

case 3:

playCaptured(waveBuffer, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

break;

case 5:

dateiEndung = ".hhw";

dateiName = _datei + dateiEndung;

datei.open(dateiName, ios::out);

if(datei)
{
for(i=0;i<SHRT_MAX;++i)
{
datei << waveBuffer[i] << endl;
}

datei.close();

_status = "gesichert";
}
else _fehler = "Die Datei konnte nicht geöffnet werden!";
break;

case -1:

return 1;

break;
}

menuRet = menu(0, 0);
}
/*
plotArray(waveBuffer, SHRT_MAX, 0);

analyse(waveBuffer, SHRT_MAX);

plotArray(waveBuffer, SHRT_MAX, 255);

SaveEnd("plott.raw");

finish();
*/
return 1;
}


diasound.hpp



#include <alsa/asoundlib.h>

#include <iostream>
#include <vector>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

using namespace std;

typedef struct _FILE_head
{
unsigned char ID[4];
unsigned int Length;
unsigned char Type[4];
} FILE_head;

typedef struct _FORMAT
{
short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
} FORMAT;

typedef struct _CHUNK_head
{
unsigned char ID[4];
unsigned int Length;
} CHUNK_head;

snd_pcm_t *soundKarte;

bool Init(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

unsigned int resample = 1;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
}

if(err < 0)
{
_fehler = "Init: Kann die Soundkarte nicht öffnen!";

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
_fehler = "Init: Parameter können nicht initialisiert werden!";

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
_fehler = "Init: Parameter können nicht ermittelt werden!";

return false;
}

err = snd_pcm_hw_params_set_rate_resample(soundKarte, hw_params, resample);

if(err < 0)
{
_fehler = "Init: Resampling kann nicht eingeschaltet werden!";

return err;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
_fehler = "Init: Zugriffstyp kann nicht gesetzt werden!";

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
_fehler = "Init: Sample-Format kann nicht gesetzt werden!";;

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
_fehler = "Init: Anzahl der Kanäle kann nicht gesetzt werden!";

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
_fehler = "Init: Sample-Rate kann nicht gesetzt werden!";

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
_fehler = "Init: Parameters können nicht gesetzt werden!";

return false;
}
snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
_fehler = "Init: Audio kann nicht zur Nutzung vorbereitet werden!";

return false;
}

return true;
}

bool InitCapture(string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err;

snd_pcm_format_t bits;

switch(WaveBits)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

snd_pcm_hw_params_t *hw_params;

if(name.length() == 0)
{
err = snd_pcm_open(&soundKarte, "plughw:1,0", SND_PCM_STREAM_CAPTURE, 0);
}
else
{
err = snd_pcm_open(&soundKarte, name.c_str(), SND_PCM_STREAM_CAPTURE, 0);
}

if(err < 0)
{
_fehler = "Init: Kann die Soundkarte nicht öffnen!!";

return false;
}

if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
_fehler = "Init: Parameter können nicht initialisiert werden!";

return false;
}

if((err = snd_pcm_hw_params_any(soundKarte, hw_params)) < 0)
{
_fehler = "Init: Parameter können nicht ermittelt werden!";

return false;
}

if((err = snd_pcm_hw_params_set_access(soundKarte, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
_fehler = "Init: Zugriffstyp kann nicht gesetzt werden!";

return false;
}

if((err = snd_pcm_hw_params_set_format(soundKarte, hw_params, bits)) < 0)
{
_fehler = "Init: Sample-Format kann nicht gesetzt werden!";

return false;
}

if((err = snd_pcm_hw_params_set_channels(soundKarte, hw_params, channels)) < 0)
{
_fehler = "Init: Anzahl der Kanäle kann nicht gesetzt werden!";

return false;
}

if((err = snd_pcm_hw_params_set_rate_near(soundKarte, hw_params, &actualRate, 0)) < 0)
{
_fehler = "Init: Sample-Rate kann nicht gesetzt werden!";

return false;
}

if((err = snd_pcm_hw_params(soundKarte, hw_params)) < 0)
{
_fehler = "Init: Parameters können nicht gesetzt werden!";

return false;
}

snd_pcm_hw_params_free(hw_params);

if((err = snd_pcm_prepare(soundKarte)) < 0)
{
_fehler = "Init: Audio kann nicht zur Nutzung vorbereitet werden!";

return false;
}

return true;
}

bool UnInit()
{
snd_pcm_close(soundKarte);

return true;
}

int playwave(string waveDatei, string name)
{
FORMAT format;
FILE_head head;
CHUNK_head chead;

char *wave;

register snd_pcm_uframes_t count, frames;

int datei;

unsigned int WaveSize;

datei = open(waveDatei.c_str(), 00);

read(datei, &head, sizeof(FILE_head));

read(datei, &chead, sizeof(CHUNK_head));

read(datei, &format, sizeof(FORMAT));

wave = (char *) malloc(head.Length);

read(datei, wave, head.Length);

WaveSize = head.Length * 8 / ((unsigned int)format.wBitsPerSample * (unsigned int)format.wChannels);

close(datei);

Init(name, format.wChannels, format.dwSamplesPerSec, format.wBitsPerSample);

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if (frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if (frames < 0)
{
printf("Kann wav nicht abspielen: %s\n", snd_strerror(frames));
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

free(wave);

UnInit();

return 0;
}

void audioCapture(int32_t *input, int max, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
int err, i=0;

int32_t *puffer;

puffer = (int32_t *) malloc(1);

if(InitCapture(name, channels, actualRate, WaveBits))
{
while(i < max)
{
err = snd_pcm_readi(soundKarte, puffer, 1);

if(err < 0) _fehler = "Fehler bei der Aufnahme!";

input[i] = puffer[0];

i++;
}

free(puffer);

UnInit();
}
else _fehler = "Bei der Initialisierung ist ein Fehler aufgetreten!";
}

void playCaptured(int32_t *array, unsigned int WaveSize, string name, unsigned int channels, unsigned int actualRate, unsigned short WaveBits)
{
register snd_pcm_uframes_t count, frames;

uint8_t *wave;

wave = (uint8_t *) malloc(WaveSize+1);

for(int32_t i=0;i<WaveSize;i++) wave[i] = array[i];

Init(name, channels, actualRate, WaveBits);

WaveSize = WaveSize * 8 / WaveBits * channels;

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, wave + count, WaveSize - count);

if(frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if(frames < 0)
{
_fehler = "Kann wav nicht abspielen";;
break;
}

count += frames;

} while (count < WaveSize);

if (count == WaveSize) snd_pcm_drain(soundKarte);

UnInit();
}


menu.hpp



#include <ncurses.h>
#include <dirent.h>

void quit()
{
endwin();
}

int subMenu(int32_t menu, string text)
{
WINDOW *w;

DIR *d;
struct dirent *dir;

int32_t i, ch, posMenu, max, start, ende, z;

vector<string> dateien;

switch(menu)
{
case 0:

w = newwin(16, 19, 4, 29);

scrollok(w, true);
keypad(w, TRUE);

wbkgd(w, COLOR_PAIR(1));

box(w, 0, 0);

d = opendir(".");

if(d)
{
while((dir = readdir(d)) != NULL)
{
if(strstr(dir->d_name, ".hhw")) dateien.push_back(dir->d_name);
}

closedir(d);
}

ende = dateien.size() - 1;

if(dateien.size() > 14) max = 14;
else max = dateien.size();

for(i=0;i<max;i++)
{
if(i == 0)
{
wattron(w, A_STANDOUT);
mvwprintw(w, i+1, 1, "%-17s", dateien[i].c_str());
wattroff(w, A_STANDOUT);
}
else mvwprintw(w, i+1, 1, "%-17s", dateien[i].c_str());
}

start = 0;

while(ch = wgetch(w))
{
switch(ch)
{
case KEY_UP:

posMenu--;

if(posMenu < 0) posMenu = 0;

if(posMenu == start && posMenu > 0)
{
start--;
max--;
}

break;

case KEY_DOWN:

if(posMenu < ende) posMenu++;

if(posMenu == max && posMenu < ende)
{
start++;
max++;
}

break;

case 10:

_datei = dateien[posMenu];

return 1;

break;
}

clear();

for(i=start,z=1;i<max;i++,z++)
{
if(i == posMenu)
{
wattron(w, A_STANDOUT);
mvwprintw(w, z, 1, "%-17s", dateien[i].c_str());
wattroff(w, A_STANDOUT);
}
else
{
mvwprintw(w, z, 1, "%-17s", dateien[i].c_str());
}
}
wrefresh(w);
}
wrefresh(w);

return 0;

break;

case 1:

w = newwin(5, 18, 4, 30);

wbkgd(w, COLOR_PAIR(1));

box(w, 0, 0);

mvwprintw(w, 1, 2, text.c_str());

wrefresh(w);

return 1;

break;

case 5:

w = newwin(4, 29, 11, 30);

char inputDatei[20];

wbkgd(w, COLOR_PAIR(1));

box(w, 0, 0);

mvwprintw(w, 1, 2, "Dateiname zum Speichern");
mvwprintw(w, 2, 2, "->");

move(2, 5);
curs_set(1);

mvwgetnstr(w, 2, 5, inputDatei, 14);

_datei = inputDatei;

wrefresh(w);

return 1;

break;
}

return -1;
}



int menu(int32_t menu, int32_t posMenu)
{
char mainMenu[7][20] = {"Datei laden", "Wave aufnehmen", "Datei abspielen", "Speicher abspielen", "Speicher optimieren", "Speicher sichern", "Beenden"};

int32_t i, ch;

initscr();
atexit(quit);
curs_set(0);

start_color();
clear();

init_pair(1, COLOR_WHITE, COLOR_BLUE);
bkgd(COLOR_PAIR(1));

border(0, 0, 0, 0, 0, 0, 0, 0);

mvhline(2, 0, ACS_LTEE, 1);
mvhline(2, 1, ACS_HLINE, 78);
mvhline(2, 79, ACS_RTEE, 1);

mvhline(21, 0, ACS_LTEE, 1);
mvhline(21, 1, ACS_HLINE, 78);
mvhline(21, 79, ACS_RTEE, 1);

mvprintw(1, 3, "HaWe und Hirnfrei\'s Soundprojekt V %s", version.c_str());
mvprintw(22, 3, "Meldung: %s", _fehler.c_str());

refresh();

WINDOW *w, *sw;

sw = newwin(5, 28, 4, 50);

wbkgd(sw, COLOR_PAIR(1));

box(sw, 0, 0);

mvwprintw(sw, 1, 2, "Speicher: %-15s", _speicher.c_str());
mvwprintw(sw, 2, 2, "Status: %-15s", _status.c_str());
mvwprintw(sw, 3, 2, "Datei: %-15s", _datei.c_str());

wrefresh(sw);

w = newwin(15, 25, 4, 2);

wbkgd(w, COLOR_PAIR(1));

box(w, 0, 0);

keypad(w, TRUE);

for(i=0;i<7;i++)
{
if(i == posMenu)
{
wattron(w, A_STANDOUT);
mvwprintw(w, i+1, 2, "%s", mainMenu[i]);
wattroff(w, A_STANDOUT);
}
else
{
mvwprintw(w, i+1, 2, "%s", mainMenu[i]);
}
}
wrefresh(w);

while(ch = wgetch(w))
{
switch(ch)
{
case KEY_UP:

posMenu--;

if(posMenu < 0) posMenu = 6;

break;

case KEY_DOWN:

posMenu++;

if(posMenu > 6) posMenu = 0;

break;

case 10:

switch(posMenu)
{
case 0:

if(subMenu(0, "") == 1) return 0;

break;

case 1:

if(subMenu(1, "Aufzeichnung...") == 1) return 1;

break;

case 2:

if(subMenu(0, "") == 1) return 2;

break;

case 3:

if(subMenu(1, "Spiele ab...") == 1) return 3;

break;

case 5:

if(_speicher.compare("leer") != 0) if(subMenu(5, "") == 1) return 5;

break;

case 6:

return -1;

break;
}

break;
}

for(i=0;i<7;i++)
{
if(i == posMenu)
{
wattron(w, A_STANDOUT);
mvwprintw(w, i+1, 2, "%s", mainMenu[i]);
wattroff(w, A_STANDOUT);
}
else
{
mvwprintw(w, i+1, 2, "%s", mainMenu[i]);
}
}
wrefresh(w);
}
}


Da gibts doch jetzt nichts mehr zu meckern ;). Jetzt gehts dann ans optimieren usw.

Hast du mittlerweile eine Soundkarte usw? Kannst du auch Sounds aufnehmen und abspielen?

Die alten csv Dateien kannst du damit auch abspielen, musst sie nur in .hhw (HaWe - Hirnfrei - Wave ^^) umbenennen.

- - - Aktualisiert - - -

Nebenbei, ich baue gerade das Plotten etwas um, so dass es in einem Fenster dargestellt wird und nicht so dumm auf den Screen geschrieben wird. Das sollte keine grosse Geschichte sein und macht die Sache etwas komfortabler.

HaWe
22.06.2016, 18:42
hallo,
nein, habe noch keine soundcard, und auch noch kein ncurses installiert.
Leider durchblicke ich dein Menü nicht, es ist - nun - etwas - uummmh - deutlich - ääähh - aufgeblähter als meins ;)

0 Soundfile *.csv in Programm öffnen / laden \n
1 Soundfile *.wav in Programm öffnen / laden \n
2 Soundfile *.wav öffnen / abspielen + Plot \n
3 Sound aufnehmen per Micro / SoundCard \n
4 akt. Sound (Array im RAM) abspielen + Plot \n
5 akt. Sound optimieren (noise/cut) \n
6 akt. Sound (im RAM) als *.wav File speichern unter... \n

7 akt. Sound an FFT + Plot \n
8 akt. Sound cross correlation mit 1 wav File \n
9 akt. Sound cross correlation mit allen *.wav Files (auto) \n\n


kann es denn schon alles wie geplant (0-6, 7-9 kommt ja erst noch) ?
aber dann hast du sicher schon die 3 geplanten Grundwörter aufgenommen und optimiert ? 8)


ps,
ich finde noch keine Funktion, um einen sound-array als *.wav zu speichern - wie heißt die bei dir?

hirnfrei
22.06.2016, 19:17
Die heisst "Gibts nicht".

Ja ist etwas aufgeblähter, funktioniert dafür super ^^. Samt eventueller Fehlermeldungen usw.

Was bisher funktioniert:

0, 1 ist bei mir der erste Menüpunkt "Datei laden". Da zeigt sich dann in der Mitte vom Menü eine Auswahl alles vorhandenen Dateien (also alle hhw Dateien), sogar scrollbar. Da wird die Datei nur in den Speicher geschrieben und kann dann nach Belieben weiter verarbeitet werden. Derzeit also abspielen, optimieren und speichern.

2 ist bei mir "Datei abspielen" und funktioniert wie 0-1 nur das die Datei eben sofort nach dem Laden auch abgespielt wird.

3 ist bei mir "Wave aufnehmen". Die Daten von der Soundkarte werden in das array geschrieben und liegen im Speicher. Wie beim Laden kann man damit dann auch alles Mögliche damit machen. Abspielen, optimieren, speichern...

4 ist bei mir "Speicher abspielen. Spielt schon prima ab, plottet aber noch nicht weil ich das noch in ein Fenster packen will. Ich weiss nicht aber so direkt in den Bildschirm schreiben fühlt sich irgendwie "falsch" an.

5 Optimieren habe ich zu meiner Schande vergessen einzubauen. Der Menüpunkt ist da aber ich hab vergessen ihn funktionsfähig zu machen :(

6 ist bei mir "Speicher sichern". Wählt man das an wird man zur Eingabe eines Dateinamens aufgefordert. Die Dateiendung wird automatisch hinzugefügt.

7-9 füge ich dann noch hinzu.

Lach nee ich hab die Worte noch nicht aufgenommen.

HaWe
22.06.2016, 19:26
openVG schreibt immer direkt in den Fensterbereich, es hat nunmal keine XWindows Funktion.
Ich habe bei mir immer einen openVG Fenster-Bereich für die Grafiken neben einem Terminal-Fenster für die Schrift-Ausgabe (ohne ncurses).
Von ncurses bin ich einfach nicht überzeugt, es funktionieren dort einfach die normalen printf Befehle nicht korrekt (\n etc), und deshalb nutze ich es nicht.

hirnfrei
22.06.2016, 19:29
Brauchst ja nicht damit zu programmieren, reicht ja wenn du es benutzt oder ^^.

OpenVG schreibt direkt in die GPU, bzw. dessen Speicher. Das kann man aber anscheinend auch in ein Fenster leiten, was die Sache angenehmer macht wie ich finde.

HaWe
22.06.2016, 19:38
wie unterscheidet das Programm, ob es einen .wav File oder einen .csv File lädt?

- - - Aktualisiert - - -

ps,

Datei laden und abspielen muss man trennen -
es muss abspielen möglich sein (z.B. zum Test), ohne dass die wave Daten ins Programm geladen werden!

hirnfrei
22.06.2016, 19:41
Warum sollte man ein wav laden? Ich verstehe deine Affinität zu wav Dateien nicht wirklich :(.

Achso. Okay das baue ich um ist kein Ding.

HaWe
22.06.2016, 19:45
wav ist das universelle Standard-Datenformat für sounds, es werden daher grundsätzlich *.wav gespeichert und geladen.
Die *.wav Files kann man dann auch mit unabhängigen Programmen abhören (und auch bearbeiten), z.B. mit Omxplayer oder mit Windows wave-edit-Programmen.

hirnfrei
22.06.2016, 19:54
Ich weiss was wav ist. So ganz dämlich bin ich dann ja doch nicht.

Okay dann kann man sie in einem anderen Programm abspielen. Leuchtet ein, nur warum sollte man das tun? Man kann sie in unserem Programm ja schliesslich auch abspielen. Und in einem anderen Programm bearbeiten leuchtet auch ein, aber da nochmal die Frage nach dem Grund. Ich meine, wenn ich mich nicht irre soll ja unser Programm am Ende ganz alleine das Wort verstehen. Dann sollte das Programm mit der notwendigen Bearbeitung ja auch zurecht kommen oder?

Fenster lasse ich doch erstmal bleiben. Ich denke da du wahrscheinlich nach wie vor auf dem Raspi das schwache LXDE benutzt wäre der Installationsaufwand zu gross.

HaWe
22.06.2016, 20:30
Es ist im Moment von mir als ein Regie-Programm zur Vorbereitung und zum Test gedacht, mit universellen Schnittstellen zu Standard-Datenformaten, und das heißt:
*.wav - Standard.

Das "echte" Spracherkennungsprogramm wird das sein, das danach kommt und auf den Tests des Regieprogramms aufbaut... ;)

Trotzdem werden Sounds immer als wav gespeichert und wieder geladen, niemals anders, sonst ist es nicht mehr Standard-kompatibel über verschiedene Plattformen hinweg (z.B. auch für die Anwendung externer Sound-Filter).

hirnfrei
22.06.2016, 20:59
Du sagst es ja. Es ist ein "Regie-Programm". Warum muss das über verschiedene Plattformen hinweg kompatibel sein?

Bleibt das Plotten jetzt in einer Spur oder wird mehr auf einem Bildschirm geplottet?

HaWe
22.06.2016, 21:39
zum Plotten:
es wäre vlt gut, wenn wir später verschiedene Plots vergleichen könnten, also dann 2-3 untereinander, ich weiß noch nicht, was auf uns zu kommt:
vlt auch oben ein Sound-Plot, darunter der FFT-Graph.
oder 2 Sounds und 2 FFT-Graphen.
Eine variable Basis-Null-Linie (x-Achse) macht die Sache sicher sehr übersichlich.

zum Regie-Programm:
auch da weiß ich noch nicht, ob wir mit der FFT und dem "Roh-Sound" Erfolg haben, gerade wenn ich spreche und deine Muster verwende, oder auch wenn verschieden starke Störungen dabei sind, oder wenn Wörter verschieden schnell gesprochen werden.
Auch was die FFT draus macht, kann ich nur grob erahnen.
Bisher haben wir ja nur die Möglichkeit, Bereiche vorher/nachher abzuschneiden - der Klang an sich wurde nicht verändert.
Es kann also heißen, daß die Vielzahl an Begleit-Frequenzen reduziert werden muss durch Hoch- oder Tiefpassfilter, damit der signifikante Bereich möglichst prägnant herausgestellt wird und unwichtige Frequenzen eliminiert werden.
Keine Ahnung, welche das sein werden - hohe Frequenzen können durchaus wichtig sein (Zisch- oder harte "t"-Laute oder Gutturale etc. ) - trotzdem mag es sein, dass die tiefen Anteile des Klangs (z.B. bis 1000 Hz) ausreichend sind.
Je nachdem, wie die CC Ergebnisse sind, muss man also mit den Klängen experimentieren, aber dafür einen eigenen Equalizer noch zu programmieren, ist sicher etwas hoch gegriffen.
Daher die Option, dafür zunächst externe wav-edit-Programme verwenden zu können bzw. können zu müssen.

hirnfrei
22.06.2016, 23:21
Was mir eben nicht so gefällt ist dieses Statische beim direkt in den Grafikspeicher schreiben. Man kann es nicht verschieben und gar nichts. Das ist so gar nicht mein Ding. Okay wenn man nur eine Anzeige hat die man danach eh wieder weg klickt von mir aus, aber wenn man das länger verwenden will ist der Teil vom Bildschirm ja futsch. Unschön. GTK wäre da eben eine nette Lösung gewesen, dafür gibt es auch Beispiele, aber dann müsstest du dir erstmal das komplette GTK 3 plus dev installieren. Das ist wohl übertrieben.

Ich habe aber noch die Möglichkeiten von OpenWF und OpenCV gefunden. Das könnte eine Möglichkeit sein. Mit OpenCV arbeite ich ja sowieso wegen Gesichtserkennung usw. Mal schauen was da geht. Wäre in meinen Augen angenehmer, wenn man das Bild auch in den Hintergrund schicken kann bei Bedarf.

Hast du eigentlich das mit fftw.org (www.fftw.org) gesehen?

HaWe
23.06.2016, 07:15
sorry, bitte bleibe bei den Tools, so wie vereinbart, ich werde nichts anderes installieren als mein jetziges openVG, also auch kein openCV.
Ich muss schließlich das Programm und seine einzelnen API-Tools auch selber verwenden und die Tools entsprechend selber weiter programmieren können.
Dazu muss ich die Funktionen kennen und selber nutzen können.

Für andere FFT-Funktionen als die von mir verlinkte sehe ich auch keinen Zusatz-Nutzen - wozu also wechseln? Die Cross-Correlation wird als Herausforderung schwierig genug werden. Es ist ja komplettes Neuland für mich, ich muss mich da selber Schritt für Schritt langsam einarbeiten und hineinfinden. Bei zuviel neuen Dingen verliere ich den Überblick.

Ich habe mich inzwischen deshalb auch gegen ncurses entschieden, weil ich mich dann in dem Menü nicht mehr zurechtfinde, und außerdem sind ncurses-Fenster für andere Ausgabefunktonen nicht mehr stdio.h-kompatibel, so wie es das LXTerminal-Window ist. Nur für diese einmalige simple Auswahl 0...9 eine inkompatible Monsterlib zu installieren, die ich später nicht mehr weiter verwende, ist für mich ein unwirtschaftlicher und nicht angemessener Aufwand.

Was in 5 oder 6 Jahren mal sein wird, weiß ich ntl nicht, da kann ich durchaus so weit sein, dass ich auch möglicherweise mal openCV und ncurses verwenden könnte.

Ich brauche als gemeinsame Regie-Schnittstelle auch genau das Menü, das ich oben bereits vorprogrammiert und dir gepostet hatte.

Ich bin kein Linux-Programmierer, ich programmiere auf MSDOS- TurboC oder Arduino-IDE-Niveau, und ich verwende selber auch kein C++, sondern ausschließlich ANSI C.
Daher auch meine absolute Abneigung genüber Vektoren und strings, stattdesen: char * array[size].

Wenn mir das Programm zu kompliziert und von der Syntax her zu fremd ist, kann ich es selber später nicht mehr für meine eigenen Spracherkennungsprogramme nutzen, und wozu dann der ganze Aufwand?

Bitte bleib also bei einfachen Menü-Schnittstellen wie diesen hier (Menü-Punkt 0 für csv kann jetzt sogar komplett wegfallen)



0 Soundfile *.csv in Programm öffnen / laden \n // csv kann jetzt sogar komplett wegfallen !

1 Soundfile *.wav in Programm öffnen / laden \n
2 Soundfile *.wav öffnen / abspielen + Plot \n
3 Sound aufnehmen per Micro / SoundCard \n
4 akt. Sound (Array im RAM) abspielen + Plot \n
5 akt. Sound optimieren (noise/cut) \n
6 akt. Sound (im RAM) als *.wav File speichern unter... \n

7 akt. Sound an FFT + Plot \n
8 akt. Sound cross correlation mit 1 wav File \n
9 akt. Sound cross correlation mit allen *.wav Files (auto) \n


und nutze bitte diese API-Schnittstellen mit dieser Syntax, wie ich sie im Prinzip bereits vor vielen Seiten gepostet hatte, nicht wesentlich anders:


a) record_sound(int32_t * array, int32_t length);
b) save_sound2wavFile(int32_t * array, FILE * fp, char * filename); // über Zenity PopUp Window
c) FILE * fp = open_wavFile(int32_t * array, char * filename); // über Zenity PopUp Window
d) play_soundArray(int32_t * array, int32_t length);
e) play_wavFile(char * filename); // über Zenity PopUp Window
sowie
f) plotArray(int32_t * array, int32_t length, int y0);
g) optimizeArray(int32_t * array, int32_t length);
h) array2FFT(int32_t * array, int32_t length, double * fx, double * fy, int32_t FFT_length);
i) cross_corr(int32_t * array, int32_t * pattern, int32_t length);


Diese Funktionen müssen genau so in der Sound-Lib und ggf. in einer weiteren Sound-Tools-Lib genau so zur Verfügung stehen.

Nur wenn wir diese gemeinsame Menü- und API-Basis haben, kann unsere Arbeit auch später auf andere Anwendungen (z.B echte Wort-Steuerung von mobilen Robotern mit automatischer lernfähiger Spracherkennung) portiert werden.


ps,

Wenn du das LXTerminal Window neben dem openVG Window positionieren und in der Größe anpassen willst, können wir diesen Befehl verwenden:


// LXTerminal window: pos (x,y): 520,0, size: 320x320
system("wmctrl -r LXTerminal -e 0, 520,0, 320,320");

hirnfrei
23.06.2016, 11:40
Also zum Mitschreiben. Dir wird die Variante mit ncurses zu kompliziert? Obwohl du mit ncourses eigentlich nichts zu tun hast und es in der Struktur in Main eigentlich sehr klar zu sehen ist was wie wo passiert? Ich meine, da sind die ganzen case Aufrufe. Diese rufen sehr einfach die Funktionen auf die für die Spracherkennung benötigt werden. Da ist nicht viel Schnick schnack drum rum und das kann man dann auch ziemlich einfach in jede andere Menüstruktur ganz nach Belieben einbauen. Das hat weder was mit Linux-Programmierung noch sonst etwas zu und da ncurses meines Wissens nach auch auf WIndows verfügbar ist wäre sogar eine Portierung kein wirkliches Problem, aber wie gesagt, wie am Ende das Menü aussieht, ob man es ganz minimalistisch über eine Eingabeaufforderung oder bis hin zu einem aufgeblasenen QT oder Windows Fenster haben will ändert nichts an den eigentlichen Funktionen.



switch(menuRet)
{
case 0:

datei.open(_datei, ios::in);

if(datei)
{
for(i=0;i<SHRT_MAX;++i)
{
datei >> waveBuffer[i];
}

datei.close();

_speicher = "Wave vorhanden";
_status = "gesichert";
}
else _fehler = "Datei konnte nicht geöffnet werden!";

break;

case 1:

audioCapture(waveBuffer, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

_speicher = "Wave vorhanden";
_status = "ungesichert";
_datei = "";

break;

case 2:

datei.open(_datei, ios::in);

if(datei)
{
for(i=0;i<SHRT_MAX;++i)
{
datei >> waveTemp[i];
}

datei.close();

_fehler = "Datei " + _datei + " wurde abgespielt!";

_datei = "";

playCaptured(waveTemp, SHRT_MAX, "plughw:1,0", 1, 12000, 8);
}
else _fehler = "Datei konnte nicht geöffnet werden!";

break;

case 3:

playCaptured(waveBuffer, SHRT_MAX, "plughw:1,0", 1, 12000, 8);

break;

case 4:

optimieren(waveBuffer, SHRT_MAX);

_fehler = "Speicher optimiert!";

_speicher = "Wave optimiert";
_status = "ungesichert";
_datei = "";

break;

case 5:

plotArray(waveBuffer, SHRT_MAX, 0);

break;

case 6:

dateiEndung = ".hhw";

dateiName = _datei + dateiEndung;

datei.open(dateiName, ios::out);

if(datei)
{
for(i=0;i<SHRT_MAX;++i)
{
datei << waveBuffer[i] << endl;
}

datei.close();

_status = "gesichert";
}
else _fehler = "Die Datei konnte nicht geöffnet werden!";
break;

case -1:

return 1;

break;

case -2:

_fehler = "Der Speicher ist leer! Erst laden oder aufnehmen!";

break;
}


Genau das hier ist das Einzige was für die Spracherkennung dann irgendwie relevant ist. Ich denke das sollte man in jede für sich bevorzugte Variante umstricken können. Denn wo nun menuRet seinen Wert her bekommt ist ja eigentlich völlig egal, ob es nun über Zenity, den Terminal oder irgendein Fenster geschieht.

Du willst kein OpenCV benutzen, okay damit kann ich leben. Wäre eine (portable) Möglichkeit gewesen das etwas bequemer zu haben. Aber gut, muss ja nicht.

HaWe
23.06.2016, 11:54
ich nutze ausschließlich das LXTerminal für printf() etc., mit angepasster Größe an definierter Position, und ich habe keinen Platz für 3 Fenster (openVG, LXTerminal und ncurses) - und warum ncurses, wenn es auch ohne geht und ich es sowieso sonst niemals nutze?


Befehle wie dieser hier


case 0:

datei.open(_datei, ios::in);

if(datei)
{
for(i=0;i<SHRT_MAX;++i)
{
datei >> waveBuffer[i];
}

datei.close();

_speicher = "Wave vorhanden";
_status = "gesichert";
}
else _fehler = "Datei konnte nicht geöffnet werden!";

break;


müssen in eine eigene Funktions-Wrap hinein, und die müssen diese Strukturen haben:

a) record_sound(int32_t * array, int32_t length);
b) save_sound2wavFile(int32_t * array, FILE * fp, char * filename); // über Zenity popen PopUp Window
c) FILE * fp = open_wavFile(int32_t * array, char * filename); // über Zenity popen PopUp Window
d) play_soundArray(int32_t * array, int32_t length);
e) play_wavFile(char * filename); // über Zenity popen PopUp Window


keine C++ Parameter, damit kann ich nicht arbeiten!
Ich bin ANSI C Programmierer, ich kann kein C++ selber benutzen für meine eigenen Zwecke.
Fehlerbehandlungen gehören in die Funktion mit hinein, nicht in das Menü!
Zenity fängt aber die File_IO fehler sowieso selber ab (man kann ja nur auswählen, was auch angezeigt wird)!
Und bitte immer nur 3 Leerzeichen für jede Einrückung, keine halbe Zeilenlänge!

hirnfrei
23.06.2016, 12:16
Wir haben offensichtlich sehr unterschiedliche Auffassungen...

zumal ncurses im Terminal läuft und kein eigenes Fenster hat.

HaWe
23.06.2016, 12:21
ja, naja, ich bin halt absoluter Programmieranfänger auf Arduino Sketch Level, brauche aber ntl alle Funktionen auch für meine eigenen Zwecke und Anwendungen.

hirnfrei
23.06.2016, 12:42
Warum habe ich denn schon soweit es möglich war alles auf int32_t usw. umgestellt? Das ist auch mit ein Grund warum ich überhaupt angefangen habe ein Menu da rein zu stricken und ich versuche auch so gut es geht da Funktionen ein zu bauen die ein späteres Verwenden in den eigentlichen Anwendungsgebieten auch vereinfacht.

Zumal das was ich gepostet habe da aus meinem Code ist nicht das Menü, sondern nur was die eizelnen Aufrufe vom Menü letzten Endes machen. Das Menü selbst ist da gar nicht zu sehen. Wie gesagt, ist alles so angelegt das man es theoretisch mit sehr geringem Aufwand mit jeder Form der Auswahl betreiben kann.

Ceos
23.06.2016, 16:01
hatte ich schonmal erwähnt dass ihr euren Code z.B. auf Github hochladen, forken und bearbeiten bzw. synchronisieren und kommentieren könnt? Ich will nicht dass ihr eure Entwicklung dahin beschränkt oder sowas, ich hab nur das Gefühl, dass es euch wesentlich helfen könnte einfach mit der gleichen Code basis zu arbeiten und darauf dann jeweils eine eigene Menüstruktur aufzubauen :D

Ihr ladet einfach den Kern des Programms hoch, schön gepackt und unterteilt in Methoden und Prozeduren und dann kann jeder in seinem eigenen Fork davon das Menü implementieren wie er es wünscht :D
Sonst gibts irgendwann nur noch streit und die Trennung von Kern und GUI tut dem Programmierstil gut und es kann alles dokumentiert werden und wenn was schiefgeht einfach ein rollback machen

HaWe
23.06.2016, 16:13
wie Github aussieht, weiß ich, aber wie man damit arbeitet: kein Plan, und da will ich mich auch gar nicht erst einarbeiten müssen.

wesentlich sind im Moment nur die API-Tool-Funktionen samt Menü.

Was das Menü angeht, heißt das für mich:
ohne ncurses,

und was die API wave-Tools angeht, eben einfach so etwas ähnliches, in ANSI C (unter Zuhilfenahme von Zenity und popen(), wie ich es ja auch schon gepostet habe):

a) record_sound(int32_t * array, int32_t length);
b) save_sound2wavFile(int32_t * array, FILE * fp, char * filename); // über Zenity popen() PopUp Window
c) FILE * fp = open_wavFile(int32_t * array, char * filename); // über Zenity popen() PopUp Window
d) play_soundArray(int32_t * array, int32_t length);
e) play_wavFile(char * filename); // über Zenity popen() PopUp Window
sowie
f) plotArray(int32_t * array, int32_t length, int y0);
g) optimizeArray(int32_t * array, int32_t length);
h) array2FFT(int32_t * array, int32_t length, double * fx, double * fy, int32_t FFT_length);
i) cross_corr(int32_t * array, int32_t * pattern, int32_t length);


wenn wir uns auf diese Schnittstellen einigen können, ist doch alles prima, damit könnte ich anfangen zu arbeiten.

hirnfrei
23.06.2016, 17:33
Mein Problem ist folgendes.

Wie du so schön sagst, du brauchst es so und so und so. Das ich in dem Programm hier aber schon massiv von meinem eigenen Programmierstil abgewichen bin ist dir klar oder? Ich habe noch nir int32_t verwendet zum Beispiel. Ich hätte nie angefangen ein Wav File zu dekodieren. Du sagst es ja, du brauchst es so und so um in deinen Projekten damit arbeiten zu können. Ich will jetzt nicht den Egoisten raus hängen lassen, aber schonmal auf den Gedanken gekommen das ich in meinen Projekten dann wieder umstricken muss?


wenn wir uns auf diese Schnittstellen einigen können

Ist das dann geeinigt oder von deiner Seite durchgesetzt?

Ich frage deshalb weil:


e) play_wavFile(char * filename); // über Zenity popen() PopUp Window

Du willst da also eine Funktion, die von sich aus dann Zenity verwendet. Das heisst, will ich es später mal in meinem Projekt damit arbeiten muss ich den ganzen Zenity-Kram erst raus werfen. In meiner Variante wird per Menu einfach die Datei ausgewählt und dann an die Funktion übergeben. Das heisst, will ich es später mal in einem anderen Programm verwenden, eins was keine direkte Benutzerschnittstelle hat, kann ich die in dem Programm verwendete Methode zur Auswahl der Datei verwenden und die Funktion dann ungeändert einfach übernehmen, sprich einfach diasound.hpp einbinden und die Funktion benutzen. Nehmen wir mal an ich würde diese Funktion dafür benutzen wollen, eine Reihe von Dateien, welche spezifische Worte enthalten nach einander abzuspielen. Sagen wir ich habe einen Satz "Hallo Welt" den ich mittels vorgefertigten Wort-Dateien dann ausgeben möchte. Dann würde ich normalerweise ein vector mit den Dateinamen generieren und diese in einer Schleife abrufen. Das würde nicht funktionieren da ja Zenity aufgehen würde, was ich allerdings gar nicht sehen würde, wenn zB kein Monitor angeschlossen ist.

"Einigen" wir uns auf dein Modell heisst das genau genommen, es funktioniert so das du damit arbeiten kannst in deinen Projekten während ich in meinen erst Aufwand betreiben muss um die erarbeiteten Funktionen zu verwenden. Das würde ich jetzt nicht als Win-Win ansehen.

@ceos


hatte ich schonmal erwähnt dass ihr euren Code z.B. auf Github hochladen...

HaWe hat schon die Antwort geliefert, warum ich damit noch nicht gekommen bin ^^

HaWe
23.06.2016, 19:19
Vielleicht kann dir ja wer anderes helfen auf der Basis von C++ und ncurses, ich muss da leider passen.
Und ich kann ja auch nicht garantieren, dass es dann später mit ANSI C und Zenity klappt.

hirnfrei
23.06.2016, 20:17
Wir können es ja ganz schlau machen. Da ich die Funktionen ja sowieso so aufbaue, dass sie auch in anderen Programmen genutzt werden können machen wir es doch ganz einfach. Wir deklarieren eine globale Variable. Steht die auf 0 wird mein Menü gestertet, bei 1 deins. Wenn das okay für dich ist kann ich dir zeigen wie ich das meine.

peterfido
23.06.2016, 22:19
Hallo,

soweit ich das verstanden habe, sollte man ncurses ODER die Standardausgaben nutzen. Das würde dann bald doppelten Code bei den Ausgaben bedeuten, es sei denn, Du kannst da per DEFINE was tricksen.
Zenity geht nur mit einem Fenstermanager. Soll es eine reine 'Konsolen-Anwendung' werden, dann muss eine Alternative her.

Ihr könntet auch gemeinsam genutzten Code in eine INCLUDE auslagern und die Oberfläche jeder für sich bauen.

Ein WAV-Export / -Import hätte den Vorteil, dass man die Dateien schnell mal auf 'Profisoftware' durch ein paar Filter jagen kann. Hinterher könnte man vor Filter mit nach Filter im neuen Format vergleichen / analysieren, um da was nachzubauen. Spracherkennung ist nicht ganz ohne, vor Allem, wenn unterschiedliche Sprecher erkannt und unter schwierigen Bedingen zuverlässig erkannt werden soll. Das schafft, wie bereits erwähnt, nicht mal Google in der Cloud zuverlässig. Euer Vorteil ist, dass es ein begrenzter Wortschatz ist.

Das sind alles nur Ideen. Ich selbst benötige ein solches Programm nicht. Verfolge jedoch Euren Fortschritt.

hirnfrei
23.06.2016, 22:34
So war es gemeint mit der Include. Ich nutze meine, HaWe seine. Bzw. damit man nicht zu viel im Code ändern muss einfach eine Variable ändern so das das Programm weiss welche Variante genutzt wird und auch nur dessen Output dann angezeigt wird. Ist keine schwere Aufgabe.

Und ja Zenity will meines Wissens nach GTK. zumindest basiert meines Wissens nach LXDE auch zum Teil darauf. ncurse hingegen läuft ganz ohne X.

Und noch ist der Wortschatz begrenzt. Zumindest auf meiner Seite darf das auf Dauer durchaus mehr werden. Wenn es richtig gut läuft können es dann auch ganze Sätze werden. Dann ist eine 100%ige Übereinstimmung der Wörter auch nicht mehr ganz so wichtig, da das Programm dann mehr oder minder "versteht", dass das was da angekommen ist nicht wirklich sinnvoll ist und prüft was da eher Sinn machen würde. Funktioniert im Terminal eigentlich schon ganz gut. Da kann man auch Wörter nicht ganz richtig schreiben, Buchstaben verdrehen usw. Aber Eins nach dem Anderen. Im aller Schlimmsten Fall (wenn der Raspi das von der Leistung her nicht schaffen sollte) dann wird da eben doch mit Google gearbeitet werden müssen. Fände ich schade, aber man ist ja flexibel.

HaWe
24.06.2016, 09:28
hallo,
von Anfang an doppelten Code halte ich für übertrieben - aber: OK, machbar.

Wesentlich sind für mich (auf die Gefahr, dass ich mich zum zig-sten Male wiederhole):

keine C++ Objekte, streams, Vectoren, strings
keine Installation von ncurses
reiner ANSI C Code

API Funktionen (ziemlich genau so, ggf. die Parameter ein wenig anders gesetzt):
a) record_sound(int32_t * array, int32_t length);
b) save_sound2wavFile(int32_t * array, FILE * fp, char * filename); // über Zenity popen() PopUp Window
c) FILE * fp = open_wavFile(int32_t * array, char * filename); // über Zenity popen() PopUp Window
d) play_soundArray(int32_t * array, int32_t length);
e) play_wavFile(char * filename); // über Zenity popen() PopUp Window
sowie
f) plotArray(int32_t * array, int32_t length, int y0);
g) optimizeArray(int32_t * array, int32_t length);

Menü im normalen LXTerminal, das genau diese (!) API Funktionen aufruft,
unter Zuhilfenahme von PopUpWindows für OpenFileDialog/SaveFileDialog/popen()+Zenity etc.

Dateiformat für sound: *.wav zum Speichern und Laden

Deine Erwartungen bezüglich der Leistungsfähigkeit muss ich allerdings etwas dämpfen:
Spracherkennung von Sätzen wird niemals funktionieren,
von gesprochenen Wörtern wird grundsätzlich immer nur das nach FFT-CC am besten passendste Muster ausgewählt (egal wie weit man daneben liegt),
keine Künstliche Intelligenz,
keine Lernfähigkeit wie beim Neuronalen Netz,
ausschließlich Trainierbarkeit der Erkennungsfähigkeit / Selektivität / Spezifität durch möglichst gute Frequenzfilter.

Es wird so ähnlich sein wie in dem bereits von mir verlinkten Video auf der Basis von Lego Mindstorms NXT, NXC Code, nur dass man wschl weniger schreien muss und es etwas schneller geht.

Dafür würde ich jetzt nicht zuviel bereits im Vorfeld in verschiedene Menüs investieren.

hirnfrei
24.06.2016, 10:25
Das Menü kannst du selber bauen oder?

Denkst du mit der Methode wäre es Möglich unterschiedliche Stimmen identifizieren zu können?

HaWe
24.06.2016, 11:04
wenn alle API Funktionen genau so existieren, wie oben beschrieben (inkl. popen()/Zenity), dann ist das Menü kein Problem



was die Worterkennung leisten wird, weiß ich nicht, FFT-CC ist Neuland für mich.

hirnfrei
24.06.2016, 11:11
Warum willst du Zenity da unbedingt rein stricken?

HaWe
24.06.2016, 11:16
weil OpenFileDialog und SaveFileDialog-Funktionen nunmal PopUpWindows mit File Browsern zur Auswahl benötigen, ist doch klar, das war doch der Sinn der Sache!

die Funktion kann ja durchaus doppelt gewrapt sein:
innen fopen()
und außen rum popen()

wie genau, ist egal, Hauptsache, am Ende steht ein FILE * fp, dessen Name man per Menü ausgewählt hat, entweder zum Öffnen oder zum Speichern.

hirnfrei
24.06.2016, 11:18
Dann wäre aber eine extra Funktion für Zenity logischer die dann einen Dateinamen zurück gibt die man dann an die entsprechende Funktion weiter gibt. So wie mein Menü das auch macht. Da liesse ich mit mir reden.

HaWe
24.06.2016, 11:22
Ich lass mich überraschen,
aber ein FILE * fp wird in jedem Fall gebraucht, da ja stdio.h Funktionen über den File Pointer (handle) funktonieren (u.a. close(FILE * fp) )

popen() hat ja praktischerweise fast dieselbe Syntax wie fopen(), wie schon woanders angemerkt.

hirnfrei
24.06.2016, 11:30
Über das Öffnen und Schliessen brauchst du dir keine Gedanken zu machen. Das läuft dann in der Funktion selbst.

Ceos
24.06.2016, 11:43
@hirnfrei es gibt Libs die tauschen FPs aus und Libs die tauschen Pfadnamen aus, bau einfach eine Variante mit ein, die es erlaubt FPs zu übergeben :D das macht das Leben einfacher, auch wenn man dafür einen klacks mehr code schreiben muss

mach einfach eine methode saveToFile(*FP), eine methode saveToFile(*Str) und eine methode *FP getFPfromStr(*Str)

in der saveToFile(*Str) dann einfach saveToFile(getFPfromStr(*Str));

sorry für die Pseudo Code verwendung, aber im Moment knabber ich heir an einem völlig unlogisch erscheinenden Ablaufproblem und hab keinen Cache für C-Code mehr im Hirn Frei

hirnfrei
24.06.2016, 11:53
Wie rufst du zenity eigentlich von c aus auf?

HaWe
24.06.2016, 12:25
auch im Original-Code dürfen ja nur FILE * fp vorhanden sein und keine streams oder Objekte oder was auch immer als files, von daher werden Dateinamen ja eh nur zur Auswahl benötigt
fp = fopen (char * filename, "r")
fp = fopen (char * filename, "w")

- und die Namen werden ja nun von Zenity gepiped.

wie Zenity funktioniert, steht schon weiter oben!

...gehe zurück auf Seite 19!
https://www.roboternetz.de/community/threads/69369-Raspbian-C-C-fertige-OpenFileDialog-oder-SaveFileDialog-Komponenten?p=628328&viewfull=1#post628328

https://www.roboternetz.de/community/threads/69369-Raspbian-C-C-fertige-OpenFileDialog-oder-SaveFileDialog-Komponenten?p=628316&viewfull=1#post628316


Anm.:
sowas wäre also NICHT erlaubt:
datei.open(_datei, ios::in)

der Code muss ja mit gcc compilierbar sein, daher nur ANSI C, kein C++!

hirnfrei
25.06.2016, 00:20
Nicht das du denkst ich hätte dich verlassen ;).

Ich kämpfe mit Wave :(. Laden und direkt abspielen ist einfach. Das aber in einem Array zu haben mit dem man dann auch noch arbeiten kann ist schwer :(.

Ich werfe glaube ich auch gleich was an die Wand oder so. Laden ist kein Problem aber dann ist nur in einem Viertel des Arrays überhaupt etwas Anderes wie 0 und man kann sich das ganze Array auch nicht ansehen denn dann gibt es einen Seg-Fault. Wie mich das nervt!

- - - Aktualisiert - - -


sowas wäre also NICHT erlaubt:
datei.open(_datei, ios::in)

Das ist das was ich in einer vorherigen Antwort meinte. Damit es für dich passt muss ich meine eigene Art zu Programmieren, so wie ich es gewohnt bin und wie es mit schnell von der Hand geht in grossen Teilen aufgeben.

hirnfrei
25.06.2016, 18:58
Was ein Scheiss! Ich bin zu doof die Daten aus dem Wave richtig in ein Array zu bekommen das ich später verarbeiten und anschliessend ausgeben kann! Hat wer rat?

@HaWe, reicht es wenn das Programm mit den selbst erstellten Wav's arbeiten kann?

LeoTheLoewe
16.10.2024, 01:50
Der thread ist zwar schon ein bischen älter, aber ich habe den WAV Player mal ein bischen in Ordnung gebracht:




#include <alsa/asoundlib.h>

#include <bitset>
#include <iostream>
#include <sstream>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

using namespace std;

/**
* The canonical WAVE format starts with the RIFF header
*/
typedef struct
{
// Contains the letters "RIFF" in ASCII form (0x52494646 big-endian form).
// The default byte ordering assumed for WAVE data files is little-endian.
// Files written using the big-endian byte ordering scheme have the identifier RIFX instead of RIFF.
unsigned char id[4];
/*
36 + SubChunk2Size, or more precisely:
4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
This is the size of the rest of the chunk
following this number. This is the size of the
entire file in bytes minus 8 bytes for the
two fields not included in this count:
ChunkID and ChunkSize.
*/
uint32_t length;
// Contains the letters "WAVE" (0x57415645 big-endian form).
unsigned char type[4];
} Header;

/**
* The "WAVE" format consists of two subchunks: "fmt " and "data":
* The "fmt " subchunk describes the sound data's format
*/
typedef struct
{
// Contains the letters "fmt " (0x666d7420 big-endian form).
unsigned char id[4];
// 16 for PCM
uint32_t length;
// PCM = 1 (i.e. Linear quantization)
// Values other than 1 indicate some
// form of compression.
uint16_t fmt;
// Mono = 1, Stereo = 2, etc.
uint16_t channels;
// 8000, 44100, etc.
uint32_t samplesPerSec;
// ByteRate == SampleRate * NumChannels * BitsPerSample/8
uint32_t avgBytesPerSec;
// BlockAlign == NumChannels * BitsPerSample/8
// The number of bytes for one sample including all channels.
// I wonder what happens when this number isn't an integer?
uint16_t blockAlign;
// 8 bits = 8, 16 bits = 16, etc.
uint16_t bitsPerSample;
} Format;
#define WAVE_FORMAT_IEEE_FLOAT 3

/**
* The "data" subchunk contains the size of the data and the actual sound
*/
typedef struct
{
// Contains the letters "data" (0x64617461 big-endian form).
unsigned char id[4];
// Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8
// This is the number of bytes in the data.
// You can also think of this as the size
// of the read of the subchunk following this number.
uint32_t length;
} ChunkHead;



class WavePlayer
{
private:
snd_pcm_t* mAudioDevice;
uint32_t mDataSize;
char* mData;
uint16_t mChannels;
uint32_t mSampleRate;
uint16_t mBitsPerSample;
uint32_t mFrameCount;
string mAudioDeviceName;
string mWaveFileName;

bool openDevice ();
bool setHwParams (uint32_t channels, uint32_t sampleRate, uint16_t bitsPerSample);

public:
WavePlayer (string audioDeviceName);
~WavePlayer();

bool isDeviceOpen ();
bool loadFile (string waveFileName);
bool play ();
};

WavePlayer::WavePlayer (string audioDeviceName)
: mAudioDevice (nullptr)
, mData (nullptr)
, mChannels (0)
, mSampleRate (0)
, mBitsPerSample (0)
, mFrameCount (0)
, mAudioDeviceName ()
, mWaveFileName ()
{
if (audioDeviceName.length() == 0)
mAudioDeviceName = "plughw:1,0";
else
mAudioDeviceName = audioDeviceName;

openDevice ();
}

WavePlayer::~WavePlayer()
{
snd_pcm_drain (mAudioDevice);

if (mAudioDevice != nullptr)
snd_pcm_close (mAudioDevice);

if (mData != nullptr)
free (mData);
}

bool WavePlayer::openDevice ()
{
int err;

err = snd_pcm_open(&mAudioDevice, mAudioDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0)
{
cout << "Cannot open audio device " << mAudioDeviceName << " (" << snd_strerror(err) << ")" << endl;
mAudioDevice = nullptr;
return false;
}
return true;
}

bool WavePlayer::isDeviceOpen ()
{
return mAudioDevice != nullptr;
}

bool WavePlayer::setHwParams (uint32_t channels, uint32_t sampleRate, uint16_t bitsPerSample)
{
snd_pcm_format_t bits;
uint32_t resample = 1;
int err;

snd_pcm_state_t state = snd_pcm_state (mAudioDevice);
//cout << "setHwParams: PCM state " << to_string(state) << endl;

cout << "setHwParams: channels: " << to_string(channels) << " sample rate: " << to_string(sampleRate) << " bits: " << to_string(bitsPerSample) << endl;

if (channels == mChannels && sampleRate == mSampleRate && bitsPerSample == mBitsPerSample)
{
if ((err = snd_pcm_prepare (mAudioDevice)) < 0)
{
cout << "setHwParams: Error preparing audio device (" << snd_strerror(err) << ")" << endl;
return false;
}

return true;
}

mChannels = channels;
mSampleRate = sampleRate;
mBitsPerSample = bitsPerSample;

switch (mBitsPerSample)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;

case 16:
bits = SND_PCM_FORMAT_S16;
break;

case 24:
bits = SND_PCM_FORMAT_S24;
break;

case 32:
bits = SND_PCM_FORMAT_S32;
break;
}

if (snd_pcm_hw_free (mAudioDevice) < 0)
{
/*
snd_pcm_drop (mAudioDevice);
snd_pcm_wait (mAudioDevice, 3000);
state = snd_pcm_state (mAudioDevice);
cout << "setHwParams: PCM state is " << to_string(state) << " after calling snd_pcm_drop" << endl;
*/

snd_pcm_drain (mAudioDevice);
snd_pcm_close (mAudioDevice);
openDevice ();
}

snd_pcm_hw_params_t *hwParams;

if ((err = snd_pcm_hw_params_malloc(&hwParams)) < 0)
{
cout << "setHwParams: Error initilizing parameter (" << snd_strerror(err) << ")" << endl;
return false;
}

if ((err = snd_pcm_hw_params_any(mAudioDevice, hwParams)) < 0)
{
cout << "setHwParams: Error getting parameter (" << snd_strerror(err) << ")" << endl;
goto error;
}

if ((err = snd_pcm_hw_params_set_rate_resample(mAudioDevice, hwParams, resample)) < 0)
{
cout << "setHwParams: Error setting re-sampling " << snd_strerror(err) << endl;
goto error;
}

if ((err = snd_pcm_hw_params_set_access(mAudioDevice, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "setHwParams: Error setting access type (" << snd_strerror(err) << ")" << endl;
goto error;
}

if ((err = snd_pcm_hw_params_set_format(mAudioDevice, hwParams, bits)) < 0)
{
cout << "setHwParams: Error setting sample format (" << snd_strerror(err) << ")" << endl;
goto error;
}

if ((err = snd_pcm_hw_params_set_channels(mAudioDevice, hwParams, channels)) < 0)
{
cout << "setHwParams: Error setting channel count (" << snd_strerror(err) << ")" << endl;
goto error;
}

if ((err = snd_pcm_hw_params_set_rate_near(mAudioDevice, hwParams, &sampleRate, 0)) < 0)
{
cout << "setHwParams: Error setting sample rate " << to_string (sampleRate) << " (" << snd_strerror(err) << ")" << endl;
goto error;
}

if ((err = snd_pcm_hw_params(mAudioDevice, hwParams)) < 0)
{
state = snd_pcm_state (mAudioDevice);
cout << "setHwParams: Error setting params (" << snd_strerror(err) << ") PCM state " << to_string(state) << endl;
goto error;
}
// snd_pcm_hw_params_get_buffer_size(hwParams, &bufferSize);

snd_pcm_hw_params_free (hwParams);
return true;

error:
snd_pcm_hw_params_free (hwParams);
return false;
}

void dumpDataHex (char* p, size_t size)
{
printf ("\n");
for (int i = 0; i < size; i++)
{
printf ("%02X ", *(p+i));
if ((i+1) % 16 == 0)
printf ("\n");
}
printf ("\n");
}

bool WavePlayer::loadFile (string waveFileName)
{
Header head;
Format format;
ChunkHead dataHead;

cout << endl;
cout << waveFileName << endl;
int file;
file = open (waveFileName.c_str(), O_RDONLY);
if (file < 0)
return false;

char text[5];
text [4] = 0;
string fmt;

read (file, &head, sizeof(Header));
read (file, &format, sizeof(Format));
if (format.length > sizeof (format)-8)
{
char b;
for (int i = 0; i < format.length - (sizeof (format)-8); i++)
{
read (file, &b, 1);
}
}
int i = 0;
do
{
read (file, &dataHead, sizeof(ChunkHead));
//dumpDataHex ((char*)&dataHead, sizeof(ChunkHead));
memcpy (text, dataHead.id, 4);
fmt = text;
if (fmt == "data")
break;
i++;
lseek (file, -sizeof(ChunkHead)+1, SEEK_CUR);
} while (i < 100);
if (fmt != "data")
{
cout << "loadFile: Do not find start of data chunk" << endl;
return false;
}

#if 0
memcpy (text, head.id, 4);
fmt = text;
cout << fmt;
cout << " length: " << to_string(head.length);

memcpy (text, head.type, 4);
fmt = text;
cout << " " << fmt;

memcpy (text, format.id, 4);
fmt = text;
cout << " " << fmt;
cout << " length: " << to_string(format.length);

memcpy (text, dataHead.id, 4);
fmt = text;
cout << " " << fmt;
cout << " length: " << to_string(dataHead.length) << endl;

cout << " channels: " << to_string(format.channels) << endl;
cout << " samplesPerSec: " << to_string(format.samplesPerSec) << endl;
cout << " avgBytesPerSec: " << to_string(format.avgBytesPerSec) << endl;
cout << " blockAlign: " << to_string(format.blockAlign) << endl;
cout << " bitsPerSample: " << to_string(format.bitsPerSample) << endl;
#endif

if (mData != nullptr)
{
cout << "Wait for " << mWaveFileName << "..." << endl;
snd_pcm_drain (mAudioDevice);
cout << "Go for " << waveFileName << endl;
free (mData);
}
mDataSize = dataHead.length;
mData = (char *)malloc(mDataSize);
read (file, mData, mDataSize);
mFrameCount = head.length * 8 / ((uint32_t)format.bitsPerSample * (uint32_t)format.channels);
close(file);
mWaveFileName = waveFileName;

if (format.bitsPerSample == 32)
{
if (format.fmt == WAVE_FORMAT_IEEE_FLOAT)
cout << "Audio format is 32Bit IEEE float" << endl;

if (mDataSize % 4 != 0)
cout << "loadFile: Buffer size is not multiple of 4" << endl;

char* wave16 = (char *)malloc (mDataSize/2);

float min = 0.0f;
float max = 0.0f;
for (int i = 0; i < mDataSize; i += 4)
{
float f = *((float*)(mData+i));

if (f > max)
max = f;

if (f < min)
min = f;
}

float scaling = 1.0f;
if (max > scaling)
scaling = max;
if (min < -scaling)
scaling = -min;
//scaling *= 2.0f;
cout << "loadFile: 32bit float data are in between " << to_string(min) << " and " << to_string(max) << " => Use scaling of " << to_string(scaling) << endl;

//dumpDataHex (mWave, 64);

int16_t sample16;
int16_t* dest = (int16_t*)wave16;
for (int i = 0; i < mDataSize; i += 4)
{
float f = *((float*)(mData+i));
sample16 = (int16_t)((f / scaling) * 32767);
//sample16 = (int16_t)(*p);
*dest = sample16;
dest++;
}

free (mData);
mData = wave16;
mDataSize /= 2;
format.bitsPerSample = 16;
#if 0
{
int16_t sample16;
int16_t min = 0;
int16_t max = 0;

//dumpDataHex (mWave, 64);
for (int i = 0; i < mDataSize; i += 2)
{
sample16 = *((int16_t*)(mData+i));
if (sample16 > max)
max = sample16;

if (sample16 < min)
min = sample16;
}
cout << "loadFile: 16bit signed data are in between " << to_string(min) << " and " << to_string(max) << endl;
}
#endif
}

if (!setHwParams (format.channels, format.samplesPerSec, format.bitsPerSample))
{
free (mData);
mData = nullptr;
mWaveFileName = "";

return false;
}

return true;
}

bool WavePlayer::play ()
{
snd_pcm_uframes_t count, frames;

cout << "Play " << mWaveFileName << " - " << to_string(mFrameCount) << " frames" << endl;
snd_pcm_state_t state = snd_pcm_state (mAudioDevice);
//cout << "PCM state " << to_string(state) << endl;
if (state == SND_PCM_STATE_XRUN)
{
int err;
if ((err = snd_pcm_prepare (mAudioDevice)) < 0)
{
cout << "Play: Error preparing audio device (" << snd_strerror(err) << ")" << endl;
return false;
}
}

count = 0;
do
{
frames = snd_pcm_writei (mAudioDevice, mData + count, mFrameCount - count);

if (frames < 0)
frames = snd_pcm_recover (mAudioDevice, frames, 0);
if (frames < 0)
{
cout << "Error playing " << mWaveFileName << " (" << snd_strerror(frames) << ")" << endl;
return false;
}
count += frames;
} while (count < mFrameCount);

return true;
}

int main(void)
{
{
string fName = "16BitMono.wav";
//fName = "/usr/share/sounds/alsa/Rear_Left.wav";
WavePlayer player ("hw:1,0");
if (!player.isDeviceOpen ())
cout << "Error opening audio device" << endl;
else if (!player.loadFile (fName))
cout << "Error loading file " << fName << endl;
else if (!player.play ())
{
cout << "Error playing sound" << fName << endl;
}
#if 1
fName = "32BitMono.wav";
//fName = "/usr/share/sounds/alsa/Rear_Left.wav";
if (!player.loadFile (fName))
cout << "Error loading file " << fName << endl;
else if (!player.play ())
{
cout << "Error playing sound" << fName << endl;
}
#endif
}

#if 1
{
string fName = "/usr/share/sounds/alsa/Rear_Left.wav";
WavePlayer player ("hw:1,0");
if (!player.isDeviceOpen ())
cout << "Error opening audio device" << endl;
else if (!player.loadFile (fName))
cout << "Error loading file " << fName << endl;
else
{
for (int i = 0; i < 2; i++)
{
if (!player.play ())
{
cout << "Error playing sound" << fName << endl;
break;
}
sleep (3);
}
}
}
#endif
}