Kirjoittaja Aihe: [ ratkaistu ] Mikrofonin syöte c++ -taulukkoon ja kaiuttimelle  (Luettu 3928 kertaa)

teele

  • Käyttäjä
  • Viestejä: 910
    • Profiili
Kun koneen mikrofoni kuulee ääntä, tämä muutettaneen jossain vaiheessa digitaaliseksi eli numeroiksi.

Miten saisi tällaisia mikrofonilta peräisin olevia numeroita esimerkiksi 100-paikkaiseen taulukkoon aina tietyin aikavälein, kun koneessa on 24.04 Ubuntu.

Millaisella nopeudella tai taajuudella näitä digitalisoituja numeroita tulee 24.04 Ubuntussa ja saisiko vaikka kaikki 100 peräkkäistä arvoa taulukkoon niin, että se olisi kohtuullisen kevyt toiminto suorittimelle tai väylälle tai muille asiaan liittyville laitteille.

En tunne digitaalista äänitekniikkaa Ubuntussa yhtään, joten ihan käytännönselitykset olisivat  tervetulleita.

« Viimeksi muokattu: 18.12.25 - klo:22.38 kirjoittanut teele »

nm

  • Käyttäjä
  • Viestejä: 16922
    • Profiili
Vs: Mikrofonin syöte c++ -taulukkoon
« Vastaus #1 : 21.11.25 - klo:20.26 »
Äänipiirin AD-muunnin muuntaa mikrofonilta tulevan analogisen signaalin digitaaliseksi PCM-signaaliksi. Esimerkiksi tyypillisessä CD-laatuisessa äänessä näytteenottotaajuus on 44 100 Hz ja käytössä on 16-bittiset näytteet, eli yhden sekunnin mittaisessa digitaalisessa tallenteessa on 44 100 kpl 16-bittisiä numeroita (0-65535). CD-äänessä on lisäksi kaksi erillistä kanavaa (stereo). Mikrofonilta tulevassa äänessä on tyypillisesti yksi kanava tai monoääni on kahdennettu kahdelle kanavalle.

Mainitsemaasi 100 paikan taulukkoon voi siis tallentaa 2,3 millisekuntia CD-laatuista monoääntä. 44 100 paikan taulukkoon voit tallentaa 1 sekunnin verran ääntä ja 2 646 000 paikan taulukkoon 1 minuutin pätkän. 16-bittisenä sekunnin pätkä tarvitsee muistia tai tallennustilaa 88 200 tavua eli 86 kt ja minuutin pätkään tarvitaan 5 292 000 tavua eli reilut 5 Mt. Tällaisten datamäärien käsittely on nykyisille tietokoneille täysin triviaalia. Esimerkiksi Full HD -näytölle piirrettävän 60 Hz RGB-signaalin datamäärä on yli 4000 kertaa isompi (356 Mt/s), ja sekin on tänä päivänä pientä lämmittelyä, kun erillisten näytönohjainten ja tehokkaimpien prosessorien muistiväylät ovat parhaimmillaan 1000 kertaa nopeampia (työpöytäprosessoreissa 50 Gt/s, uusimmissa näytönohjaimissa 700 Gt/s). Mikrofonin äänisignaalin siirto ja kevyt prosessointi vaatii siis noin miljoonasosan nykyaikaisen tietokoneen laskentakapasiteetista.

Jos haluat esimerkkikoodia äänen kaappaamista varten, ChatGPT, Claude, Gemini ja vastaavat tekoälymallit osaavat opastaa alusta alkaen. Esimerkiksi simppeli C-ohjelma äänen tallentamiseen ALSA-rajapinnan kautta voisi näyttää seuraavalta:

Prompti:
Lainaus
Kirjoita C-kielinen ohjelma äänen kaappaamiseen muistipuskuriin Linuxissa. Toteuta komentorivivalitsin, jolla asetetaan kaapattavan äänen pituus sekunteina. Käytä lähdekoodissa englanninkielisiä tekstejä ja kommentteja.

Tekoälyn kirjoittama koodi capture.c:
Koodia: [Valitse]
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>

#define SAMPLE_RATE 44100
#define CHANNELS 1
#define FORMAT SND_PCM_FORMAT_S16_LE

