open_the_device(); set_the_parameters_of_the_device(); while (!done) { /* 다음 중 하나 또는 두 가지 작업 */ receive_audio_data_from_the_device(); deliver_audio_data_to_the_device(); } close the device사운드 카드를 공부하는데 있어 ALSA 사운드 카드 매트릭스를 훑어보는 것은 좋은 시작점입니다. 그러나 대부분의 경우 여러분은 기계에 대해서 깊이 알 필요 까지는 없습니다. (여러분이 달성하려는 정도에 따라 달라집니다).
#includeALSA#include #include #include #include #include #include int fd_out; int sample_rate = 48000; static void write_sinewave (void) { /* 이 루틴은 합성을 이용해 오디오 시그널을 만드는 어플리케이션 루틴의 전형적인 예제입니다. 덧붙이면 이것은 사실 매우 기초적인 “wave table” 알고리즘입니다. 이것은 사인 함수의 완전한 순환을 위한 이미 계산된 사인 함수 값을 이용합니다. 이것은 각각의 샘플을 위해 sin() 함수를 호출하는 것보다 훨씬 빠릅니다. 다른 어플리케이션에서 이 루틴은 해당 어플리케이션이 필요로 하는 작업으로 간단하게 대체할 수 있습니다. */ static unsigned int phase = 0; /* 사인파의 위상 */ unsigned int p; int i; short buf[1024]; /* 1024 샘플/쓰기 는 안전한 선택입니다. */ int outsz = sizeof (buf) / 2; static int sinebuf[48] = { 0, 4276, 8480, 12539, 16383, 19947, 23169, 25995, 28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272, 28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276, 0, -4276, -8480, -12539, -16383, -19947, -23169, -25995, -28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272, -28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276 }; for (i = 0; i < outsz; i++) { /* sinebuf[] 테이블은 48000 Hz 로 계산되어 있습니다. 우리는 단순한 샘플 레이트 보정을 사용할 것입니다. 너무 크게 증가하는 위상 변수는 일정 시간 이후에 산술 오버플로우를 발생할 수 있기 때문에 너무 크게 증가하는 위상 변수를 막아야합니다. 몇 시간이나 몇 달, 심지어 몇 년 동안 인터럽트 없이 계속 실행하는 오디오 프로그램을 작성할 때는 이런 종류의 에러 가능성을 확인해야 합니다. 매 초 192000 샘플을 32비트 정수형 범위로 연산을 하면 오버플로우가 매우 빨리 발생합니다. 192 kHZ 로 샘플을 재생하면 6시간 후에는 오버 플로우가 발생할 것입니다. */ p = (phase * sample_rate) / 48000; phase = (phase + 1) % 4800; buf[i] = sinebuf[p % 48]; } /* 기록시에는 적당한 에러 체크를 반드시 해야 합니다. 시스템이 반환하는 에러 코드를 확인하는 것 역시 중요합니다. */ if (write (fd_out, buf, sizeof (buf)) != sizeof (buf)) { perror ("Audio write"); exit (-1); } } /* open_audio_device 는 오디오 장치를 열고 필요한 모드로 초기화 합니다. */ static int open_audio_device (char *name, int mode) { int tmp, fd; if ((fd = open (name, mode, 0)) == -1) { perror (name); exit (-1); } /* 장치를 설정합니다. 샘플 포맷과 채널의 개수, 샘플 레이트를 정확하게 이 순서로 설정해야 함을 주의하세요. 어떤 장치는 순서의 영향을 받습니다. */ /* 샘플 포맷을 설정합니다. */ tmp = AFMT_S16_NE; /* Native 16 bits */ if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1) { perror ("SNDCTL_DSP_SETFMT"); exit (-1); } if (tmp != AFMT_S16_NE) { fprintf (stderr, "The device doesn"t support the 16 bit sample format.n"); exit (-1); } /* 채널 개수를 설정합니다. */ tmp = 1; if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) { perror ("SNDCTL_DSP_CHANNELS"); exit (-1); } if (tmp != 1) { fprintf (stderr, "The device doesn"t support mono mode.n"); exit (-1); } /* 샘플 레이트를 설정합니다. */ sample_rate = 48000; if (ioctl (fd, SNDCTL_DSP_SPEED, &sample_rate) == -1) { perror ("SNDCTL_DSP_SPEED"); exit (-1); } /* 우리는 실제 샘플 레이트를 기준으로 자동으로 시그널을 조정하기 때문에 에러 체크가 필요 없습니다. 그러나 대부분의 어플리케이션은 샘플 레이트의 값을 점검하거나 요청한 레이트와 샘플 레이트를 비교해야 합니다. 레이트 사이의 약간의 차이(10% 또는 그 이하)는 일반적이고, 어플리케이션이 견뎌낼 수 있지만 큰 차이가 있으면 미키마우스 같은 골치아픈 음감(pitch) 문제가 발생합니다. */ return fd; } int main (int argc, char *argv[]) { /* 보통 시스템 관리자들은 ossctl이나 기타 방법을 이용해서 /dev/dsp 장치를 선택하기 때문에 /dev/dsp 를 기본 장치로 이용합니다. */ char *name_out = "/dev/dsp"; /* 기본 값 대신 다른 장치를 선택할 수 있는 방법을 제공하는 것을 추천합니다. 우리는 커맨드 라인 인자를 사용하지만 어떤 경우는 환경 변수를 이용하거나 설정 파일을 이용하는 것이 더 나을 수도 있습니다. */ if (argc > 1) name_out = argv[1]; /* 재생만 하는 프로그램에서 O_WRONLY 모드를 사용하는 것은 필수입니다. 다른 모드는 드라이버의 자원 (메모리) 사용을 증가시킵니다. 또한 다른 어플리케이션에서 동시에 같은 장치를 사용하여 녹음하는 것을 방지할 수 있습니다. */ fd_out = open_audio_device (name_out, O_WRONLY); while (1) write_sinewave (); exit (0); } Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
#include이 예제 다음으로 Paul은 앞서 이야기했던 양방향(full-duplex) 또는, 캡쳐와 재생을 동시에 수행하는 방법을 언급합니다. Paul은 이것을 달성하기 위해 통상적인 캡쳐와 재생을 결합하는 것은 결함이 크며, 인터럽트는 이것을 달성하기 위한 방법이지만 복잡하기 때문에 JACK 이용하는 것을 추천합니다.#include #include #include #include snd_pcm_t *playback_handle; short buf[4096]; int playback_callback (snd_pcm_sframes_t nframes) { int err; printf ("playback callback called with %u framesn", nframes); /* ... buf를 데이터로 채웁니다. ... */ if ((err = snd_pcm_writei (playback_handle, buf, nframes)) < 0) { fprintf (stderr, "write failed (%s)n", snd_strerror (err)); } return err; } main (int argc, char *argv[]) { snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; snd_pcm_sframes_t frames_to_deliver; int nfds; int err; struct pollfd *pfds; if ((err = snd_pcm_open (&playback_handle, argv[1], SND_PCM_STREAM_PLAYBACK, 0)) < 0) { fprintf (stderr, "cannot open audio device %s (%s)n", argv[1], snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf (stderr, "cannot set access type (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { fprintf (stderr, "cannot set sample format (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, 44100, 0)) < 0) { fprintf (stderr, "cannot set sample rate (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 2)) < 0) { fprintf (stderr, "cannot set channel count (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < 0) { fprintf (stderr, "cannot set parameters (%s)n", snd_strerror (err)); exit (1); } snd_pcm_hw_params_free (hw_params); /* 재생을 위한 데이터를 4096 프레임이나 그 이상을 전달 받으면 깨어나도록 ALSA를 설정합니다. 또한 우리들 스스로 장치를 시작할 것이라고 ALSA에게 말해줍니다. */ if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) { fprintf (stderr, "cannot allocate software parameters structure (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_sw_params_current (playback_handle, sw_params)) < 0) { fprintf (stderr, "cannot initialize software parameters structure (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_sw_params_set_avail_min (playback_handle, sw_params, 4096)) < 0) { fprintf (stderr, "cannot set minimum available count (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_sw_params_set_start_threshold (playback_handle, sw_params, 0U)) < 0) { fprintf (stderr, "cannot set start mode (%s)n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_sw_params (playback_handle, sw_params)) < 0) { fprintf (stderr, "cannot set software parameters (%s)n", snd_strerror (err)); exit (1); } /* 인터페이스는 매번 4096 프레임 마다 커널을 인터럽트 할 것입니다. 그리고 ALSA는 그 때마다 곧 이 프로그램을 깨울 것입니다. */ if ((err = snd_pcm_prepare (playback_handle)) < 0) { fprintf (stderr, "cannot prepare audio interface for use (%s)n", snd_strerror (err)); exit (1); } while (1) { /* 데이터를 위한 인터페이스가 준비되거나 또는 1초가 경과할 때까지 기다립니다. */ if ((err = snd_pcm_wait (playback_handle, 1000)) < 0) { fprintf (stderr, "poll failed (%s)n", strerror (errno)); break; } /* 데이터를 재생하기 위해 얼마 만큼의 공간을 사용할 수 있는지 확인합니다. */ if ((frames_to_deliver = snd_pcm_avail_update (playback_handle)) < 0) { if (frames_to_deliver == -EPIPE) { fprintf (stderr, "an xrun occuredn"); break; } else { fprintf (stderr, "unknown ALSA avail update return value (% d)n", frames_to_deliver); break; } } frames_to_deliver = frames_to_deliver > 4096 ? 4096 : frames_to_deliver; /* 데이터를 전송합니다. */ if (playback_callback (frames_to_deliver) != frames_to_deliver) { fprintf (stderr, "playback callback failedn"); break; } } snd_pcm_close (playback_handle); exit (0); }
/** @file simple_client.c * * @brief 이것은 많은 어플리케이션에서 사용하는 JACK의 기본적인 특징을 * 시연하는 매우 간단한 예제입니다. */ #includePython: PyAudio 와 Gstreamer를 이용한 PyGst#include #include #include #include #include jack_port_t *input_port; jack_port_t *output_port; /** * JACK 어플리케이션을 위한 프로세스 콜백 * 적절한 시점에 JACK이 이 콜백 함수를 호출 합니다. */ int process (jack_nframes_t nframes, void *arg) { jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port, nframes); jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer (input_port, nframes); memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes); return 0; } /** * 이것은 JACK 어플리케이션의 shutdown 콜백입니다. * 서버를 종료하거나 클라이언트를 접속 종료하기로 결정했을때 * JACK이 이 콜백 함수를 호출합니다. */ void jack_shutdown (void *arg) { exit (1); } int main (int argc, char *argv[]) { jack_client_t *client; const char **ports; if (argc < 2) { fprintf (stderr, "usage: jack_simple_client n"); return 1; } /* JACK 서버의 클라이언트가 되기 위해 시도합니다. */ if ((client = jack_client_new (argv[1])) == 0) { fprintf (stderr, "jack server not running?n"); return 1; } /* 처리해야할 작업이 있다면 JACK 서버가 `process()" 를 호출하게 합니다. */ jack_set_process_callback (client, process, 0); /* /* 종료하거나 우리를 호출하는 것을 그만두리고 결정했을 때 JACK 서버가 `jack_shutdown()" 를 호출하게 합니다. */ jack_on_shutdown (client, jack_shutdown, 0); /* 현재 샘플 레이트를 보여줍니다. */ printf ("engine sample rate: %" PRIu32 "n", jack_get_sample_rate (client)); /* 두개의 포트를 생성합니다. */ input_port = jack_port_register (client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); output_port = jack_port_register (client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); /* 우리가 준비되었음을 JACK 서버에게 알려줍니다. */ if (jack_activate (client)) { fprintf (stderr, "cannot activate client"); return 1; } /* ports를 연결합니다. 주의: 여러분은 클라이언트가 활성화 되기 전에 이 작업을 할 수 없습니다. 이것은 동작하고 있지 않은 클라이언트를 연결하는 것을 허용할 수 없기 때문입니다. */ if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == NULL) { fprintf(stderr, "Cannot find any physical capture portsn"); exit(1); } if (jack_connect (client, ports[0], jack_port_name (input_port))) { fprintf (stderr, "cannot connect input portsn"); } free (ports); if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) { fprintf(stderr, "Cannot find any physical playback portsn"); exit(1); } if (jack_connect (client, jack_port_name (output_port), ports[0])) { fprintf (stderr, "cannot connect output portsn"); } free (ports); /* 이것은 단지 장난감이므로 몇초간만 실행하고 종료하도록 합니다. */ sleep (10); jack_client_close (client); exit (0); }
""" 몇 초간 오디오를 녹음하고 WAVE 파일로 저장합니다. """ import pyaudio import wave import sys chunk = 1024 FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 44100 RECORD_SECONDS = 5 WAVE_OUTPUT_FILENAME = "output.wav" p = pyaudio.PyAudio() stream = p.open(format = FORMAT, channels = CHANNELS, rate = RATE, input = True, frames_per_buffer = chunk) print "* recording" all = [] for i in range(0, RATE / chunk * RECORD_SECONDS): data = stream.read(chunk) all.append(data) print "* done recording" stream.close() p.terminate() # write data to WAVE file data = "".join(all) wf = wave.open(WAVE_OUTPUT_FILENAME, "wb") wf.setnchannels(CHANNELS) wf.setsampwidth(p.get_sample_size(FORMAT)) wf.setframerate(RATE) wf.writeframes(data) wf.close()얼마나 코드가 간단한지 유념하길 바랍니다. There is no GUI, of course, so that helps as well. 물론, 이해를 돕기 위해 GUI는 없습니다.
#!/usr/bin/env python import sys, os, os.path import pygtk, gtk, gobject import pygst pygst.require("0.10") import gst class GTK_Main: def __init__(self): window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_title("Audio-Player") window.set_default_size(400, 300) window.connect("destroy", gtk.main_quit, "WM destroy") vbox = gtk.VBox() window.add(vbox) self.entry = gtk.Entry() vbox.pack_start(self.entry, False, True) self.button = gtk.Button("Start") self.button.connect("clicked", self.start_stop) vbox.add(self.button) window.show_all() self.player = gst.element_factory_make("playbin", "player") fakesink = gst.element_factory_make("fakesink", "my-fakesink") self.player.set_property("video-sink", fakesink) bus = self.player.get_bus() bus.add_signal_watch() bus.connect("message", self.on_message) def start_stop(self, w): if self.button.get_label() == "Start": filepath = self.entry.get_text() if os.path.exists(filepath): self.button.set_label("Stop") self.player.set_property("uri", "file://" + filepath) self.player.set_state(gst.STATE_PLAYING) else: self.player.set_state(gst.STATE_NULL) self.button.set_label("Start") def on_message(self, bus, message): t = message.type if t == gst.MESSAGE_EOS: self.player.set_state(gst.STATE_NULL) self.button.set_label("Start") elif t == gst.MESSAGE_ERROR: self.player.set_state(gst.STATE_NULL) self.button.set_label("Start") gtk.gdk.threads_init() GTK_Main() gtk.main()이 예제는 실제로 GUI를 포함하지만 저수준의 언어와 비교했을 때 코드양은 여전히 작습니다.
최신 콘텐츠