aboutsummaryrefslogtreecommitdiff
path: root/examples/toCheck/7-sqlite-pitchmatcher.csd
blob: 1d4523481e5dc67efe59e2f618a4f99696efdea2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
/*
    EXAMPLE 9

    A rudimentary pitch tracking concatenative resynthesizer

    This creates a sqlite database in memory, and then uses the opcode getpitches to scan through a 
    sound file in non-realtime, storing detected pitches and the relevant offset time of the pitch in 
    the database. The example file is a series of ascending piano notes.
    
    When done, the instrument "playmatches" is scheduled. This plays a descending oscillator
    which is pitch tracked and then a similar pitch is found from the database, prompting the "segment" 
    instrument to be scheduled with the relevant offset.
    The result is that the piano notes/sound file segments picked should moreorless match what the 
    oscillator is doing.

*/

sr = 44100
kr = 4410
nchnls = 2
0dbfs = 1


; create an in-memory sqlite database and create a table
gidb  dbconnect "sqlite", ":memory:"
dbexec gidb, "CREATE TABLE pitches (time REAL, cps REAL)"

; file of ascending piano pitches
gSfile = "sounds/piano.wav"


; detect pitches and insert time/cps to database given a filename, in non-realtime
opcode getpitches, 0, S
    Sfile xin
    ktimek timeinstk
    ktime timeinsts
    klast init -1
    kcount init 0

    ; run in the first single k-rate cycle
    if (ktimek == 0) then

        ; get file length in k-cycles
        ilen filelen Sfile
        kcycles = ilen * kr
loop:
        ; read file and track pitch
        ain diskin2 Sfile, 1
        koct, kamp pitch ain, 0.01, 7, 11, 6, 12, 10, 8

        ; only take action if pitch has changed
        kchanged changed2 koct
        if (kchanged == 1) then

            ; only store if cps is reasonably different from the last value
            kcps = cpsoct(koct)
            if (kcps > klast*1.1 || kcps < klast*0.9) then

                ; insert to database: the dbexec_kb opcode is k-rate but blocking/synchronous. 
                ; simpler to use for the non-realtime operation as regular _k opcodes are threaded/asynchronous
                ktime = kcount / kr
                Squery sprintfk "INSERT INTO pitches (time, cps) VALUES (%f, %f)", ktime, kcps
                dbexec_kb gidb, Squery
                klast = kcps
            endif
        endif
        loop_lt kcount, 1, kcycles, loop
        
    endif
endop



; begin the example: find pitches and then schedule the next step
instr start_example
    getpitches gSfile
    schedkwhen 1, 0, 0, "playmatches", 0, 20
    turnoff

endin



; pitch follow a descending oscillator and attempt to find a similar cps in the database, then schedule segment accordingly
instr playmatches
    kdone init 0    ; for when the select query is done
    klast init 0    ; last pitch played, in order to avoid repeats

    ; descending oscillator
    k1 linseg 2000, p3, 200
    ain oscil 1, k1, 1

    ; track it
    koct, kamp pitch ain, 0.01, 7, 11, 6, 12, 10, 8

    ; only take action when the tracked pitch has changed
    kchanged changed2 koct
    if (kchanged == 1) then

        ; (very roughly) get a near frequency match from the database
        kcps = cpsoct(koct)
        Squery sprintfk "SELECT time FROM pitches WHERE cps >= %f LIMIT 1", kcps
        kdone, kpos dbscalar_k gidb, Squery, kchanged

        ; don't repeat notes (try the schedkwhen outside of the if block for continuous play)
        if (kpos != klast) then
            schedkwhen kdone, 0, 0, "segment", 0, 0.2, kpos
            klast = kpos
        endif
    endif
    
    ; uncomment to hear the oscillator as well as pitch matched output
    ;outs ain*0.01, ain*0.01
endin



; play part of the sound file given a skip time with a basic fade in/out
instr segment
    iskip = p4
    kamp linseg 0, p3*0.1, 1, p3*0.8, 1, p3*0.1, 0
    a1 diskin2 gSfile, 1, iskip
    aout = a1*kamp * 0.1
    outs aout, aout
endin


</CsInstruments>
<CsScore>
f1 0 16384 10 1 0 0.3 0 0.2 0 0.14 0 .111 ; square wave

i"start_example" 0 1

</CsScore>
</CsoundSynthesizer>