int main(int argc, char *argv[]) {
    snd_pcm_t *handle;
    snd_pcm_hw_params_t *params;
    int err;

    // Default capture time = 1 second
    int seconds = 1;

    // Parse command-line argument
    if (argc > 1) {
        seconds = atoi(argv[1]);
        if (seconds <= 0) {
            fprintf(stderr, "Invalid duration: %s\n", argv[1]);
            return 1;
        }
    }

    printf("Capturing %d second(s) of audio...\n", seconds);

    // Open default capture device
    if ((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0) {
        fprintf(stderr, "Unable to open PCM device: %s\n", snd_strerror(err));
        return 1;
    }

    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(handle, params);

    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(handle, params, FORMAT);
    snd_pcm_hw_params_set_channels(handle, params, CHANNELS);
    snd_pcm_hw_params_set_rate(handle, params, SAMPLE_RATE, 0);

    if ((err = snd_pcm_hw_params(handle, params)) < 0) {
        fprintf(stderr, "Unable to set HW params: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        return 1;
    }

    // Calculate total samples & buffer size
    size_t bytes_per_sample = 2; // S16_LE = 2 bytes
    size_t total_samples = (size_t)SAMPLE_RATE * CHANNELS * seconds;
    size_t buffer_size = total_samples * bytes_per_sample;

    unsigned char *buffer = malloc(buffer_size);
    if (!buffer) {
        fprintf(stderr, "Memory allocation failed\n");
        snd_pcm_close(handle);
        return 1;
    }

    size_t frames_to_read = SAMPLE_RATE * seconds;

    // Capture audio
    ssize_t frames_read = snd_pcm_readi(handle, buffer, frames_to_read);
    if (frames_read < 0) {
        fprintf(stderr, "Read error: %s\n", snd_strerror(frames_read));
        snd_pcm_close(handle);
        free(buffer);
        return 1;
    }

    printf("Captured %zd frames (%zu bytes) into memory.\n",
           frames_read, buffer_size);

    snd_pcm_close(handle);
    free(buffer);

    return 0;
}

Asenna ALSA:n headerit eli libasound2-dev:
Koodia: [Valitse]
sudo apt install libasound2-dev
Käännä:
Koodia: [Valitse]
gcc capture.c -lasound -o capture
Suorita:
Koodia: [Valitse]
./capture 5

Snufkin

  • Käyttäjä
  • Viestejä: 803
    • Profiili
Vs: Mikrofonin syöte c++ -taulukkoon
« Vastaus #2 : 21.11.25 - klo:21.10 »
En osaa sano asian digitaalisesta puolesta mitään, mutta jos tarve tallentaa vain puhetta, niin siihen riittää varmaan alle 10% tuosta CD-äänen tallennuksesta. Analogisissa puhelimissa taisi olla noin 3kHz taajuusalue käytössä. Jos ääni vielä melko tasaista voimakkuudeltaan, voisi riittää pienempi bittisyvyyskin.



Xubuntu 22.04 LTS, Fujitsu Lifebook E754

teele

  • Käyttäjä
  • Viestejä: 910
    • Profiili
Vs: Mikrofonin syöte c++ -taulukkoon
« Vastaus #3 : 15.12.25 - klo:13.38 »

Tekoälyn käyttö oli hyvä vihje. Se kirjoittaakin nopeasti ohjelmia pyydettäessä

Koodia: [Valitse]
On Ubuntu 24.04 with the libasound2-dev package, write a c++ program that reads a sound sample of 5 seconds from the laptop microphone and stores in a buffer as 16 bit values from a single channel and then outputs the sample from the buffer via the laptop speakers.

Ylllä olevan pyynnön tuloksena saatu ohjelma kääntyy ilman varoituksia, mutta ei näytä tekevän yhtään mitään. Lisäsin sinne yhden tulostusrivinkin tarkistusta varten, mutta sekään ei tulostu.

En valitettavasti vielä ymmärrä äänenkäsittelystä niin paljon, että voisin arvioida, voisiko ohjelman kohtuutyöllä saada toimimaan.

Koodia: [Valitse]

#include <iostream>
#include <vector>
#include <alsa/asoundlib.h>

int main() {
    const unsigned int sample_rate = 44100;
    const unsigned int seconds     = 5;
    const unsigned int channels    = 1;
    const snd_pcm_format_t format  = SND_PCM_FORMAT_S16_LE;

    const snd_pcm_uframes_t total_frames = sample_rate * seconds;
    const snd_pcm_uframes_t period_size  = 1024; // frames per read/write
    std::vector<int16_t> buffer(total_frames * channels);

    int rc;
    snd_pcm_t* capture_handle  = nullptr;
    snd_pcm_t* playback_handle = nullptr;
    snd_pcm_hw_params_t* params = nullptr;

    // --------- Open and configure capture (microphone) ----------
    rc = snd_pcm_open(&capture_handle, "default",
                      SND_PCM_STREAM_CAPTURE, 0);
    if (rc < 0) {
        std::cerr << "Unable to open capture device: "
                  << snd_strerror(rc) << std::endl;
        return 1;
    }

    snd_pcm_hw_params_malloc(&params);
    snd_pcm_hw_params_any(capture_handle, params);
    snd_pcm_hw_params_set_access(capture_handle, params,
                                 SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(capture_handle, params, format);
    snd_pcm_hw_params_set_channels(capture_handle, params, channels);
    unsigned int rate = sample_rate;
    snd_pcm_hw_params_set_rate_near(capture_handle, params, &rate, nullptr);

    snd_pcm_uframes_t psize = period_size;
    snd_pcm_hw_params_set_period_size_near(capture_handle, params, &psize, nullptr);

    rc = snd_pcm_hw_params(capture_handle, params);
    if (rc < 0) {
        std::cerr << "Unable to set capture HW params: "
                  << snd_strerror(rc) << std::endl;
        snd_pcm_hw_params_free(params);
        snd_pcm_close(capture_handle);
        return 1;
    }
    snd_pcm_hw_params_free(params);

    // --------- Capture into buffer ----------
    {
        snd_pcm_uframes_t frames_captured = 0;
        while (frames_captured < total_frames) {
            snd_pcm_uframes_t frames_to_read =
                std::min(psize, total_frames - frames_captured);

            rc = snd_pcm_readi(capture_handle,
                               buffer.data() + frames_captured * channels,
                               frames_to_read);
            if (rc == -EPIPE) {
                // Overrun
                snd_pcm_prepare(capture_handle);
                continue;
            } else if (rc < 0) {
                std::cerr << "Error reading from capture device: "
                          << snd_strerror(rc) << std::endl;
                break;
            } else {
                frames_captured += rc;
            }
        }
    }

    snd_pcm_drain(capture_handle);
    snd_pcm_close(capture_handle);


    std::cout << "näyte luettu:" << rc << std::endl;

    // --------- Open and configure playback (speakers) ----------
    rc = snd_pcm_open(&playback_handle, "default",
                      SND_PCM_STREAM_PLAYBACK, 0);
    if (rc < 0) {
        std::cerr << "Unable to open playback device: "
                  << snd_strerror(rc) << std::endl;
        return 1;
    }

    snd_pcm_hw_params_malloc(&params);
    snd_pcm_hw_params_any(playback_handle, params);
    snd_pcm_hw_params_set_access(playback_handle, params,
                                 SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(playback_handle, params, format);
    snd_pcm_hw_params_set_channels(playback_handle, params, channels);
    rate = sample_rate;
    snd_pcm_hw_params_set_rate_near(playback_handle, params, &rate, nullptr);

    psize = period_size;
    snd_pcm_hw_params_set_period_size_near(playback_handle, params, &psize, nullptr);

    rc = snd_pcm_hw_params(playback_handle, params);
    if (rc < 0) {
        std::cerr << "Unable to set playback HW params: "
                  << snd_strerror(rc) << std::endl;
        snd_pcm_hw_params_free(params);
        snd_pcm_close(playback_handle);
        return 1;
    }
    snd_pcm_hw_params_free(params);

    // --------- Play back from buffer ----------
    {
        snd_pcm_uframes_t frames_written = 0;
        while (frames_written < total_frames) {
            snd_pcm_uframes_t frames_to_write =
                std::min(psize, total_frames - frames_written);

            rc = snd_pcm_writei(playback_handle,
                                buffer.data() + frames_written * channels,
                                frames_to_write);
            if (rc == -EPIPE) {
                // Underrun
                snd_pcm_prepare(playback_handle);
                continue;
            } else if (rc < 0) {
                std::cerr << "Error writing to playback device: "
                          << snd_strerror(rc) << std::endl;
                break;
            } else {
                frames_written += rc;
            }
        }
    }

    snd_pcm_drain(playback_handle);
    snd_pcm_close(playback_handle);

    return 0;
}

/*
g++ play_after_01.cpp -o play_after_01 -lasound
./play_after_01
*/


Muokk.
Muokkasin vähän vanhaa otsikkoa, ehkä olisi pitänyt laittaa uusi aihe, mutta toiminnan tarkistamiseksi se ääni olisi kätevää saada myös ulos koneesta. Tavote olisi siis saada ääni taullukkoon ja sieltä ulos kaiuttimelle tässä vaiheessa.

« Viimeksi muokattu: 15.12.25 - klo:13.44 kirjoittanut teele »

teele

  • Käyttäjä
  • Viestejä: 910
    • Profiili
Vs: Mikrofonin syöte c++ -taulukkoon ja kaiuttimelle
« Vastaus #4 : 18.12.25 - klo:22.37 »
Toisen tekoälyn kirjoittaman ohjelman kanssa onnistui vähän paremmin. Ohjelma tuntuu toimivan, ja nyt pitäisi sitten selvittää, mitä se oikeastaan tekee. Pitäisi päästä käsiksi niihin tallennettuihin ääniarvoihin lukuina.

Mutta alkuperäinen kysymys taitaa olla ratkennut. Kiitoksia lähestymistapavihjeistä :)

Ohjelma on tässä:

Koodia: [Valitse]

#include <iostream>
#include <vector>
#include <alsa/asoundlib.h>
#define RATE 44100
#define CHANNELS 1
#define SECONDS 5
void configure_pcm(snd_pcm_t* handle) {
 snd_pcm_hw_params_t* params;
 snd_pcm_hw_params_alloca(&params);
 snd_pcm_hw_params_any(handle, params);
 snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
 snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
 snd_pcm_hw_params_set_channels(handle, params, CHANNELS);
 unsigned int rate = RATE;
 snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0);
 snd_pcm_hw_params(handle, params);
}
int main() {
 snd_pcm_t *capture_handle, *playback_handle;
 long total_frames = RATE * SECONDS;
 std::vector<int16_t> buffer(total_frames);
 // Open capture device (Microphone)
 if (snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0) < 0) return 1;
 configure_pcm(capture_handle);
 std::cout << "Recording for 5 seconds..." << std::endl;
 snd_pcm_readi(capture_handle, buffer.data(), total_frames);
 snd_pcm_close(capture_handle);
 // Open playback device (Speakers)
 if (snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) return 1;
 configure_pcm(playback_handle);
 std::cout << "Playing back..." << std::endl;
 snd_pcm_writei(playback_handle, buffer.data(), total_frames);
 
 snd_pcm_drain(playback_handle);
 snd_pcm_close(playback_handle);
 return 0;
}


Käännös näin ja ajo

Koodia: [Valitse]
g++ äänikoe.cpp -o äänikoe -lasound
./äänikoe

« Viimeksi muokattu: 18.12.25 - klo:22.41 kirjoittanut teele »

nm

  • Käyttäjä
  • Viestejä: 16922
    • Profiili
Vs: Mikrofonin syöte c++ -taulukkoon ja kaiuttimelle
« Vastaus #5 : 19.12.25 - klo:00.18 »
Toisen tekoälyn kirjoittaman ohjelman kanssa onnistui vähän paremmin. Ohjelma tuntuu toimivan, ja nyt pitäisi sitten selvittää, mitä se oikeastaan tekee. Pitäisi päästä käsiksi niihin tallennettuihin ääniarvoihin lukuina.

Tallennettu ääni on kaappauksen jälkeen 16-bittisinä kokonaislukuina buffer-vektorissa. Voit vaikka listata numerot stdoutiin, jos haluat nähdä ne. Mielenkiintoisempana harjoituksena voisi esimerkiksi toteuttaa yksinkertaisen kaikuefektin tai häivyttää äänen laskemalla äänenvoimakkuutta (eli pienentämällä näytteiden itseisarvoa) sopivalla funktiolla.