diff options
-rw-r--r-- | src/LICENSE.txt | 342 | ||||
-rw-r--r-- | src/Makefile | 29 | ||||
-rw-r--r-- | src/capture.c | 203 | ||||
-rw-r--r-- | src/capture.h | 9 | ||||
-rw-r--r-- | src/compute.c | 116 | ||||
-rw-r--r-- | src/compute.h | 10 | ||||
-rw-r--r-- | src/fft.c | 241 | ||||
-rw-r--r-- | src/fft.h | 19 | ||||
-rw-r--r-- | src/gtkvumeter.c | 263 | ||||
-rw-r--r-- | src/gtkvumeter.h | 39 | ||||
-rw-r--r-- | src/hsv2rgb.c | 59 | ||||
-rw-r--r-- | src/hsv2rgb.h | 18 | ||||
-rw-r--r-- | src/music2light.c | 80 | ||||
-rw-r--r-- | src/win_main.c | 73 | ||||
-rw-r--r-- | src/win_main.h | 9 |
15 files changed, 1510 insertions, 0 deletions
diff --git a/src/LICENSE.txt b/src/LICENSE.txt new file mode 100644 index 0000000..ec091c1 --- /dev/null +++ b/src/LICENSE.txt @@ -0,0 +1,342 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..67d7035 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,29 @@ +CC=gcc +CFLAGS=-W -Wall -Werror -Wno-error=unused-parameter -g +LDFLAGS=-Werror -g +EXEC=music2light + +CFLAGS+=$(shell pkg-config --cflags gtk+-2.0 gthread-2.0 libpulse) +LDFLAGS+=-lm $(shell pkg-config --libs gtk+-2.0 gthread-2.0 libpulse) + +SRC= $(wildcard *.c) +OBJ= $(SRC:.c=.o) + +all: $(EXEC) + +$(EXEC): $(OBJ) + $(CC) -o $@ $^ $(LDFLAGS) + +#main.o: hello.h + +%.o: %.c + $(CC) -o $@ -c $< $(CFLAGS) + +.PHONY: clean mrproper + +clean: + rm *.o + +mrproper: clean + rm $(EXEC) + diff --git a/src/capture.c b/src/capture.c new file mode 100644 index 0000000..e8ca2fd --- /dev/null +++ b/src/capture.c @@ -0,0 +1,203 @@ +#include "capture.h" +#include <pulse/pulseaudio.h> +#include <gtk/gtk.h> + +#define APP_TITLE "Test 5 lpo" +#define BUFSIZE 1024 + +extern void my_process(float *data, size_t nsamples, size_t nchan); + +capture_sound_level_cb_t *capture_sound_level_cb=NULL; + +int capture_init(pa_mainloop **m, pa_context **c); +void context_state_callback(pa_context *c, void *userdata); +void context_get_server_info_callback(pa_context *c, const pa_server_info*si, void *userdata); +void context_get_source_info_callback(pa_context *c, const pa_source_info *si, int is_last, void *userdata); +pa_stream * create_stream(pa_context *c, const pa_source_info *si); +void stream_state_callback(pa_stream *s, void *userdata); +void stream_read_callback(pa_stream *s, size_t nbytes, void *userdata); + +char *device_name=NULL; + +// Main procedure of this thread +void audio_thread(void *args) { + pa_context *c; + pa_mainloop *m; + int res, retval; + + capture_sound_level_cb=(capture_sound_level_cb_t *)args; + + //printf("debug : args==%p capture_sound_level_cb==%p\n", args, capture_sound_level_cb); + + res=capture_init(&m, &c); + g_assert(res==0); + + res=pa_mainloop_run(m,&retval); + g_assert(res==0); + g_assert(retval==0); + +// pa_context_disconnect(pa_ct); + pa_mainloop_free(m); +} + +int capture_init(pa_mainloop **m, pa_context **c) { + int res=0; + + *m=pa_mainloop_new(); + g_assert(*m); + + *c = pa_context_new(pa_mainloop_get_api(*m), APP_TITLE); + g_assert(*c); + + pa_context_set_state_callback(*c, context_state_callback, NULL); + res=pa_context_connect(*c, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); + + return res; +} + +void context_state_callback(pa_context *c, void *userdata) { + switch (pa_context_get_state(c)) { + case PA_CONTEXT_UNCONNECTED: + printf("PA_CONTEXT_UNCONNECTED\n"); + break; + case PA_CONTEXT_CONNECTING: + printf("PA_CONTEXT_CONNECTING\n"); + break; + case PA_CONTEXT_AUTHORIZING: + printf("PA_CONTEXT_AUTHORIZING\n"); + break; + case PA_CONTEXT_SETTING_NAME: + printf("PA_CONTEXT_SETTING_NAME\n"); + break; + case PA_CONTEXT_READY: + printf("PA_CONTEXT_READY\n"); +// if (!device_name) { + pa_operation_unref(pa_context_get_server_info(c, context_get_server_info_callback, NULL)); +/* } else { + pa_operation_unref(pa_context_get_source_info_by_name(c, device_name, context_get_source_info_callback, NULL)); + } +*/ + break; + + case PA_CONTEXT_FAILED: + printf("PA_CONTEXT_FAILED\n"); + break; + + case PA_CONTEXT_TERMINATED: + printf("PA_CONTEXT_TERMINATED\n"); + break; + } +} + +void context_get_server_info_callback(pa_context *c, const pa_server_info*si, void *userdata) { + if (!si) { + printf("Failed to get server information\n"); + return; + } + + if (!si->default_source_name) { + printf("No default source set\n"); + return; + } + + pa_operation_unref(pa_context_get_source_info_by_name(c, si->default_source_name, context_get_source_info_callback, NULL)); + +} + +void context_get_source_info_callback(pa_context *c, const pa_source_info *si, int is_last, void *userdata) { + + if (is_last < 0) { + printf("Failed to get sink information\n"); + return; + } + + if (!si) { + return; + } + + create_stream(c,si); +} + +pa_stream *create_stream(pa_context *c, const pa_source_info *si) { + static const pa_buffer_attr ba={ + .maxlength=-1, + .tlength=1024, + .prebuf=-1, + .minreq=-1, + // .minreq=256, //For FFT calculus + .fragsize=512 + }; + + pa_sample_spec nss = { + .format = PA_SAMPLE_FLOAT32LE, + .rate = si->sample_spec.rate, + .channels = si->sample_spec.channels + }; + + pa_stream *stream=NULL; + //pa_proplist *pl=NULL; + char t[256]; + +/* + pa_channel_map cmap; + pa_channel_map_init_mono(&cmap); + //pa_channel_map_init_stereo(&cmap); +*/ + + printf("Source : %s (%s)\n", si->name, si->description); + printf("Using sample format: %s\n", pa_sample_spec_snprint(t, sizeof(t), &nss)); + printf("Using channel map: %s\n", pa_channel_map_snprint(t, sizeof(t), &(si->channel_map))); +/* + pl=pa_proplist_new(); + pa_proplist_set(pl, "maxlength", &pl_maxlength, sizeof(pl_maxlength)); + pa_proplist_set(pl, "tlength", &pl_tlength, sizeof(pl_tlength)); + pa_proplist_set(pl, "fragsize", &pl_fragsize, sizeof(pl_fragsize)); +*/ + stream=pa_stream_new(c, APP_TITLE, &nss, &(si->channel_map)); + //stream=pa_stream_new_with_proplist(c, APP_TITLE, &(si->sample_spec), &(si->channel_map), pl); + pa_stream_set_state_callback(stream, stream_state_callback, NULL); + pa_stream_set_read_callback(stream, stream_read_callback, NULL); + //pa_stream_connect_record(stream, APP_TITLE, NULL, (enum pa_stream_flags) 0); + pa_stream_connect_record(stream, si->name, &ba, PA_STREAM_ADJUST_LATENCY); + + return stream; +} + +void stream_state_callback(pa_stream *s, void *userdata) { + switch (pa_stream_get_state(s)) { + case PA_STREAM_UNCONNECTED: + printf("PA_STREAM_UNCONNECTED\n"); + break; + case PA_STREAM_CREATING: + printf("PA_STREAM_CREATING\n"); + break; + case PA_STREAM_READY: + printf("PA_STREAM_READY\n"); + //g_timeout_add(100, latency_func, NULL); + //pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_info_callback, NULL)); + break; + case PA_STREAM_FAILED: + printf("PA_STREAM_FAILED\n"); + break; + case PA_STREAM_TERMINATED: + printf("PA_STREAM_TERMINATED\n"); + break; + } +} + +void stream_read_callback(pa_stream *s, size_t nbytes, void *userdata) { + const void *p; + //printf("stream_read_callback %i\n", nbytes); + + if (pa_stream_peek(s, &p, &nbytes) < 0) { + printf("pa_stream_peek() failed\n");//: %s", pa_strerror(pa_context_errno(context))); + return; + } + +//printf("debug : before call capture_sound_level_cb==%p\n", capture_sound_level_cb); + my_process((float *)p,nbytes/sizeof(float), pa_stream_get_sample_spec(s)->channels); +//printf("debug : after call capture_sound_level_cb==%p\n", capture_sound_level_cb); + + pa_stream_drop(s); +} + diff --git a/src/capture.h b/src/capture.h new file mode 100644 index 0000000..2acbe09 --- /dev/null +++ b/src/capture.h @@ -0,0 +1,9 @@ +#ifndef CAPTURE_H +#define CAPTURE_H +#include <gtk/gtk.h> + +typedef void(* capture_sound_level_cb_t)(gint sound_level, void *userdata); + +void audio_thread(void *args); + +#endif /*CAPTURE_H*/ diff --git a/src/compute.c b/src/compute.c new file mode 100644 index 0000000..272d6f8 --- /dev/null +++ b/src/compute.c @@ -0,0 +1,116 @@ +#include "compute.h" + +#include "fft.h" +#include <math.h> + +#define MIN_SAMPLES 256 +#define MAX_SAMPLES 2048 + +//#define MAX(a,b) (a>b?a:b) +//#define MIN(a,b) (a<b?a:b) + +//static inline float todB_a(const float *x); +void compute_spectrum(float *data, int width, float output[PSHalf]); + + +float compute_level(const float *data, size_t nsamples, int rate) { + + size_t i; + float input[MAX_SAMPLES], pwrspec[PSHalf]; + float value; + int f, min_f_index, max_f_index; + + if (nsamples >= MAX_SAMPLES) { + printf("WARN : nsamples >= MAX_SAMPLES : %i >= %i\n", nsamples, MAX_SAMPLES); + nsamples=MAX_SAMPLES; + } + if (nsamples < MIN_SAMPLES) { + printf("WARN : nsamples < MIN_SAMPLES : %i >= %i\n", nsamples, MIN_SAMPLES); + // Replicate with symmetry the sound to obtain an input buffer of the minimal len + for (i=0;i<MIN_SAMPLES;i++) { + if ( (i/nsamples)%2==1 ) + input[i]=data[i]; // First channel only + else + input[i]=data[nsamples-i-1]; + } + nsamples=MIN_SAMPLES; + } else { + for (i=0;i<nsamples;i++) { + input[i]=data[i]; // First channel only + } + } + + compute_spectrum(input, nsamples, pwrspec); + + // Compute the mean power for 200Hz to 2000Hz band + min_f_index=((float)PSHalf)*200.f/(((float)rate)/2.f); + max_f_index=((float)PSHalf)*2000.f/(((float)rate)/2.f); + + value=0.f; + for (f=min_f_index;f<=max_f_index;f++) { + value+=pwrspec[f]; + } + // Mean value + value=value/(max_f_index-min_f_index+1); + + return value; +} +/* +static inline float todB_a(const float *x){ + return (float)((*(int32_t *)x)&0x7fffffff) * 7.17711438e-7f -764.6161886f; +} +*/ +// Adapted from Audacity +void compute_spectrum(float *data, int width, float output[PSHalf]) { + + int i, start, windows; + float temp; + float in[PSNumS]; + float out[PSHalf]; + float processed[PSHalf]={0.0f}; + + start = 0; + windows = 0; + while (start + PSNumS <= width) { + // Windowing : Hanning + for (i=0; i<PSNumS; i++) + in[i] = data[start+i] *(0.50-0.50*cos(2*M_PI*i/(PSNumS-1))); + + // Returns only the real part of the result + PowerSpectrum(in, out); + + for (i=0; i<PSHalf; i++) + processed[i] += out[i]; + + start += PSHalf; + windows++; + } + // Convert to decibels + // But do it safely; -Inf is nobody's friend + for (i = 0; i < PSHalf; i++){ + temp=(processed[i] / PSNumS / windows); + if (temp > 0.0) + output[i] = 10*log10(temp); + else + output[i] = 0; + } +} + +void audio2hsv_1(float audio_level, float *light_h, float *light_s, float *light_v) { + static float hue=0; + + float level_norm=(50.f+audio_level)/30.f; + + if (level_norm<0.f) level_norm=0.f; + if (level_norm>1.f) level_norm=1.f; + hue=(hue+0.0002f); + if (hue>1.f) hue-=1.f; + + printf("%+3.1f %+1.3f\n", audio_level, level_norm); + + // Dummy code + *light_h=hue; + *light_s=1.f; + *light_v=level_norm; +} + diff --git a/src/compute.h b/src/compute.h new file mode 100644 index 0000000..23d5798 --- /dev/null +++ b/src/compute.h @@ -0,0 +1,10 @@ +#ifndef COMPUTE_H +#define COMPUTE_H +#include <stdlib.h> // for size_t +#include <stdio.h> // for printf + +float compute_level(const float *data, size_t nsamples, int rate); +void audio2hsv_1(float audio_level, float *light_h, float *light_s, float *light_v); + +#endif + diff --git a/src/fft.c b/src/fft.c new file mode 100644 index 0000000..dd91d14 --- /dev/null +++ b/src/fft.c @@ -0,0 +1,241 @@ +#include "fft.h" +#include <math.h> +#include <stdlib.h> + +/* Everything here comes from Audacity 1.3.13 + (orignally in C++ and with more genericity and functionnality) + Original Author : Dominic Mazzoni + Licenced under GPL 2.0 (see LICENCE) +*/ + +#define MaxFastBits 16 + +int *gFFTBitTable[MaxFastBits]={NULL}; + +void InitFFT(); +int NumberOfBitsNeeded(int PowerOfTwo); +inline int FastReverseBits(int i, int NumBits); +int ReverseBits(int index, int NumBits); + + +void FFT(int NumSamples, + int InverseTransform, + float *RealIn, float *ImagIn, float *RealOut, float *ImagOut) +{ + int NumBits; /* Number of bits needed to store indices */ + int i, j, k, n; + int BlockSize, BlockEnd; + + double angle_numerator = 2.0 * M_PI; + double tr, ti; /* temp real, temp imaginary */ +/* + if (!IsPowerOfTwo(NumSamples)) { + fprintf(stderr, "%d is not a power of two\n", NumSamples); + exit(1); + } +*/ + if (!gFFTBitTable[0]) + InitFFT(); + + if (!InverseTransform) + angle_numerator = -angle_numerator; + + NumBits = NumberOfBitsNeeded(NumSamples); + + /* + ** Do simultaneous data copy and bit-reversal ordering into outputs... + */ + for (i = 0; i < NumSamples; i++) { + j = FastReverseBits(i, NumBits); + RealOut[j] = RealIn[i]; + ImagOut[j] = (ImagIn == NULL) ? 0.0 : ImagIn[i]; + } + + /* + ** Do the FFT itself... + */ + + BlockEnd = 1; + for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1) { + + double delta_angle = angle_numerator / (double) BlockSize; + + double sm2 = sin(-2 * delta_angle); + double sm1 = sin(-delta_angle); + double cm2 = cos(-2 * delta_angle); + double cm1 = cos(-delta_angle); + double w = 2 * cm1; + double ar0, ar1, ar2, ai0, ai1, ai2; + + for (i = 0; i < NumSamples; i += BlockSize) { + ar2 = cm2; + ar1 = cm1; + + ai2 = sm2; + ai1 = sm1; + + for (j = i, n = 0; n < BlockEnd; j++, n++) { + ar0 = w * ar1 - ar2; + ar2 = ar1; + ar1 = ar0; + + ai0 = w * ai1 - ai2; + ai2 = ai1; + ai1 = ai0; + + k = j + BlockEnd; + tr = ar0 * RealOut[k] - ai0 * ImagOut[k]; + ti = ar0 * ImagOut[k] + ai0 * RealOut[k]; + + RealOut[k] = RealOut[j] - tr; + ImagOut[k] = ImagOut[j] - ti; + + RealOut[j] += tr; + ImagOut[j] += ti; + } + } + BlockEnd = BlockSize; + } + + /* + ** Need to normalize if inverse transform... + */ + + if (InverseTransform) { + float denom = (float) NumSamples; + + for (i = 0; i < NumSamples; i++) { + RealOut[i] /= denom; + ImagOut[i] /= denom; + } + } +} + +void InitFFT() { + int len=2; + int b, i; + for (b=0; b<MaxFastBits; b++) { + gFFTBitTable[b]=malloc(len*sizeof(int)); + + for (i=0; i<len; i++) + gFFTBitTable[b][i] = ReverseBits(i, b+1); + + len <<= 1; + } +} + +int NumberOfBitsNeeded(int PowerOfTwo) +{ + int i; + +/* + if (PowerOfTwo < 2) { + fprintf(stderr, "Error: FFT called with size %d\n", PowerOfTwo); + exit(1); + } +*/ + for (i = 0;; i++) + if (PowerOfTwo & (1 << i)) + return i; +} + +inline int FastReverseBits(int i, int NumBits) +{ + if (NumBits <= MaxFastBits) + return gFFTBitTable[NumBits - 1][i]; + else + return ReverseBits(i, NumBits); +} + +int ReverseBits(int index, int NumBits) +{ + int i, rev; + + for (i = rev = 0; i < NumBits; i++) { + rev = (rev << 1) | (index & 1); + index >>= 1; + } + + return rev; +} + + +/* + * PowerSpectrum + * + * This function computes the same as RealFFT, above, but + * adds the squares of the real and imaginary part of each + * coefficient, extracting the power and throwing away the + * phase. + * + * For speed, it does not call RealFFT, but duplicates some + * of its code. + */ + +void PowerSpectrum(float In[PSNumS], float Out[PSHalf]) { + int i; + + float theta = M_PI / PSHalf; + + float tmpReal[PSHalf]; + float tmpImag[PSHalf]; + float RealOut[PSHalf]; + float ImagOut[PSHalf]; + + for (i=0; i<PSHalf; i++) { + tmpReal[i] = In[2*i]; + tmpImag[i] = In[2*i+1]; + } + + FFT(PSHalf, 0, tmpReal, tmpImag, RealOut, ImagOut); + + float wtemp = sin(0.5 * theta); + + float wpr = -2.0 * wtemp * wtemp; + float wpi = -1.0 * sin(theta); + float wr = 1.0 + wpr; + float wi = wpi; + + int i3; + + float h1r, h1i, h2r, h2i, rt, it; + for (i=1; i < PSHalf/2; i++) { + + i3 = PSHalf - i; + + h1r = 0.5 * (RealOut[i] + RealOut[i3]); + h1i = 0.5 * (ImagOut[i] - ImagOut[i3]); + h2r = 0.5 * (ImagOut[i] + ImagOut[i3]); + h2i = -0.5 * (RealOut[i] - RealOut[i3]); + + rt = h1r + wr * h2r - wi * h2i; + it = h1i + wr * h2i + wi * h2r; + + Out[i] = rt * rt + it * it; + + rt = h1r - wr * h2r + wi * h2i; + it = -h1i + wr * h2i + wi * h2r; + + Out[i3] = rt * rt + it * it; + + wr = (wtemp = wr) * wpr - wi * wpi + wr; + wi = wi * wpr + wtemp * wpi + wi; + } + + rt = (h1r = RealOut[0]) + ImagOut[0]; + it = h1r - ImagOut[0]; + Out[0] = rt * rt + it * it; + rt = RealOut[PSHalf / 2]; + it = ImagOut[PSHalf / 2]; + Out[PSHalf / 2] = rt * rt + it * it; +} + +void DeinitFFT() { + int i; + if (gFFTBitTable[0]) { + for (i=0;i<MaxFastBits;i++) { + free(gFFTBitTable[i]); + } + } +} + diff --git a/src/fft.h b/src/fft.h new file mode 100644 index 0000000..6066d52 --- /dev/null +++ b/src/fft.h @@ -0,0 +1,19 @@ +#ifndef FFT_H +#define FFT_H + +/* Everything here comes from Audacity 1.3.13 + (orignally in C++ and with more genericity and functionnality) + Original Author : Dominic Mazzoni + Licenced under GPL 2.0 (see LICENCE) +*/ + +#define PSNumS 256 +#define PSHalf 128 +void PowerSpectrum(float In[PSNumS], float Out[PSHalf]); + +void FFT(int NumSamples, int InverseTransform, + float *RealIn, float *ImagIn, float *RealOut, float *ImagOut); +void DeinitFFT(); + +#endif + diff --git a/src/gtkvumeter.c b/src/gtkvumeter.c new file mode 100644 index 0000000..d57cc62 --- /dev/null +++ b/src/gtkvumeter.c @@ -0,0 +1,263 @@ +#include <string.h> +#include "gtkvumeter.h" + +#define MIN_HORIZONTAL_VUMETER_WIDTH 40 +#define HORIZONTAL_VUMETER_HEIGHT 20 +#define VERTICAL_VUMETER_WIDTH 20 +#define MIN_VERTICAL_VUMETER_HEIGHT 40 + +G_DEFINE_TYPE (GtkVuMeter, gtk_vu_meter, GTK_TYPE_DRAWING_AREA); + +static GdkColor default_f_gradient_keys[3] = {{0,65535,0,0},{0,65535,65535,0},{0,0,65535,0}}; +static GdkColor default_b_gradient_keys[3] = {{0,49151,0,0},{0,49151,49151,0},{0,0,49151,0}}; + +static gboolean gtk_vu_meter_expose (GtkWidget *vumeter, GdkEventExpose *event); +static void gtk_vu_meter_setup_colors (GtkVuMeter *vumeter); +static void gtk_vu_meter_free_colors (GtkVuMeter *vumeter); +static void free_drawbuf(guchar *pixels, gpointer data); +static void gtk_vu_meter_size_request (GtkWidget *widget, GtkRequisition *requisition); +static void gtk_vu_meter_size_allocate (GtkWidget *widget, GtkAllocation *allocation); +static gint gtk_vu_meter_sound_level_to_draw_level (GtkVuMeter *vumeter); + +static void gtk_vu_meter_class_init (GtkVuMeterClass *class) { + GtkWidgetClass *widget_class; + widget_class = GTK_WIDGET_CLASS (class); + widget_class->expose_event = gtk_vu_meter_expose; + widget_class->size_request = gtk_vu_meter_size_request; + widget_class->size_allocate = gtk_vu_meter_size_allocate; +} + +static void gtk_vu_meter_init (GtkVuMeter *vumeter) { + vumeter->vertical=TRUE; + gtk_vu_meter_set_gradient(vumeter, 3, default_f_gradient_keys, 3, default_b_gradient_keys); + gtk_vu_meter_setup_colors(vumeter); +} + +void gtk_vu_meter_set_gradient (GtkVuMeter *vumeter, gint f_gradient_key_count, GdkColor *f_gradient_keys, gint b_gradient_key_count, GdkColor *b_gradient_keys) { + //XXX : memdup is a bad idea ? + GdkColor *fgk = g_memdup(f_gradient_keys, f_gradient_key_count*sizeof(GdkColor)); + GdkColor *bgk = g_memdup(b_gradient_keys, b_gradient_key_count*sizeof(GdkColor)); + g_return_if_fail (fgk != NULL); + g_return_if_fail (bgk != NULL); + + vumeter->f_gradient_keys = fgk; + vumeter->f_gradient_key_count=f_gradient_key_count; + vumeter->b_gradient_keys = bgk; + vumeter->b_gradient_key_count=b_gradient_key_count; +} +static void gtk_vu_meter_free_colors (GtkVuMeter *vumeter) { + // TODO : free pixmaps +} + +static void gtk_vu_meter_setup_colors (GtkVuMeter *vumeter) { + + gint i,j; + guchar *f_drawbuf, *b_drawbuf, f_r, f_g, f_b, b_r, b_g, b_b; + gint f_key_len, b_key_len; + gint f_key_i, b_key_i; + gdouble f_key_pos, b_key_pos; + GdkColor *fgk, *bgk; + + gint h=GTK_WIDGET(vumeter)->allocation.height; + gint w=GTK_WIDGET(vumeter)->allocation.width; + + // Clean every previous colors and buffers + gtk_vu_meter_free_colors (vumeter); + + if (vumeter->vertical == TRUE) { + vumeter->colors = MAX(h, 0); + } else { + vumeter->colors = MAX(w, 0); + } + + // Allocate a memory buffers to hold the gradients + f_drawbuf=g_malloc(h*w*3); + b_drawbuf=g_malloc(h*w*3); + g_return_if_fail (f_drawbuf != NULL); + g_return_if_fail (b_drawbuf != NULL); + + // Compute some values before looping for gradient generation + f_key_len = vumeter->colors / (vumeter->f_gradient_key_count-1) + 1; + b_key_len = vumeter->colors / (vumeter->b_gradient_key_count-1) + 1; + fgk=vumeter->f_gradient_keys; + bgk=vumeter->b_gradient_keys; + + for (i=0; i<vumeter->colors; i++) { + // Compute the current position in the gradient keys + f_key_i=i/f_key_len; + f_key_pos=((gdouble) (i%f_key_len)/f_key_len); + b_key_i=i/f_key_len; + b_key_pos=((gdouble) (i%b_key_len)/b_key_len); + + /* Generate the Colours */ + /* foreground */ + f_r = ( fgk[f_key_i].red*(1.0-f_key_pos) + fgk[f_key_i+1].red*f_key_pos ) / 256; + f_g = ( fgk[f_key_i].green*(1.0-f_key_pos) + fgk[f_key_i+1].green*f_key_pos ) / 256; + f_b = ( fgk[f_key_i].blue*(1.0-f_key_pos) + fgk[f_key_i+1].blue*f_key_pos ) / 256; + /* background */ + b_r = ( bgk[b_key_i].red*(1.0-b_key_pos) + bgk[b_key_i+1].red*b_key_pos ) / 256; + b_g = ( bgk[b_key_i].green*(1.0-b_key_pos) + bgk[b_key_i+1].green*b_key_pos ) / 256; + b_b = ( bgk[b_key_i].blue*(1.0-b_key_pos) + bgk[b_key_i+1].blue*b_key_pos ) / 256; + + /* Apply the color in the drawbufs */ + if (vumeter->vertical == TRUE) { + // Vertical mode : draw directly the whole line of identical color + for (j=3*w*i; j<3*w*(i+1); ) { + f_drawbuf[j++]=f_r; + f_drawbuf[j++]=f_g; + f_drawbuf[j++]=f_b; + } + for (j=3*w*i; j<3*w*(i+1); ) { + b_drawbuf[j++]=b_r; + b_drawbuf[j++]=b_g; + b_drawbuf[j++]=b_b; + } + } else { + // Horiziontal mode : draw only the first line (color change at each pixel) + // Others line will be duplicated at the end + f_drawbuf[i]=f_r; + f_drawbuf[i+1]=f_g; + f_drawbuf[i+2]=f_b; + b_drawbuf[i]=b_r; + b_drawbuf[i+1]=b_g; + b_drawbuf[i+2]=b_b; + } + } + if (vumeter->vertical != TRUE) { + // Duplicate the first line over the others + for (j=1; j<h; j++) { + memcpy(f_drawbuf, f_drawbuf+3*w*j, w); + } + } + + vumeter->f_pixbuf = gdk_pixbuf_new_from_data(f_drawbuf, GDK_COLORSPACE_RGB, FALSE, 8, w, h, w*3, free_drawbuf, NULL); + vumeter->b_pixbuf = gdk_pixbuf_new_from_data(b_drawbuf, GDK_COLORSPACE_RGB, FALSE, 8, w, h, w*3, free_drawbuf, NULL); +} + +static void gtk_vu_meter_size_request (GtkWidget *widget, GtkRequisition *requisition) +{ + GtkVuMeter *vumeter; + + g_return_if_fail (GTK_IS_VU_METER (widget)); + g_return_if_fail (requisition != NULL); + + vumeter = GTK_VU_METER (widget); + + if (vumeter->vertical == TRUE) { + requisition->width = VERTICAL_VUMETER_WIDTH; + requisition->height = MIN_VERTICAL_VUMETER_HEIGHT; + } else { + requisition->width = MIN_HORIZONTAL_VUMETER_WIDTH; + requisition->height = HORIZONTAL_VUMETER_HEIGHT; + } +} + +static void gtk_vu_meter_size_allocate (GtkWidget *widget, GtkAllocation *allocation) +{ + GtkVuMeter *vumeter; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_VU_METER (widget)); + g_return_if_fail (allocation != NULL); + + widget->allocation = *allocation; + vumeter = GTK_VU_METER (widget); + + if (GTK_WIDGET_REALIZED (widget)) { + if (vumeter->vertical == TRUE) { /* veritcal */ + gdk_window_move_resize (widget->window, allocation->x, allocation->y, + VERTICAL_VUMETER_WIDTH, MAX(allocation->height, MIN_VERTICAL_VUMETER_HEIGHT)); + } else { /* horizontal */ + gdk_window_move_resize (widget->window, allocation->x, allocation->y, + MAX(allocation->width, MIN_HORIZONTAL_VUMETER_WIDTH), HORIZONTAL_VUMETER_HEIGHT); + } + /* Fix the colours */ + gtk_vu_meter_setup_colors (vumeter); + } +} + +static void free_drawbuf(guchar *pixels, gpointer data) { + g_free(pixels); +} + +static gboolean gtk_vu_meter_expose (GtkWidget *vumeter, GdkEventExpose *event) { + //gdk_pixbuf_render_to_drawable(pixbuf, dbuf_pixmap, app->style->fg_gc[GTK_STATE_NORMAL], 0, 0, 0, 0, WIDTH, HEIGHT, GDK_RGB_DITHER_NORMAL, 0, 0); + cairo_t *cr; + gint draw_level = gtk_vu_meter_sound_level_to_draw_level(GTK_VU_METER(vumeter)); + + cr = gdk_cairo_create (vumeter->window); + + // set a clip region for the expose event + cairo_rectangle (cr,event->area.x, event->area.y, event->area.width, event->area.height); + cairo_clip(cr); + + // Paint from the stored gradient + gdk_cairo_set_source_pixbuf(cr, GTK_VU_METER(vumeter)->b_pixbuf,0,0); + cairo_paint(cr); + + // set a clip region for the expose event + if (GTK_VU_METER(vumeter)->vertical==TRUE) { + cairo_rectangle (cr,event->area.x, event->area.y+draw_level, event->area.width, event->area.height-draw_level); + } else { + //TODO + cairo_rectangle (cr,event->area.x, event->area.y, event->area.width, 12/*event->area.height*/); + } + cairo_clip(cr); + + // Paint from the stored gradient + gdk_cairo_set_source_pixbuf(cr, GTK_VU_METER(vumeter)->f_pixbuf,0,0); + cairo_paint(cr); + + cairo_destroy(cr); + + return FALSE; +} + +static gint gtk_vu_meter_sound_level_to_draw_level (GtkVuMeter *vumeter) { + gint draw_level; + gdouble level, min, max, height; + + level = (gdouble)vumeter->level; + min = (gdouble)vumeter->min; + max = (gdouble)vumeter->max; + height = (gdouble)vumeter->colors; + + draw_level = (1.0 - (level - min)/(max - min)) * height; + + //printf("1.0 - (%lf - %lf)/(%lf - %lf)) * %lf => %i\n", level, min, max, min, height, draw_level); + + return draw_level; +} + +GtkWidget * gtk_vu_meter_new (gboolean vertical) { + GtkWidget *vumeter = g_object_new (GTK_TYPE_VU_METER, NULL); + GTK_VU_METER(vumeter)->vertical=vertical; + GTK_VU_METER(vumeter)->level=0; + GTK_VU_METER(vumeter)->min=-32767; + GTK_VU_METER(vumeter)->max=32767; + + return vumeter; +} + + +void gtk_vu_meter_set_min_max (GtkVuMeter *vumeter, gint min, gint max) +{ + g_return_if_fail (vumeter != NULL); + + vumeter->max = MAX(max, min); + vumeter->min = MIN(min, max); + if (vumeter->max == vumeter->min) { + vumeter->max++; + } + vumeter->level = CLAMP (vumeter->level, vumeter->min, vumeter->max); + gtk_widget_queue_draw (GTK_WIDGET(vumeter)); +} + +void gtk_vu_meter_set_level(GtkVuMeter *vumeter, gint level) +{ + g_return_if_fail (vumeter != NULL); + + vumeter->level = CLAMP (level, vumeter->min, vumeter->max); + gtk_widget_queue_draw (GTK_WIDGET(vumeter)); +} + diff --git a/src/gtkvumeter.h b/src/gtkvumeter.h new file mode 100644 index 0000000..07eb7c5 --- /dev/null +++ b/src/gtkvumeter.h @@ -0,0 +1,39 @@ +#ifndef GTKVUMETER_H +#define GTKVUMETER_H +#include <gtk/gtk.h> + +#define GTK_TYPE_VU_METER (gtk_vu_meter_get_type ()) +#define GTK_VU_METER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_VU_METER, GtkVuMeter)) +#define GTK_VU_METER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GTK_VU_METER, GtkVuMeterClass)) +#define GTK_IS_VU_METER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_VU_METER)) +#define GTK_IS_VU_METER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), GTK_TYPE_VU_METER)) +#define GTK_VU_METER_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_VU_METER, GtkVuMeterClass)) + +typedef struct _GtkVuMeter GtkVuMeter; +typedef struct _GtkVuMeterClass GtkVuMeterClass; + +struct _GtkVuMeter { + GtkDrawingArea parent; + + /* private */ + gboolean vertical; + gint level, min, max; + + gint f_gradient_key_count, b_gradient_key_count; + GdkColor *f_gradient_keys, *b_gradient_keys; + + gint colors; + GdkPixbuf *f_pixbuf, *b_pixbuf; +}; + +struct _GtkVuMeterClass { + GtkDrawingAreaClass parent_class; +}; + +GtkType gtk_vu_meter_get_type (void) G_GNUC_CONST; +GtkWidget * gtk_vu_meter_new (gboolean vertical); +void gtk_vu_meter_set_gradient (GtkVuMeter *vumeter, gint f_gradient_key_count, GdkColor *f_gradient_keys, gint b_gradient_key_count, GdkColor *b_gradient_keys); +void gtk_vu_meter_set_min_max (GtkVuMeter *vumeter, gint min, gint max); +void gtk_vu_meter_set_level(GtkVuMeter *vumeter, gint level); + +#endif /*GTKVUMETER_H*/ diff --git a/src/hsv2rgb.c b/src/hsv2rgb.c new file mode 100644 index 0000000..9f445e9 --- /dev/null +++ b/src/hsv2rgb.c @@ -0,0 +1,59 @@ +/* hsv2rgb.c + * Convert Hue Saturation Value to Red Green Blue + * + * P.J. 08-Aug-98 + * + * Reference: + * D. F. Rogers + * Procedural Elements for Computer Graphics + * McGraw Hill 1985 + */ + +#include "hsv2rgb.h" + +int hsv2rgb( struct hsv_colour *hsv, struct rgb_colour *rgb ) { + /* + * Purpose: + * Convert HSV values to RGB values + * All values are in the range [0.0 .. 1.0] + */ + float S, H, V, F, M, N, K; + int I; + + S = hsv->s; /* Saturation */ + H = hsv->h; /* Hue */ + V = hsv->v; /* value or brightness */ + + if ( S == 0.0 ) { + /* + * Achromatic case, set level of grey + */ + rgb->r = V; + rgb->g = V; + rgb->b = V; + } else { + /* + * Determine levels of primary colours. + */ + if (H >= 1.0) { + H = 0.0; + } else { + H = H * 6; + } /* end if */ + I = (int) H; /* should be in the range 0..5 */ + F = H - I; /* fractional part */ + + M = V * (1 - S); + N = V * (1 - S * F); + K = V * (1 - S * (1 - F)); + + if (I == 0) { rgb->r = V; rgb->g = K; rgb->b = M; } + if (I == 1) { rgb->r = N; rgb->g = V; rgb->b = M; } + if (I == 2) { rgb->r = M; rgb->g = V; rgb->b = K; } + if (I == 3) { rgb->r = M; rgb->g = N; rgb->b = V; } + if (I == 4) { rgb->r = K; rgb->g = M; rgb->b = V; } + if (I == 5) { rgb->r = V; rgb->g = M; rgb->b = N; } + } /* end if */ + + return 0; +} /* end function hsv2rgb */ diff --git a/src/hsv2rgb.h b/src/hsv2rgb.h new file mode 100644 index 0000000..bd880ff --- /dev/null +++ b/src/hsv2rgb.h @@ -0,0 +1,18 @@ +#ifndef HSV2RGB_H +#define HSV2RGB_H + +struct hsv_colour { + float h; + float s; + float v; +}; /* end struct */ + +struct rgb_colour { + float r; + float g; + float b; +}; /* end struct */ + +int hsv2rgb( struct hsv_colour *hsv, struct rgb_colour *rgb ); + +#endif /* HSV2RGB_H */ diff --git a/src/music2light.c b/src/music2light.c new file mode 100644 index 0000000..fd58d03 --- /dev/null +++ b/src/music2light.c @@ -0,0 +1,80 @@ +#include <gtk/gtk.h> +#include <stdlib.h> +#include <pthread.h> +#include "gtkvumeter.h" +#include "win_main.h" +#include "compute.h" +#include "capture.h" +#include "hsv2rgb.h" + +gint *audio_vumeter_val, *light_h, *light_s, *light_v, *light_r, *light_g, *light_b; +void my_process(float *data, size_t nsamples, size_t nchan); + +int main (int argc, char **argv) { + GtkWidget *mainwin; + gint vals_for_vumeters[7]={0,0,0,0,0,0,0}; //sound,h,s,v,r,g,b + //Some handy references to the previous array items to make things clear whenever possible + audio_vumeter_val=vals_for_vumeters+0; + light_h=vals_for_vumeters+1; + light_s=vals_for_vumeters+2; + light_v=vals_for_vumeters+3; + light_r=vals_for_vumeters+4; + light_g=vals_for_vumeters+5; + light_b=vals_for_vumeters+6; + + pthread_t audio_analyzer; + + g_thread_init(NULL); + gdk_threads_init(); + gdk_threads_enter(); + gtk_init (&argc, &argv); + + mainwin=win_main_build(); + gtk_widget_show_all (mainwin); + + printf("debug : main my_process==%p\n", my_process); + printf("debug : main (void *)my_process==%p\n", (void *)my_process); + pthread_create (&audio_analyzer, (void *)NULL, (void *)audio_thread, (void *)my_process); + g_timeout_add (25, win_main_update_vumeters, (gpointer)vals_for_vumeters); + + gtk_main (); + gdk_threads_leave(); + + return 0; +} + +void my_process(float *data, size_t nsamples, size_t nchan) { + + float sound_level; + struct hsv_colour hsv; + struct rgb_colour rgb; + + //FIXME : rate should came from PulseAudio + sound_level=compute_level(data, nsamples, 44100); + +// printf("sound_level==%f\n", sound_level); + + // Update sound vumeter value (refreshed asynchronously) + *audio_vumeter_val=(int)sound_level; + + // Transfert Function + audio2hsv_1(sound_level,&(hsv.h),&(hsv.s),&(hsv.v)); + + // Conversion + hsv2rgb(&hsv, &rgb); +// printf("hsv %+.3f %+.3f %+.3f\n", hsv.h, hsv.s, hsv.v); +// printf("rgb %+.3f %+.3f %+.3f\n", rgb.r, rgb.g, rgb.b); + + //Update vu-meters + *light_h=hsv.h*255; + *light_s=hsv.s*255; + *light_v=hsv.v*255; + + *light_r=rgb.r*255; + *light_g=rgb.g*255; + *light_b=rgb.b*255; + + // Send to DMX + //TODO +} + diff --git a/src/win_main.c b/src/win_main.c new file mode 100644 index 0000000..daa0a34 --- /dev/null +++ b/src/win_main.c @@ -0,0 +1,73 @@ +#include "win_main.h" +#include "gtkvumeter.h" + +GtkWidget *vumeter_sound, *vumeter_r, *vumeter_g, *vumeter_b, *vumeter_h, *vumeter_s, *vumeter_v; + +GtkWidget *win_main_build() { + GtkWidget *win, *hbox1; + + GdkColor f_gradient_red[2] = {{0,65535,0,0},{0,0,0,0}}; + GdkColor b_gradient_red[2] = {{0,49151,0,0},{0,0,0,0}}; + GdkColor f_gradient_green[2] = {{0,0,65535,0},{0,0,0,0}}; + GdkColor b_gradient_green[2] = {{0,0,49151,0},{0,0,0,0}}; + GdkColor f_gradient_blue[2] = {{0,0,0,65535},{0,0,0,0}}; + GdkColor b_gradient_blue[2] = {{0,0,0,49151},{0,0,0,0}}; + GdkColor f_gradient_hue[7] = {{0,65535,0,0},{0,65535,0,65535},{0,0,0,65535},{0,0,65535,65535},{0,0,65535,0},{0,65535,65535,0},{0,65535,0,0}}; + GdkColor b_gradient_hue[7] = {{0,49151,0,0},{0,49151,0,49151},{0,0,0,49151},{0,0,49151,49151},{0,0,49151,0},{0,49151,49151,0},{0,49151,0,0}}; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + hbox1 = gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(win), hbox1); + gtk_container_set_border_width(GTK_CONTAINER(hbox1), 5); + +//TODO : gtk_vumeter_set_min_max (GTK_VU_METER(vumeter), min, max); + vumeter_sound = gtk_vu_meter_new (TRUE); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_sound), -50, 0); + gtk_box_pack_start(GTK_BOX(hbox1), vumeter_sound, FALSE, FALSE, 0); + + vumeter_h = gtk_vu_meter_new (TRUE); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_h), 0, 255); + gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_h), 7, f_gradient_hue, 7, b_gradient_hue); + gtk_box_pack_start(GTK_BOX(hbox1), vumeter_h, FALSE, FALSE, 0); + + vumeter_s = gtk_vu_meter_new (TRUE); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_s), 0, 255); + gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_s), 2, f_gradient_red, 2, b_gradient_red); + gtk_box_pack_start(GTK_BOX(hbox1), vumeter_s, FALSE, FALSE, 0); + + vumeter_v = gtk_vu_meter_new (TRUE); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_v), 0, 255); + gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_v), 2, f_gradient_red, 2, b_gradient_red); + gtk_box_pack_start(GTK_BOX(hbox1), vumeter_v, FALSE, FALSE, 0); + + + vumeter_r = gtk_vu_meter_new (TRUE); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_r), 0, 255); + gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_r), 2, f_gradient_red, 2, b_gradient_red); + gtk_box_pack_start(GTK_BOX(hbox1), vumeter_r, FALSE, FALSE, 0); + + vumeter_g = gtk_vu_meter_new (TRUE); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_g), 0, 255); + gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_g), 2, f_gradient_green, 2, b_gradient_green); + gtk_box_pack_start(GTK_BOX(hbox1), vumeter_g, FALSE, FALSE, 0); + + vumeter_b = gtk_vu_meter_new (TRUE); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_b), 0, 255); + gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_b), 2, f_gradient_blue, 2, b_gradient_blue); + gtk_box_pack_start(GTK_BOX(hbox1), vumeter_b, FALSE, FALSE, 0); + + return win; +} + +gboolean win_main_update_vumeters(gpointer vals) { + gtk_vu_meter_set_level(GTK_VU_METER(vumeter_sound), ((gint*)vals)[0]); + gtk_vu_meter_set_level(GTK_VU_METER(vumeter_h), ((gint*)vals)[1]); + gtk_vu_meter_set_level(GTK_VU_METER(vumeter_s), ((gint*)vals)[2]); + gtk_vu_meter_set_level(GTK_VU_METER(vumeter_v), ((gint*)vals)[3]); + gtk_vu_meter_set_level(GTK_VU_METER(vumeter_r), ((gint*)vals)[4]); + gtk_vu_meter_set_level(GTK_VU_METER(vumeter_g), ((gint*)vals)[5]); + gtk_vu_meter_set_level(GTK_VU_METER(vumeter_b), ((gint*)vals)[6]); + return TRUE; +} + diff --git a/src/win_main.h b/src/win_main.h new file mode 100644 index 0000000..5068c89 --- /dev/null +++ b/src/win_main.h @@ -0,0 +1,9 @@ +#ifndef WIN_MAIN_H +#define WIN_MAIN_H +#include <gtk/gtk.h> + +GtkWidget *win_main_build(); +gboolean win_main_update_vumeters(gpointer vals); + +#endif + |